Chris@416: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@416: Chris@416: /* Chris@416: Sonic Visualiser Chris@416: An audio file viewer and annotation editor. Chris@416: Centre for Digital Music, Queen Mary, University of London. Chris@416: This file copyright 2008 QMUL. Chris@416: Chris@416: This program is free software; you can redistribute it and/or Chris@416: modify it under the terms of the GNU General Public License as Chris@416: published by the Free Software Foundation; either version 2 of the Chris@416: License, or (at your option) any later version. See the file Chris@416: COPYING included with this distribution for more information. Chris@416: */ Chris@416: Chris@416: #include "TransformFinder.h" Chris@416: Chris@424: #include "base/XmlExportable.h" Chris@416: #include "transform/TransformFactory.h" Chris@424: #include "SelectableLabel.h" Chris@416: Chris@421: #include Chris@416: #include Chris@416: #include Chris@416: #include Chris@416: #include Chris@416: #include Chris@421: #include Chris@423: #include Chris@424: #include Chris@426: #include Chris@421: Chris@416: TransformFinder::TransformFinder(QWidget *parent) : Chris@417: QDialog(parent), Chris@1408: m_resultsFrame(nullptr), Chris@1408: m_resultsLayout(nullptr) Chris@416: { Chris@416: setWindowTitle(tr("Find a Transform")); Chris@416: Chris@416: QGridLayout *mainGrid = new QGridLayout; Chris@421: mainGrid->setVerticalSpacing(0); Chris@416: setLayout(mainGrid); Chris@416: Chris@416: mainGrid->addWidget(new QLabel(tr("Find:")), 0, 0); Chris@416: Chris@416: QLineEdit *searchField = new QLineEdit; Chris@416: mainGrid->addWidget(searchField, 0, 1); Chris@416: connect(searchField, SIGNAL(textChanged(const QString &)), Chris@416: this, SLOT(searchTextChanged(const QString &))); Chris@416: Chris@450: // m_infoLabel = new QLabel(tr("Type in this box to search descriptions of available and known transforms")); Chris@450: m_infoLabel = new QLabel; Chris@448: mainGrid->addWidget(m_infoLabel, 1, 1); Chris@448: Chris@416: m_resultsScroll = new QScrollArea; Chris@448: mainGrid->addWidget(m_resultsScroll, 2, 0, 1, 2); Chris@448: mainGrid->setRowStretch(2, 10); Chris@416: Chris@416: QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Ok | Chris@416: QDialogButtonBox::Cancel); Chris@448: mainGrid->addWidget(bb, 3, 0, 1, 2); Chris@416: connect(bb, SIGNAL(accepted()), this, SLOT(accept())); Chris@416: connect(bb, SIGNAL(rejected()), this, SLOT(reject())); Chris@448: Chris@448: m_resultsFrame = new QWidget; Chris@448: QPalette palette = m_resultsFrame->palette(); Chris@448: palette.setColor(QPalette::Window, palette.color(QPalette::Base)); Chris@448: m_resultsFrame->setPalette(palette); Chris@448: m_resultsScroll->setPalette(palette); Chris@448: m_resultsLayout = new QVBoxLayout; Chris@448: m_resultsLayout->setSpacing(0); Chris@448: m_resultsLayout->setContentsMargins(0, 0, 0, 0); Chris@448: m_resultsFrame->setLayout(m_resultsLayout); Chris@448: m_resultsScroll->setWidget(m_resultsFrame); Chris@448: m_resultsFrame->show(); Chris@448: Chris@448: m_noResultsLabel = new QLabel(tr("
  No results found")); Chris@448: m_resultsLayout->addWidget(m_noResultsLabel); Chris@448: m_noResultsLabel->hide(); Chris@448: Chris@448: m_beforeSearchLabel = new QLabel; Chris@448: m_resultsLayout->addWidget(m_beforeSearchLabel); Chris@448: m_beforeSearchLabel->hide(); Chris@416: Chris@426: QAction *up = new QAction(tr("Up"), this); Chris@426: up->setShortcut(tr("Up")); Chris@426: connect(up, SIGNAL(triggered()), this, SLOT(up())); Chris@426: addAction(up); Chris@426: Chris@426: QAction *down = new QAction(tr("Down"), this); Chris@426: down->setShortcut(tr("Down")); Chris@426: connect(down, SIGNAL(triggered()), this, SLOT(down())); Chris@426: addAction(down); Chris@426: Chris@423: QDesktopWidget *desktop = QApplication::desktop(); Chris@423: QRect available = desktop->availableGeometry(); Chris@423: Chris@423: int width = available.width() / 2; Chris@423: int height = available.height() / 2; Chris@423: if (height < 450) { Chris@423: if (available.height() > 500) height = 450; Chris@423: } Chris@423: if (width < 600) { Chris@423: if (available.width() > 650) width = 600; Chris@423: } Chris@423: Chris@423: resize(width, height); Chris@423: raise(); Chris@424: Chris@450: setupBeforeSearchLabel(); Chris@450: Chris@424: m_upToDateCount = 0; Chris@424: m_timer = new QTimer(this); Chris@424: connect(m_timer, SIGNAL(timeout()), this, SLOT(timeout())); Chris@425: m_timer->start(30); Chris@416: } Chris@416: Chris@416: TransformFinder::~TransformFinder() Chris@416: { Chris@416: } Chris@416: Chris@416: void Chris@450: TransformFinder::setupBeforeSearchLabel() Chris@450: { Chris@450: bool haveInstalled = Chris@450: TransformFactory::getInstance()->haveInstalledTransforms(); Chris@450: bool haveUninstalled = Chris@450: TransformFactory::getInstance()->haveUninstalledTransforms(); Chris@450: Chris@450: m_beforeSearchLabel->setWordWrap(true); Chris@450: m_beforeSearchLabel->setOpenExternalLinks(true); Chris@450: m_beforeSearchLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse); Chris@450: m_beforeSearchLabel->setMargin(12); Chris@450: m_beforeSearchLabel->setFixedWidth(this->width() - 40); Chris@450: Chris@450: QString base = Chris@450: tr("

