/* * Stellarium * Copyright (C) 2008 Guillaume Chereau * * This program 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "Dialog.hpp" #include "SearchDialog.hpp" #include "ui_searchDialogGui.h" #include "StelApp.hpp" #include "StelCore.hpp" #include "StelObjectMgr.hpp" #include "StelModuleMgr.hpp" #include "StelMovementMgr.hpp" #include "StelNavigator.hpp" #include "StelUtils.hpp" #include #include #include #include #include #include #include "SimbadSearcher.hpp" // Start of members for class CompletionLabel CompletionLabel::CompletionLabel(QWidget* parent) : QLabel(parent), selectedIdx(0) { } CompletionLabel::~CompletionLabel() { } void CompletionLabel::setValues(const QStringList& v) { values=v; updateText(); } void CompletionLabel::appendValues(const QStringList& v) { values+=v; updateText(); } void CompletionLabel::clearValues() { values.clear(); selectedIdx=0; updateText(); } QString CompletionLabel::getSelected() { if (values.isEmpty()) return QString(); return values.at(selectedIdx); } void CompletionLabel::selectNext() { ++selectedIdx; if (selectedIdx>=values.size()) selectedIdx=0; updateText(); } void CompletionLabel::selectPrevious() { --selectedIdx; if (selectedIdx<0) selectedIdx = values.size()-1; updateText(); } void CompletionLabel::selectFirst() { selectedIdx=0; updateText(); } void CompletionLabel::updateText() { QString newText; // Regenerate the list with the selected item in bold for (int i=0;i"; else newText+=values[i]; if (i!=values.size()-1) newText += ", "; } setText(newText); } // Start of members for class SearchDialog SearchDialog::SearchDialog() : simbadReply(NULL) { ui = new Ui_searchDialogForm; simbadSearcher = new SimbadSearcher(this); objectMgr = GETSTELMODULE(StelObjectMgr); Q_ASSERT(objectMgr); greekLetters["alpha"] = QString(QChar(0x03B1)); greekLetters["beta"] = QString(QChar(0x03B2)); greekLetters["gamma"] = QString(QChar(0x03B3)); greekLetters["delta"] = QString(QChar(0x03B4)); greekLetters["epsilon"] = QString(QChar(0x03B5)); greekLetters["zeta"] = QString(QChar(0x03B6)); greekLetters["eta"] = QString(QChar(0x03B7)); greekLetters["theta"] = QString(QChar(0x03B8)); greekLetters["iota"] = QString(QChar(0x03B9)); greekLetters["kappa"] = QString(QChar(0x03BA)); greekLetters["lambda"] = QString(QChar(0x03BB)); greekLetters["mu"] = QString(QChar(0x03BC)); greekLetters["nu"] = QString(QChar(0x03BD)); greekLetters["xi"] = QString(QChar(0x03BE)); greekLetters["omicron"] = QString(QChar(0x03BF)); greekLetters["pi"] = QString(QChar(0x03C0)); greekLetters["rho"] = QString(QChar(0x03C1)); greekLetters["sigma"] = QString(QChar(0x03C3)); // second lower-case sigma shouldn't affect anything greekLetters["tau"] = QString(QChar(0x03C4)); greekLetters["upsilon"] = QString(QChar(0x03C5)); greekLetters["phi"] = QString(QChar(0x03C6)); greekLetters["chi"] = QString(QChar(0x03C7)); greekLetters["psi"] = QString(QChar(0x03C8)); greekLetters["omega"] = QString(QChar(0x03C9)); } SearchDialog::~SearchDialog() { delete ui; if (simbadReply) { simbadReply->deleteLater(); simbadReply = NULL; } } void SearchDialog::languageChanged() { if (dialog) { QString text(ui->lineEditSearchSkyObject->text()); ui->retranslateUi(dialog); ui->lineEditSearchSkyObject->setText(text); } } void SearchDialog::styleChanged() { // Nothing for now } // Initialize the dialog widgets and connect the signals/slots void SearchDialog::createDialogContent() { ui->setupUi(dialog); setSimpleStyle(true); connect(ui->closeStelWindow, SIGNAL(clicked()), this, SLOT(close())); connect(ui->lineEditSearchSkyObject, SIGNAL(textChanged(const QString&)), this, SLOT(onTextChanged(const QString&))); connect(ui->pushButtonGotoSearchSkyObject, SIGNAL(clicked()), this, SLOT(gotoObject())); onTextChanged(ui->lineEditSearchSkyObject->text()); connect(ui->lineEditSearchSkyObject, SIGNAL(returnPressed()), this, SLOT(gotoObject())); dialog->installEventFilter(this); ui->RAAngleSpinBox->setDisplayFormat(AngleSpinBox::HMSLetters); ui->DEAngleSpinBox->setDisplayFormat(AngleSpinBox::DMSSymbols); ui->DEAngleSpinBox->setPrefixType(AngleSpinBox::NormalPlus); // This simply doesn't work. Probably a Qt bug ui->RAAngleSpinBox->setFocusPolicy(Qt::NoFocus); ui->DEAngleSpinBox->setFocusPolicy(Qt::NoFocus); connect(ui->RAAngleSpinBox, SIGNAL(valueChanged()), this, SLOT(manualPositionChanged())); connect(ui->DEAngleSpinBox, SIGNAL(valueChanged()), this, SLOT(manualPositionChanged())); } void SearchDialog::setVisible(bool v) { StelDialog::setVisible(v); // Set the focus directly on the line edit if (ui->lineEditSearchSkyObject->isVisible()) ui->lineEditSearchSkyObject->setFocus(); } void SearchDialog::setSimpleStyle(bool b) { ui->RAAngleSpinBox->setVisible(!b); ui->DEAngleSpinBox->setVisible(!b); ui->simbadStatusLabel->setVisible(false); ui->raDecLabel->setVisible(!b); } void SearchDialog::manualPositionChanged() { ui->completionLabel->clearValues(); StelMovementMgr* mvmgr = GETSTELMODULE(StelMovementMgr); Vec3d pos; StelUtils::spheToRect(ui->RAAngleSpinBox->valueRadians(), ui->DEAngleSpinBox->valueRadians(), pos); mvmgr->setFlagTracking(false); mvmgr->moveToJ2000(pos, 0.05); } void SearchDialog::onTextChanged(const QString& text) { if (simbadReply) { disconnect(simbadReply, SIGNAL(statusChanged()), this, SLOT(onSimbadStatusChanged())); delete simbadReply; simbadReply=NULL; } simbadResults.clear(); QString trimmedText = text.trimmed().toLower(); if (trimmedText.isEmpty()) { ui->completionLabel->clearValues(); ui->completionLabel->selectFirst(); ui->simbadStatusLabel->setText(""); ui->pushButtonGotoSearchSkyObject->setEnabled(false); } else { simbadReply = simbadSearcher->lookup(trimmedText, 3); onSimbadStatusChanged(); connect(simbadReply, SIGNAL(statusChanged()), this, SLOT(onSimbadStatusChanged())); QString greekText = substituteGreek(trimmedText); QStringList matches; if(greekText != trimmedText) { matches = objectMgr->listMatchingObjectsI18n(trimmedText, 3); matches += objectMgr->listMatchingObjectsI18n(greekText, (5 - matches.size())); } else matches = objectMgr->listMatchingObjectsI18n(trimmedText, 5); ui->completionLabel->setValues(matches); ui->completionLabel->selectFirst(); // Update push button enabled state ui->pushButtonGotoSearchSkyObject->setEnabled(true); } } // Called when the current simbad query status changes void SearchDialog::onSimbadStatusChanged() { Q_ASSERT(simbadReply); if (simbadReply->getCurrentStatus()==SimbadLookupReply::SimbadLookupErrorOccured) { ui->simbadStatusLabel->setText(QString("Simbad Lookup Error: ")+simbadReply->getErrorString()); if (ui->completionLabel->isEmpty()) ui->pushButtonGotoSearchSkyObject->setEnabled(false); } else { ui->simbadStatusLabel->setText(QString("Simbad Lookup: ")+simbadReply->getCurrentStatusString()); // Query not over, don't disable button ui->pushButtonGotoSearchSkyObject->setEnabled(true); } if (simbadReply->getCurrentStatus()==SimbadLookupReply::SimbadLookupFinished) { simbadResults = simbadReply->getResults(); ui->completionLabel->appendValues(simbadResults.keys()); // Update push button enabled state ui->pushButtonGotoSearchSkyObject->setEnabled(!ui->completionLabel->isEmpty()); } if (simbadReply->getCurrentStatus()!=SimbadLookupReply::SimbadLookupQuerying) { disconnect(simbadReply, SIGNAL(statusChanged()), this, SLOT(onSimbadStatusChanged())); delete simbadReply; simbadReply=NULL; // Update push button enabled state // Update push button enabled state ui->pushButtonGotoSearchSkyObject->setEnabled(!ui->completionLabel->isEmpty()); } } void SearchDialog::gotoObject() { QString name = ui->completionLabel->getSelected(); if (name.isEmpty()) return; StelMovementMgr* mvmgr = GETSTELMODULE(StelMovementMgr); if (simbadResults.contains(name)) { close(); Vec3d pos = simbadResults[name]; objectMgr->unSelect(); mvmgr->moveToJ2000(pos, mvmgr->getAutoMoveDuration()); ui->lineEditSearchSkyObject->clear(); ui->completionLabel->clearValues(); } else if (objectMgr->findAndSelectI18n(name)) { const QList newSelected = objectMgr->getSelectedObject(); if (!newSelected.empty()) { close(); ui->lineEditSearchSkyObject->clear(); ui->completionLabel->clearValues(); // Can't point to home planet if (newSelected[0]->getEnglishName()!=StelApp::getInstance().getCore()->getNavigator()->getCurrentLocation().name) { mvmgr->moveToObject(newSelected[0], mvmgr->getAutoMoveDuration()); mvmgr->setFlagTracking(true); } else { GETSTELMODULE(StelObjectMgr)->unSelect(); } } } simbadResults.clear(); } bool SearchDialog::eventFilter(QObject*, QEvent *event) { if (event->type() == QEvent::KeyRelease) { QKeyEvent *keyEvent = static_cast(event); // Kludgy workaround for Qt focusPolicy bug. Get rid of this if // they ever fix it. if(keyEvent->key() == Qt::Key_Tab) ui->lineEditSearchSkyObject->setFocus(Qt::OtherFocusReason); if (keyEvent->key() == Qt::Key_Tab || keyEvent->key() == Qt::Key_Down) { ui->completionLabel->selectNext(); event->accept(); return true; } if (keyEvent->key() == Qt::Key_Up) { ui->completionLabel->selectPrevious(); event->accept(); return true; } } return false; } QString SearchDialog::substituteGreek(const QString& keyString) { if (!keyString.contains(' ')) return getGreekLetterByName(keyString); else { QStringList nameComponents = keyString.split(" ", QString::SkipEmptyParts); if(!nameComponents.empty()) nameComponents[0] = getGreekLetterByName(nameComponents[0]); return nameComponents.join(" "); } } QString SearchDialog::getGreekLetterByName(const QString& potentialGreekLetterName) { if(greekLetters.contains(potentialGreekLetterName)) return greekLetters[potentialGreekLetterName.toLower()]; // There can be indices (e.g. "α1 Cen" instead of "α Cen A"), so strip // any trailing digit. int lastCharacterIndex = potentialGreekLetterName.length()-1; if(potentialGreekLetterName.at(lastCharacterIndex).isDigit()) { QChar digit = potentialGreekLetterName.at(lastCharacterIndex); QString name = potentialGreekLetterName.left(lastCharacterIndex); if(greekLetters.contains(name)) return greekLetters[name.toLower()] + digit; } return potentialGreekLetterName; }