svgui  1.9
TransformFinder.cpp
Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     This file copyright 2008 QMUL.
00008     
00009     This program is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU General Public License as
00011     published by the Free Software Foundation; either version 2 of the
00012     License, or (at your option) any later version.  See the file
00013     COPYING included with this distribution for more information.
00014 */
00015 
00016 #include "TransformFinder.h"
00017 
00018 #include "base/XmlExportable.h"
00019 #include "transform/TransformFactory.h"
00020 #include "SelectableLabel.h"
00021 
00022 #include <QVBoxLayout>
00023 #include <QGridLayout>
00024 #include <QLineEdit>
00025 #include <QLabel>
00026 #include <QDialogButtonBox>
00027 #include <QScrollArea>
00028 #include <QApplication>
00029 #include <QDesktopWidget>
00030 #include <QTimer>
00031 #include <QAction>
00032 
00033 TransformFinder::TransformFinder(QWidget *parent) :
00034     QDialog(parent),
00035     m_resultsFrame(0),
00036     m_resultsLayout(0)
00037 {
00038     setWindowTitle(tr("Find a Transform"));
00039     
00040     QGridLayout *mainGrid = new QGridLayout;
00041     mainGrid->setVerticalSpacing(0);
00042     setLayout(mainGrid);
00043 
00044     mainGrid->addWidget(new QLabel(tr("Find:")), 0, 0);
00045     
00046     QLineEdit *searchField = new QLineEdit;
00047     mainGrid->addWidget(searchField, 0, 1);
00048     connect(searchField, SIGNAL(textChanged(const QString &)),
00049             this, SLOT(searchTextChanged(const QString &)));
00050 
00051 //    m_infoLabel = new QLabel(tr("Type in this box to search descriptions of available and known transforms"));
00052     m_infoLabel = new QLabel;
00053     mainGrid->addWidget(m_infoLabel, 1, 1);
00054 
00055     m_resultsScroll = new QScrollArea;
00056     mainGrid->addWidget(m_resultsScroll, 2, 0, 1, 2);
00057     mainGrid->setRowStretch(2, 10);
00058 
00059     QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Ok |
00060                                                 QDialogButtonBox::Cancel);
00061     mainGrid->addWidget(bb, 3, 0, 1, 2);
00062     connect(bb, SIGNAL(accepted()), this, SLOT(accept()));
00063     connect(bb, SIGNAL(rejected()), this, SLOT(reject()));
00064 
00065     m_resultsFrame = new QWidget;
00066     QPalette palette = m_resultsFrame->palette();
00067     palette.setColor(QPalette::Window, palette.color(QPalette::Base));
00068     m_resultsFrame->setPalette(palette);
00069     m_resultsScroll->setPalette(palette);
00070     m_resultsLayout = new QVBoxLayout;
00071     m_resultsLayout->setSpacing(0);
00072     m_resultsLayout->setContentsMargins(0, 0, 0, 0);
00073     m_resultsFrame->setLayout(m_resultsLayout);
00074     m_resultsScroll->setWidget(m_resultsFrame);
00075     m_resultsFrame->show();
00076 
00077     m_noResultsLabel = new QLabel(tr("<br>&nbsp;&nbsp;No results found"));
00078     m_resultsLayout->addWidget(m_noResultsLabel);
00079     m_noResultsLabel->hide();
00080 
00081     m_beforeSearchLabel = new QLabel;
00082     m_resultsLayout->addWidget(m_beforeSearchLabel);
00083     m_beforeSearchLabel->hide();
00084 
00085     QAction *up = new QAction(tr("Up"), this);
00086     up->setShortcut(tr("Up"));
00087     connect(up, SIGNAL(triggered()), this, SLOT(up()));
00088     addAction(up);
00089 
00090     QAction *down = new QAction(tr("Down"), this);
00091     down->setShortcut(tr("Down"));
00092     connect(down, SIGNAL(triggered()), this, SLOT(down()));
00093     addAction(down);
00094 
00095     QDesktopWidget *desktop = QApplication::desktop();
00096     QRect available = desktop->availableGeometry();
00097 
00098     int width = available.width() / 2;
00099     int height = available.height() / 2;
00100     if (height < 450) {
00101         if (available.height() > 500) height = 450;
00102     }
00103     if (width < 600) {
00104         if (available.width() > 650) width = 600;
00105     }
00106 
00107     resize(width, height);
00108     raise();
00109 
00110     setupBeforeSearchLabel();
00111 
00112     m_upToDateCount = 0;
00113     m_timer = new QTimer(this);
00114     connect(m_timer, SIGNAL(timeout()), this, SLOT(timeout()));
00115     m_timer->start(30);
00116 }
00117 
00118 TransformFinder::~TransformFinder()
00119 {
00120 }
00121 
00122 void
00123 TransformFinder::setupBeforeSearchLabel()
00124 {
00125     bool haveInstalled =
00126         TransformFactory::getInstance()->haveInstalledTransforms();
00127     bool haveUninstalled =
00128         TransformFactory::getInstance()->haveUninstalledTransforms();
00129 
00130     m_beforeSearchLabel->setWordWrap(true);
00131     m_beforeSearchLabel->setOpenExternalLinks(true);
00132     m_beforeSearchLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
00133     m_beforeSearchLabel->setMargin(12);
00134     m_beforeSearchLabel->setFixedWidth(this->width() - 40);
00135 
00136     QString base =
00137         tr("<p>Type some text into the search box to search the descriptions of:<ul><li>All currently installed <a href=\"http://www.vamp-plugins.org/\">Vamp</a> audio feature extraction plugins</li><li>All currently installed <a href=\"http://www.ladspa.org/\">LADSPA</a> audio effects plugins</li><li>Vamp plugins that are not currently installed but that have descriptions published via the semantic web</li></ul>");
00138 
00139     QString nopull =
00140         tr("<b>Unable to retrieve published descriptions from network!</b>");
00141 
00142     QString noinst =
00143         tr("<b>No plugins are currently installed!</b>");
00144 
00145     if (haveInstalled) {
00146         if (haveUninstalled) {
00147             m_beforeSearchLabel->setText(base);
00148         } else {
00149             m_beforeSearchLabel->setText
00150                 (base +
00151                  tr("<p>%1<br>Perhaps the network connection is down, services are responding too slowly, or a processing problem has occurred.<br>Only the descriptions of installed plugins will be searched.").arg(nopull));
00152         }
00153     } else {
00154         if (haveUninstalled) {
00155             m_beforeSearchLabel->setText
00156                 (base +
00157                  tr("<p>%1<br>Only the published descriptions of Vamp feature extraction plugins will be searched.").arg(noinst));
00158         } else {
00159             m_beforeSearchLabel->setText
00160                 (base +
00161                  tr("<p>%1<br>%2<br>Perhaps the network connection is down, or services are responding too slowly.<br>No search results will be available.").arg(noinst).arg(nopull));
00162         }
00163     }
00164 
00165     m_beforeSearchLabel->show();
00166     m_resultsFrame->resize(m_resultsFrame->sizeHint());
00167 }
00168 
00169 void
00170 TransformFinder::searchTextChanged(const QString &text)
00171 {
00172 //    cerr << "text is " << text << endl;
00173     m_newSearchText = text;
00174 }
00175 
00176 void
00177 TransformFinder::timeout()
00178 {
00179     int maxResults = 60;
00180     
00181     if (m_newSearchText != "") {
00182 
00183         QString text = m_newSearchText;
00184         m_newSearchText = "";
00185 
00186         QStringList keywords = text.split(' ', QString::SkipEmptyParts);
00187         TransformFactory::SearchResults results =
00188             TransformFactory::getInstance()->search(keywords);
00189         
00190 //        cerr << results.size() << " result(s)..." << endl;
00191         
00192         std::set<TextMatcher::Match> sorted;
00193         sorted.clear();
00194         for (TransformFactory::SearchResults::const_iterator j = results.begin();
00195              j != results.end(); ++j) {
00196             sorted.insert(j->second);
00197         }
00198         
00199         m_sortedResults.clear();
00200         for (std::set<TextMatcher::Match>::const_iterator j = sorted.end();
00201              j != sorted.begin(); ) {
00202             --j;
00203             m_sortedResults.push_back(*j);
00204             if ((int)m_sortedResults.size() == maxResults) break;
00205         }
00206 
00207         if (m_sortedResults.empty()) m_selectedTransform = "";
00208         else m_selectedTransform = m_sortedResults.begin()->key;
00209 
00210         m_upToDateCount = 0;
00211 
00212         for (int j = (int)m_labels.size(); j > (int)m_sortedResults.size(); ) {
00213             m_labels[--j]->hide();
00214         }
00215 
00216         m_beforeSearchLabel->hide();
00217 
00218         if (m_sortedResults.empty()) {
00219             m_noResultsLabel->show();
00220             m_resultsFrame->resize(m_resultsFrame->sizeHint());
00221         } else {
00222             m_noResultsLabel->hide();
00223         }
00224 
00225         if (m_sortedResults.size() < sorted.size()) {
00226             m_infoLabel->setText
00227                 (tr("Found %n description(s) containing <b>%1</b>, showing the first %2 only",
00228                     0, sorted.size()).arg(text).arg(m_sortedResults.size()));
00229         } else {
00230             m_infoLabel->setText
00231                 (tr("Found %n description(s) containing <b>%1</b>",
00232                     0, sorted.size()).arg(text));
00233         }
00234 
00235         return;
00236     }
00237 
00238     if (m_upToDateCount >= (int)m_sortedResults.size()) return;
00239 
00240     while (m_upToDateCount < (int)m_sortedResults.size()) {
00241 
00242         int i = m_upToDateCount;
00243 
00244 //        cerr << "sorted size = " << m_sortedResults.size() << endl;
00245 
00246         TransformDescription desc;
00247         TransformId tid = m_sortedResults[i].key;
00248         TransformFactory *factory = TransformFactory::getInstance();
00249         TransformFactory::TransformInstallStatus status =
00250             factory->getTransformInstallStatus(tid);
00251         QString suffix;
00252 
00253         if (status == TransformFactory::TransformInstalled) {
00254             desc = factory->getTransformDescription(tid);
00255         } else {
00256             desc = factory->getUninstalledTransformDescription(tid);
00257             suffix = tr("<i> (not installed)</i>");
00258         }
00259 
00260         QString labelText;
00261         labelText += tr("%1%2<br><small>")
00262             .arg(XmlExportable::encodeEntities(desc.name))
00263             .arg(suffix);
00264 
00265         labelText += "...";
00266         for (TextMatcher::Match::FragmentMap::const_iterator k =
00267                  m_sortedResults[i].fragments.begin();
00268              k != m_sortedResults[i].fragments.end(); ++k) {
00269             labelText += k->second;
00270             labelText += "... ";
00271         }
00272         labelText += tr("</small>");
00273 
00274         QString selectedText;
00275         selectedText += tr("<b>%1</b>%2<br>")
00276             .arg(XmlExportable::encodeEntities
00277                  (desc.name == "" ? desc.identifier : desc.name))
00278             .arg(suffix);
00279 
00280         if (desc.longDescription != "") {
00281             selectedText += tr("<small>%1</small>")
00282                 .arg(XmlExportable::encodeEntities(desc.longDescription));
00283         } else if (desc.description != "") {
00284             selectedText += tr("<small>%1</small>")
00285                 .arg(XmlExportable::encodeEntities(desc.description));
00286         }
00287 
00288         selectedText += tr("<small>");
00289         if (desc.type != TransformDescription::UnknownType) {
00290             selectedText += tr("<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&mdash; Plugin type: %1")
00291                 .arg(XmlExportable::encodeEntities(factory->getTransformTypeName(desc.type)));
00292         }
00293         if (desc.category != "") {
00294             selectedText += tr("<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&mdash; Category: %1")
00295                 .arg(XmlExportable::encodeEntities(desc.category));
00296         }
00297         selectedText += tr("<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&mdash; System identifier: %1")
00298             .arg(XmlExportable::encodeEntities(desc.identifier));
00299         if (desc.infoUrl != "") {
00300             selectedText += tr("<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&mdash; More information: <a href=\"%1\">%1</a>")
00301                 .arg(desc.infoUrl);
00302         }
00303         selectedText += tr("</small>");
00304 
00305         if (i >= (int)m_labels.size()) {
00306             SelectableLabel *label = new SelectableLabel(m_resultsFrame);
00307             m_resultsLayout->addWidget(label);
00308             connect(label, SIGNAL(selectionChanged()), this,
00309                     SLOT(selectedLabelChanged()));
00310             connect(label, SIGNAL(doubleClicked()), this,
00311                     SLOT(labelDoubleClicked()));
00312             QPalette palette = label->palette();
00313             label->setPalette(palette);
00314             m_labels.push_back(label);
00315         }
00316 
00317         m_labels[i]->setObjectName(desc.identifier);
00318         m_labels[i]->setFixedWidth(this->width() - 40);
00319         m_labels[i]->setUnselectedText(labelText);
00320 
00321 //        cerr << "selected text: " << selectedText << endl;
00322         m_labels[i]->setSelectedText(selectedText);
00323 
00324         m_labels[i]->setSelected(m_selectedTransform == desc.identifier);
00325 
00326         if (!m_labels[i]->isVisible()) m_labels[i]->show();
00327 
00328         ++m_upToDateCount;
00329 
00330         if (i == 0) break;
00331     }
00332 
00333     m_resultsFrame->resize(m_resultsFrame->sizeHint());
00334 }
00335 
00336 void
00337 TransformFinder::selectedLabelChanged()
00338 {
00339     QObject *s = sender();
00340     m_selectedTransform = "";
00341     for (int i = 0; i < (int)m_labels.size(); ++i) {
00342         if (!m_labels[i]->isVisible()) continue;
00343         if (m_labels[i] == s) {
00344             if (m_labels[i]->isSelected()) {
00345                 m_selectedTransform = m_labels[i]->objectName();
00346             }
00347         } else {
00348             if (m_labels[i]->isSelected()) {
00349                 m_labels[i]->setSelected(false);
00350             }
00351         }
00352     }
00353     cerr << "selectedLabelChanged: selected transform is now \""
00354               << m_selectedTransform << "\"" << endl;
00355 }
00356 
00357 void
00358 TransformFinder::labelDoubleClicked()
00359 {
00360     // The first click should have selected the label already
00361     if (TransformFactory::getInstance()->getTransformInstallStatus
00362         (m_selectedTransform) == 
00363         TransformFactory::TransformInstalled) {
00364         accept();
00365     }
00366 }
00367 
00368 TransformId
00369 TransformFinder::getTransform() const
00370 {
00371     return m_selectedTransform;
00372 }
00373 
00374 void
00375 TransformFinder::up()
00376 {
00377     for (int i = 0; i < (int)m_labels.size(); ++i) {
00378         if (!m_labels[i]->isVisible()) continue;
00379         if (m_labels[i]->objectName() == m_selectedTransform) {
00380             if (i > 0) {
00381                 m_labels[i]->setSelected(false);
00382                 m_labels[i-1]->setSelected(true);
00383                 m_selectedTransform = m_labels[i-1]->objectName();
00384             }
00385             return;
00386         }
00387     }
00388 }
00389 
00390 void
00391 TransformFinder::down()
00392 {
00393     for (int i = 0; i < (int)m_labels.size(); ++i) {
00394         if (!m_labels[i]->isVisible()) continue;
00395         if (m_labels[i]->objectName() == m_selectedTransform) {
00396             if (i+1 < (int)m_labels.size() &&
00397                 m_labels[i+1]->isVisible()) {
00398                 m_labels[i]->setSelected(false);
00399                 m_labels[i+1]->setSelected(true);
00400                 m_selectedTransform = m_labels[i+1]->objectName();
00401             }
00402             return;
00403         }
00404     }
00405 }
00406