Type some text into the search box to search the descriptions of:

  • All currently installed Vamp audio feature extraction plugins
  • All currently installed LADSPA audio effects plugins
  • Vamp plugins that are not currently installed but that have descriptions published via the semantic web
"); Chris@450: Chris@450: QString nopull = Chris@450: tr("Unable to retrieve published descriptions from network!"); Chris@450: Chris@450: QString noinst = Chris@450: tr("No plugins are currently installed!"); Chris@450: Chris@450: if (haveInstalled) { Chris@450: if (haveUninstalled) { Chris@450: m_beforeSearchLabel->setText(base); Chris@450: } else { Chris@450: m_beforeSearchLabel->setText Chris@450: (base + Chris@451: tr("

%1
Perhaps the network connection is down, services are responding too slowly, or a processing problem has occurred.
Only the descriptions of installed plugins will be searched.").arg(nopull)); Chris@450: } Chris@450: } else { Chris@450: if (haveUninstalled) { Chris@450: m_beforeSearchLabel->setText Chris@450: (base + Chris@450: tr("

%1
Only the published descriptions of Vamp feature extraction plugins will be searched.").arg(noinst)); Chris@450: } else { Chris@450: m_beforeSearchLabel->setText Chris@450: (base + Chris@450: tr("

%1
%2
Perhaps the network connection is down, or services are responding too slowly.
No search results will be available.").arg(noinst).arg(nopull)); Chris@450: } Chris@450: } Chris@450: Chris@450: m_beforeSearchLabel->show(); Chris@450: m_resultsFrame->resize(m_resultsFrame->sizeHint()); Chris@450: } Chris@450: Chris@450: void Chris@416: TransformFinder::searchTextChanged(const QString &text) Chris@416: { Chris@682: // cerr << "text is " << text << endl; Chris@424: m_newSearchText = text; Chris@424: } Chris@416: Chris@424: void Chris@424: TransformFinder::timeout() Chris@424: { Chris@448: int maxResults = 60; Chris@416: Chris@424: if (m_newSearchText != "") { Chris@416: Chris@424: QString text = m_newSearchText; Chris@424: m_newSearchText = ""; Chris@424: Chris@424: QStringList keywords = text.split(' ', QString::SkipEmptyParts); Chris@424: TransformFactory::SearchResults results = Chris@424: TransformFactory::getInstance()->search(keywords); Chris@424: Chris@682: // cerr << results.size() << " result(s)..." << endl; Chris@424: Chris@431: std::set sorted; Chris@424: sorted.clear(); Chris@424: for (TransformFactory::SearchResults::const_iterator j = results.begin(); Chris@424: j != results.end(); ++j) { Chris@424: sorted.insert(j->second); Chris@424: } Chris@448: Chris@424: m_sortedResults.clear(); Chris@431: for (std::set::const_iterator j = sorted.end(); Chris@424: j != sorted.begin(); ) { Chris@424: --j; Chris@424: m_sortedResults.push_back(*j); Chris@807: if ((int)m_sortedResults.size() == maxResults) break; Chris@424: } Chris@424: Chris@424: if (m_sortedResults.empty()) m_selectedTransform = ""; Chris@431: else m_selectedTransform = m_sortedResults.begin()->key; Chris@424: Chris@424: m_upToDateCount = 0; Chris@424: Chris@807: for (int j = (int)m_labels.size(); j > (int)m_sortedResults.size(); ) { Chris@424: m_labels[--j]->hide(); Chris@424: } Chris@424: Chris@450: m_beforeSearchLabel->hide(); Chris@450: Chris@448: if (m_sortedResults.empty()) { Chris@448: m_noResultsLabel->show(); Chris@448: m_resultsFrame->resize(m_resultsFrame->sizeHint()); Chris@448: } else { Chris@448: m_noResultsLabel->hide(); Chris@448: } Chris@448: Chris@448: if (m_sortedResults.size() < sorted.size()) { Chris@448: m_infoLabel->setText Chris@448: (tr("Found %n description(s) containing %1, showing the first %2 only", Chris@1408: nullptr, int(sorted.size())).arg(text).arg(m_sortedResults.size())); Chris@448: } else { Chris@448: m_infoLabel->setText Chris@448: (tr("Found %n description(s) containing %1", Chris@1408: nullptr, int(sorted.size())).arg(text)); Chris@448: } Chris@448: Chris@424: return; Chris@416: } Chris@416: Chris@807: if (m_upToDateCount >= (int)m_sortedResults.size()) return; Chris@425: Chris@807: while (m_upToDateCount < (int)m_sortedResults.size()) { Chris@417: Chris@424: int i = m_upToDateCount; Chris@416: Chris@682: // cerr << "sorted size = " << m_sortedResults.size() << endl; Chris@417: Chris@431: TransformDescription desc; Chris@431: TransformId tid = m_sortedResults[i].key; Chris@431: TransformFactory *factory = TransformFactory::getInstance(); Chris@431: TransformFactory::TransformInstallStatus status = Chris@431: factory->getTransformInstallStatus(tid); Chris@431: QString suffix; Chris@431: Chris@431: if (status == TransformFactory::TransformInstalled) { Chris@431: desc = factory->getTransformDescription(tid); Chris@431: } else { Chris@431: desc = factory->getUninstalledTransformDescription(tid); Chris@431: suffix = tr(" (not installed)"); Chris@431: } Chris@419: Chris@419: QString labelText; Chris@431: labelText += tr("%1%2
") Chris@431: .arg(XmlExportable::encodeEntities(desc.name)) Chris@431: .arg(suffix); Chris@424: Chris@416: labelText += "..."; Chris@431: for (TextMatcher::Match::FragmentMap::const_iterator k = Chris@424: m_sortedResults[i].fragments.begin(); Chris@424: k != m_sortedResults[i].fragments.end(); ++k) { Chris@416: labelText += k->second; Chris@416: labelText += "... "; Chris@416: } Chris@416: labelText += tr(""); Chris@417: Chris@419: QString selectedText; Chris@431: selectedText += tr("%1%2
") Chris@431: .arg(XmlExportable::encodeEntities Chris@431: (desc.name == "" ? desc.identifier : desc.name)) Chris@431: .arg(suffix); Chris@431: Chris@431: if (desc.longDescription != "") { Chris@431: selectedText += tr("%1") Chris@431: .arg(XmlExportable::encodeEntities(desc.longDescription)); Chris@431: } else if (desc.description != "") { Chris@431: selectedText += tr("%1") Chris@431: .arg(XmlExportable::encodeEntities(desc.description)); Chris@431: } Chris@419: Chris@436: selectedText += tr(""); Chris@453: if (desc.type != TransformDescription::UnknownType) { Chris@436: selectedText += tr("
      — Plugin type: %1") Chris@453: .arg(XmlExportable::encodeEntities(factory->getTransformTypeName(desc.type))); Chris@431: } Chris@431: if (desc.category != "") { Chris@436: selectedText += tr("
      — Category: %1") Chris@431: .arg(XmlExportable::encodeEntities(desc.category)); Chris@431: } Chris@436: selectedText += tr("
      — System identifier: %1") Chris@424: .arg(XmlExportable::encodeEntities(desc.identifier)); Chris@436: if (desc.infoUrl != "") { Chris@436: selectedText += tr("
      — More information: %1") Chris@436: .arg(desc.infoUrl); Chris@436: } Chris@426: selectedText += tr("
"); Chris@419: Chris@807: if (i >= (int)m_labels.size()) { Chris@419: SelectableLabel *label = new SelectableLabel(m_resultsFrame); Chris@421: m_resultsLayout->addWidget(label); Chris@420: connect(label, SIGNAL(selectionChanged()), this, Chris@420: SLOT(selectedLabelChanged())); Chris@424: connect(label, SIGNAL(doubleClicked()), this, Chris@448: SLOT(labelDoubleClicked())); Chris@421: QPalette palette = label->palette(); Chris@421: label->setPalette(palette); Chris@417: m_labels.push_back(label); Chris@417: } Chris@421: Chris@420: m_labels[i]->setObjectName(desc.identifier); Chris@423: m_labels[i]->setFixedWidth(this->width() - 40); Chris@419: m_labels[i]->setUnselectedText(labelText); Chris@425: Chris@682: // cerr << "selected text: " << selectedText << endl; Chris@419: m_labels[i]->setSelectedText(selectedText); Chris@423: Chris@424: m_labels[i]->setSelected(m_selectedTransform == desc.identifier); Chris@425: Chris@425: if (!m_labels[i]->isVisible()) m_labels[i]->show(); Chris@417: Chris@424: ++m_upToDateCount; Chris@424: Chris@425: if (i == 0) break; Chris@416: } Chris@425: Chris@425: m_resultsFrame->resize(m_resultsFrame->sizeHint()); Chris@416: } Chris@416: Chris@420: void Chris@420: TransformFinder::selectedLabelChanged() Chris@420: { Chris@420: QObject *s = sender(); Chris@420: m_selectedTransform = ""; Chris@807: for (int i = 0; i < (int)m_labels.size(); ++i) { Chris@420: if (!m_labels[i]->isVisible()) continue; Chris@420: if (m_labels[i] == s) { Chris@420: if (m_labels[i]->isSelected()) { Chris@420: m_selectedTransform = m_labels[i]->objectName(); Chris@420: } Chris@420: } else { Chris@420: if (m_labels[i]->isSelected()) { Chris@420: m_labels[i]->setSelected(false); Chris@420: } Chris@420: } Chris@420: } Chris@682: cerr << "selectedLabelChanged: selected transform is now \"" Chris@682: << m_selectedTransform << "\"" << endl; Chris@420: } Chris@420: Chris@448: void Chris@448: TransformFinder::labelDoubleClicked() Chris@448: { Chris@448: // The first click should have selected the label already Chris@448: if (TransformFactory::getInstance()->getTransformInstallStatus Chris@448: (m_selectedTransform) == Chris@448: TransformFactory::TransformInstalled) { Chris@448: accept(); Chris@448: } Chris@448: } Chris@448: Chris@416: TransformId Chris@416: TransformFinder::getTransform() const Chris@416: { Chris@424: return m_selectedTransform; Chris@416: } Chris@416: Chris@426: void Chris@426: TransformFinder::up() Chris@426: { Chris@807: for (int i = 0; i < (int)m_labels.size(); ++i) { Chris@426: if (!m_labels[i]->isVisible()) continue; Chris@426: if (m_labels[i]->objectName() == m_selectedTransform) { Chris@426: if (i > 0) { Chris@426: m_labels[i]->setSelected(false); Chris@426: m_labels[i-1]->setSelected(true); Chris@426: m_selectedTransform = m_labels[i-1]->objectName(); Chris@426: } Chris@426: return; Chris@426: } Chris@426: } Chris@426: } Chris@426: Chris@426: void Chris@426: TransformFinder::down() Chris@426: { Chris@807: for (int i = 0; i < (int)m_labels.size(); ++i) { Chris@426: if (!m_labels[i]->isVisible()) continue; Chris@426: if (m_labels[i]->objectName() == m_selectedTransform) { Chris@807: if (i+1 < (int)m_labels.size() && Chris@426: m_labels[i+1]->isVisible()) { Chris@426: m_labels[i]->setSelected(false); Chris@426: m_labels[i+1]->setSelected(true); Chris@426: m_selectedTransform = m_labels[i+1]->objectName(); Chris@426: } Chris@426: return; Chris@426: } Chris@426: } Chris@426: } Chris@426: