annotate widgets/TransformFinder.cpp @ 1608:6616e1899daa

Make ImageLayer able to report whether an image format can be opened
author Chris Cannam
date Mon, 11 May 2020 17:28:12 +0100
parents 5e091b0e317c
children
rev   line source
Chris@416 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@416 2
Chris@416 3 /*
Chris@416 4 Sonic Visualiser
Chris@416 5 An audio file viewer and annotation editor.
Chris@416 6 Centre for Digital Music, Queen Mary, University of London.
Chris@416 7 This file copyright 2008 QMUL.
Chris@416 8
Chris@416 9 This program is free software; you can redistribute it and/or
Chris@416 10 modify it under the terms of the GNU General Public License as
Chris@416 11 published by the Free Software Foundation; either version 2 of the
Chris@416 12 License, or (at your option) any later version. See the file
Chris@416 13 COPYING included with this distribution for more information.
Chris@416 14 */
Chris@416 15
Chris@416 16 #include "TransformFinder.h"
Chris@416 17
Chris@424 18 #include "base/XmlExportable.h"
Chris@416 19 #include "transform/TransformFactory.h"
Chris@424 20 #include "SelectableLabel.h"
Chris@416 21
Chris@421 22 #include <QVBoxLayout>
Chris@416 23 #include <QGridLayout>
Chris@416 24 #include <QLineEdit>
Chris@416 25 #include <QLabel>
Chris@416 26 #include <QDialogButtonBox>
Chris@416 27 #include <QScrollArea>
Chris@421 28 #include <QApplication>
Chris@1478 29 #include <QScreen>
Chris@424 30 #include <QTimer>
Chris@426 31 #include <QAction>
Chris@421 32
Chris@416 33 TransformFinder::TransformFinder(QWidget *parent) :
Chris@417 34 QDialog(parent),
Chris@1408 35 m_resultsFrame(nullptr),
Chris@1408 36 m_resultsLayout(nullptr)
Chris@416 37 {
Chris@416 38 setWindowTitle(tr("Find a Transform"));
Chris@416 39
Chris@416 40 QGridLayout *mainGrid = new QGridLayout;
Chris@421 41 mainGrid->setVerticalSpacing(0);
Chris@416 42 setLayout(mainGrid);
Chris@416 43
Chris@416 44 mainGrid->addWidget(new QLabel(tr("Find:")), 0, 0);
Chris@416 45
Chris@416 46 QLineEdit *searchField = new QLineEdit;
Chris@416 47 mainGrid->addWidget(searchField, 0, 1);
Chris@416 48 connect(searchField, SIGNAL(textChanged(const QString &)),
Chris@416 49 this, SLOT(searchTextChanged(const QString &)));
Chris@416 50
Chris@450 51 // m_infoLabel = new QLabel(tr("Type in this box to search descriptions of available and known transforms"));
Chris@450 52 m_infoLabel = new QLabel;
Chris@448 53 mainGrid->addWidget(m_infoLabel, 1, 1);
Chris@448 54
Chris@416 55 m_resultsScroll = new QScrollArea;
Chris@448 56 mainGrid->addWidget(m_resultsScroll, 2, 0, 1, 2);
Chris@448 57 mainGrid->setRowStretch(2, 10);
Chris@416 58
Chris@416 59 QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Ok |
Chris@416 60 QDialogButtonBox::Cancel);
Chris@448 61 mainGrid->addWidget(bb, 3, 0, 1, 2);
Chris@416 62 connect(bb, SIGNAL(accepted()), this, SLOT(accept()));
Chris@416 63 connect(bb, SIGNAL(rejected()), this, SLOT(reject()));
Chris@448 64
Chris@448 65 m_resultsFrame = new QWidget;
Chris@448 66 QPalette palette = m_resultsFrame->palette();
Chris@448 67 palette.setColor(QPalette::Window, palette.color(QPalette::Base));
Chris@448 68 m_resultsFrame->setPalette(palette);
Chris@448 69 m_resultsScroll->setPalette(palette);
Chris@448 70 m_resultsLayout = new QVBoxLayout;
Chris@448 71 m_resultsLayout->setSpacing(0);
Chris@448 72 m_resultsLayout->setContentsMargins(0, 0, 0, 0);
Chris@448 73 m_resultsFrame->setLayout(m_resultsLayout);
Chris@448 74 m_resultsScroll->setWidget(m_resultsFrame);
Chris@448 75 m_resultsFrame->show();
Chris@448 76
Chris@448 77 m_noResultsLabel = new QLabel(tr("<br>&nbsp;&nbsp;No results found"));
Chris@448 78 m_resultsLayout->addWidget(m_noResultsLabel);
Chris@448 79 m_noResultsLabel->hide();
Chris@448 80
Chris@448 81 m_beforeSearchLabel = new QLabel;
Chris@448 82 m_resultsLayout->addWidget(m_beforeSearchLabel);
Chris@448 83 m_beforeSearchLabel->hide();
Chris@416 84
Chris@426 85 QAction *up = new QAction(tr("Up"), this);
Chris@426 86 up->setShortcut(tr("Up"));
Chris@426 87 connect(up, SIGNAL(triggered()), this, SLOT(up()));
Chris@426 88 addAction(up);
Chris@426 89
Chris@426 90 QAction *down = new QAction(tr("Down"), this);
Chris@426 91 down->setShortcut(tr("Down"));
Chris@426 92 connect(down, SIGNAL(triggered()), this, SLOT(down()));
Chris@426 93 addAction(down);
Chris@426 94
Chris@1478 95 QScreen *screen = QApplication::primaryScreen();
Chris@1478 96 QRect available = screen->availableGeometry();
Chris@423 97
Chris@423 98 int width = available.width() / 2;
Chris@423 99 int height = available.height() / 2;
Chris@423 100 if (height < 450) {
Chris@423 101 if (available.height() > 500) height = 450;
Chris@423 102 }
Chris@423 103 if (width < 600) {
Chris@423 104 if (available.width() > 650) width = 600;
Chris@423 105 }
Chris@423 106
Chris@423 107 resize(width, height);
Chris@423 108 raise();
Chris@424 109
Chris@450 110 setupBeforeSearchLabel();
Chris@450 111
Chris@424 112 m_upToDateCount = 0;
Chris@424 113 m_timer = new QTimer(this);
Chris@424 114 connect(m_timer, SIGNAL(timeout()), this, SLOT(timeout()));
Chris@425 115 m_timer->start(30);
Chris@416 116 }
Chris@416 117
Chris@416 118 TransformFinder::~TransformFinder()
Chris@416 119 {
Chris@416 120 }
Chris@416 121
Chris@416 122 void
Chris@450 123 TransformFinder::setupBeforeSearchLabel()
Chris@450 124 {
Chris@450 125 bool haveInstalled =
Chris@450 126 TransformFactory::getInstance()->haveInstalledTransforms();
Chris@450 127 bool haveUninstalled =
Chris@450 128 TransformFactory::getInstance()->haveUninstalledTransforms();
Chris@450 129
Chris@450 130 m_beforeSearchLabel->setWordWrap(true);
Chris@450 131 m_beforeSearchLabel->setOpenExternalLinks(true);
Chris@450 132 m_beforeSearchLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
Chris@450 133 m_beforeSearchLabel->setMargin(12);
Chris@450 134 m_beforeSearchLabel->setFixedWidth(this->width() - 40);
Chris@450 135
Chris@450 136 QString base =
Chris@450 137 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>");
Chris@450 138
Chris@450 139 QString nopull =
Chris@450 140 tr("<b>Unable to retrieve published descriptions from network!</b>");
Chris@450 141
Chris@450 142 QString noinst =
Chris@450 143 tr("<b>No plugins are currently installed!</b>");
Chris@450 144
Chris@450 145 if (haveInstalled) {
Chris@450 146 if (haveUninstalled) {
Chris@450 147 m_beforeSearchLabel->setText(base);
Chris@450 148 } else {
Chris@450 149 m_beforeSearchLabel->setText
Chris@450 150 (base +
Chris@451 151 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));
Chris@450 152 }
Chris@450 153 } else {
Chris@450 154 if (haveUninstalled) {
Chris@450 155 m_beforeSearchLabel->setText
Chris@450 156 (base +
Chris@450 157 tr("<p>%1<br>Only the published descriptions of Vamp feature extraction plugins will be searched.").arg(noinst));
Chris@450 158 } else {
Chris@450 159 m_beforeSearchLabel->setText
Chris@450 160 (base +
Chris@450 161 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));
Chris@450 162 }
Chris@450 163 }
Chris@450 164
Chris@450 165 m_beforeSearchLabel->show();
Chris@450 166 m_resultsFrame->resize(m_resultsFrame->sizeHint());
Chris@450 167 }
Chris@450 168
Chris@450 169 void
Chris@416 170 TransformFinder::searchTextChanged(const QString &text)
Chris@416 171 {
Chris@682 172 // cerr << "text is " << text << endl;
Chris@424 173 m_newSearchText = text;
Chris@424 174 }
Chris@416 175
Chris@424 176 void
Chris@424 177 TransformFinder::timeout()
Chris@424 178 {
Chris@448 179 int maxResults = 60;
Chris@416 180
Chris@424 181 if (m_newSearchText != "") {
Chris@416 182
Chris@424 183 QString text = m_newSearchText;
Chris@424 184 m_newSearchText = "";
Chris@424 185
Chris@424 186 QStringList keywords = text.split(' ', QString::SkipEmptyParts);
Chris@424 187 TransformFactory::SearchResults results =
Chris@424 188 TransformFactory::getInstance()->search(keywords);
Chris@424 189
Chris@682 190 // cerr << results.size() << " result(s)..." << endl;
Chris@424 191
Chris@431 192 std::set<TextMatcher::Match> sorted;
Chris@424 193 sorted.clear();
Chris@424 194 for (TransformFactory::SearchResults::const_iterator j = results.begin();
Chris@424 195 j != results.end(); ++j) {
Chris@424 196 sorted.insert(j->second);
Chris@424 197 }
Chris@448 198
Chris@424 199 m_sortedResults.clear();
Chris@431 200 for (std::set<TextMatcher::Match>::const_iterator j = sorted.end();
Chris@424 201 j != sorted.begin(); ) {
Chris@424 202 --j;
Chris@424 203 m_sortedResults.push_back(*j);
Chris@807 204 if ((int)m_sortedResults.size() == maxResults) break;
Chris@424 205 }
Chris@424 206
Chris@424 207 if (m_sortedResults.empty()) m_selectedTransform = "";
Chris@431 208 else m_selectedTransform = m_sortedResults.begin()->key;
Chris@424 209
Chris@424 210 m_upToDateCount = 0;
Chris@424 211
Chris@807 212 for (int j = (int)m_labels.size(); j > (int)m_sortedResults.size(); ) {
Chris@424 213 m_labels[--j]->hide();
Chris@424 214 }
Chris@424 215
Chris@450 216 m_beforeSearchLabel->hide();
Chris@450 217
Chris@448 218 if (m_sortedResults.empty()) {
Chris@448 219 m_noResultsLabel->show();
Chris@448 220 m_resultsFrame->resize(m_resultsFrame->sizeHint());
Chris@448 221 } else {
Chris@448 222 m_noResultsLabel->hide();
Chris@448 223 }
Chris@448 224
Chris@448 225 if (m_sortedResults.size() < sorted.size()) {
Chris@448 226 m_infoLabel->setText
Chris@448 227 (tr("Found %n description(s) containing <b>%1</b>, showing the first %2 only",
Chris@1408 228 nullptr, int(sorted.size())).arg(text).arg(m_sortedResults.size()));
Chris@448 229 } else {
Chris@448 230 m_infoLabel->setText
Chris@448 231 (tr("Found %n description(s) containing <b>%1</b>",
Chris@1408 232 nullptr, int(sorted.size())).arg(text));
Chris@448 233 }
Chris@448 234
Chris@424 235 return;
Chris@416 236 }
Chris@416 237
Chris@807 238 if (m_upToDateCount >= (int)m_sortedResults.size()) return;
Chris@425 239
Chris@807 240 while (m_upToDateCount < (int)m_sortedResults.size()) {
Chris@417 241
Chris@424 242 int i = m_upToDateCount;
Chris@416 243
Chris@682 244 // cerr << "sorted size = " << m_sortedResults.size() << endl;
Chris@417 245
Chris@431 246 TransformDescription desc;
Chris@431 247 TransformId tid = m_sortedResults[i].key;
Chris@431 248 TransformFactory *factory = TransformFactory::getInstance();
Chris@431 249 TransformFactory::TransformInstallStatus status =
Chris@431 250 factory->getTransformInstallStatus(tid);
Chris@431 251 QString suffix;
Chris@431 252
Chris@431 253 if (status == TransformFactory::TransformInstalled) {
Chris@431 254 desc = factory->getTransformDescription(tid);
Chris@431 255 } else {
Chris@431 256 desc = factory->getUninstalledTransformDescription(tid);
Chris@431 257 suffix = tr("<i> (not installed)</i>");
Chris@431 258 }
Chris@419 259
Chris@419 260 QString labelText;
Chris@431 261 labelText += tr("%1%2<br><small>")
Chris@431 262 .arg(XmlExportable::encodeEntities(desc.name))
Chris@431 263 .arg(suffix);
Chris@424 264
Chris@416 265 labelText += "...";
Chris@431 266 for (TextMatcher::Match::FragmentMap::const_iterator k =
Chris@424 267 m_sortedResults[i].fragments.begin();
Chris@424 268 k != m_sortedResults[i].fragments.end(); ++k) {
Chris@416 269 labelText += k->second;
Chris@416 270 labelText += "... ";
Chris@416 271 }
Chris@416 272 labelText += tr("</small>");
Chris@417 273
Chris@419 274 QString selectedText;
Chris@431 275 selectedText += tr("<b>%1</b>%2<br>")
Chris@431 276 .arg(XmlExportable::encodeEntities
Chris@431 277 (desc.name == "" ? desc.identifier : desc.name))
Chris@431 278 .arg(suffix);
Chris@431 279
Chris@431 280 if (desc.longDescription != "") {
Chris@431 281 selectedText += tr("<small>%1</small>")
Chris@431 282 .arg(XmlExportable::encodeEntities(desc.longDescription));
Chris@431 283 } else if (desc.description != "") {
Chris@431 284 selectedText += tr("<small>%1</small>")
Chris@431 285 .arg(XmlExportable::encodeEntities(desc.description));
Chris@431 286 }
Chris@419 287
Chris@1599 288 QString indentation = tr("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&mdash;");
Chris@1599 289
Chris@436 290 selectedText += tr("<small>");
Chris@453 291 if (desc.type != TransformDescription::UnknownType) {
Chris@1599 292 selectedText += tr("<br>%1 Plugin type: %2")
Chris@1599 293 .arg(indentation)
Chris@1599 294 .arg(XmlExportable::encodeEntities
Chris@1599 295 (factory->getTransformTypeName(desc.type)));
Chris@431 296 }
Chris@431 297 if (desc.category != "") {
Chris@1599 298 selectedText += tr("<br>%1 Category: %2")
Chris@1599 299 .arg(indentation)
Chris@431 300 .arg(XmlExportable::encodeEntities(desc.category));
Chris@431 301 }
Chris@1599 302 selectedText += tr("<br>%1 System identifier: %2")
Chris@1599 303 .arg(indentation)
Chris@424 304 .arg(XmlExportable::encodeEntities(desc.identifier));
Chris@1598 305 if (desc.provider.infoUrl != "") {
Chris@1599 306 selectedText += tr("<br>%1 More information: <a href=\"%2\">%2</a>")
Chris@1599 307 .arg(indentation)
Chris@1598 308 .arg(desc.provider.infoUrl);
Chris@1598 309 }
Chris@1599 310 if (status != TransformFactory::TransformInstalled) {
Chris@1599 311 bool haveSuitableDownloadLink =
Chris@1599 312 (desc.provider.downloadUrl != "" &&
Chris@1599 313 desc.provider.hasDownloadForThisPlatform());
Chris@1599 314 if (haveSuitableDownloadLink) {
Chris@1599 315 selectedText += tr("<br>%1 Download for %2: <a href=\"%3\">%3</a>")
Chris@1599 316 .arg(indentation)
Chris@1599 317 .arg(desc.provider.thisPlatformName())
Chris@1599 318 .arg(desc.provider.downloadUrl);
Chris@1599 319 }
Chris@1599 320 if (!desc.provider.foundInPacks.empty()) {
Chris@1599 321 QStringList packsLinks;
Chris@1599 322 for (auto p: desc.provider.foundInPacks) {
Chris@1599 323 packsLinks <<
Chris@1599 324 tr("<a href=\"%1\">%2</a>").arg(p.second).arg(p.first);
Chris@1599 325 }
Chris@1599 326 selectedText +=
Chris@1599 327 (haveSuitableDownloadLink ?
Chris@1599 328 tr("<br>%1 Also available in: %2") :
Chris@1599 329 tr("<br>%1 Available in: %2"))
Chris@1599 330 .arg(indentation)
Chris@1599 331 .arg(packsLinks.join(tr(", ")));
Chris@1599 332 }
Chris@436 333 }
Chris@1599 334
Chris@426 335 selectedText += tr("</small>");
Chris@419 336
Chris@807 337 if (i >= (int)m_labels.size()) {
Chris@419 338 SelectableLabel *label = new SelectableLabel(m_resultsFrame);
Chris@421 339 m_resultsLayout->addWidget(label);
Chris@420 340 connect(label, SIGNAL(selectionChanged()), this,
Chris@420 341 SLOT(selectedLabelChanged()));
Chris@424 342 connect(label, SIGNAL(doubleClicked()), this,
Chris@448 343 SLOT(labelDoubleClicked()));
Chris@421 344 QPalette palette = label->palette();
Chris@421 345 label->setPalette(palette);
Chris@417 346 m_labels.push_back(label);
Chris@417 347 }
Chris@421 348
Chris@420 349 m_labels[i]->setObjectName(desc.identifier);
Chris@423 350 m_labels[i]->setFixedWidth(this->width() - 40);
Chris@419 351 m_labels[i]->setUnselectedText(labelText);
Chris@425 352
Chris@682 353 // cerr << "selected text: " << selectedText << endl;
Chris@419 354 m_labels[i]->setSelectedText(selectedText);
Chris@423 355
Chris@424 356 m_labels[i]->setSelected(m_selectedTransform == desc.identifier);
Chris@425 357
Chris@425 358 if (!m_labels[i]->isVisible()) m_labels[i]->show();
Chris@417 359
Chris@424 360 ++m_upToDateCount;
Chris@424 361
Chris@425 362 if (i == 0) break;
Chris@416 363 }
Chris@425 364
Chris@425 365 m_resultsFrame->resize(m_resultsFrame->sizeHint());
Chris@416 366 }
Chris@416 367
Chris@420 368 void
Chris@420 369 TransformFinder::selectedLabelChanged()
Chris@420 370 {
Chris@420 371 QObject *s = sender();
Chris@420 372 m_selectedTransform = "";
Chris@807 373 for (int i = 0; i < (int)m_labels.size(); ++i) {
Chris@420 374 if (!m_labels[i]->isVisible()) continue;
Chris@420 375 if (m_labels[i] == s) {
Chris@420 376 if (m_labels[i]->isSelected()) {
Chris@420 377 m_selectedTransform = m_labels[i]->objectName();
Chris@420 378 }
Chris@420 379 } else {
Chris@420 380 if (m_labels[i]->isSelected()) {
Chris@420 381 m_labels[i]->setSelected(false);
Chris@420 382 }
Chris@420 383 }
Chris@420 384 }
Chris@682 385 cerr << "selectedLabelChanged: selected transform is now \""
Chris@682 386 << m_selectedTransform << "\"" << endl;
Chris@420 387 }
Chris@420 388
Chris@448 389 void
Chris@448 390 TransformFinder::labelDoubleClicked()
Chris@448 391 {
Chris@448 392 // The first click should have selected the label already
Chris@448 393 if (TransformFactory::getInstance()->getTransformInstallStatus
Chris@448 394 (m_selectedTransform) ==
Chris@448 395 TransformFactory::TransformInstalled) {
Chris@448 396 accept();
Chris@448 397 }
Chris@448 398 }
Chris@448 399
Chris@416 400 TransformId
Chris@416 401 TransformFinder::getTransform() const
Chris@416 402 {
Chris@424 403 return m_selectedTransform;
Chris@416 404 }
Chris@416 405
Chris@426 406 void
Chris@426 407 TransformFinder::up()
Chris@426 408 {
Chris@807 409 for (int i = 0; i < (int)m_labels.size(); ++i) {
Chris@426 410 if (!m_labels[i]->isVisible()) continue;
Chris@426 411 if (m_labels[i]->objectName() == m_selectedTransform) {
Chris@426 412 if (i > 0) {
Chris@426 413 m_labels[i]->setSelected(false);
Chris@426 414 m_labels[i-1]->setSelected(true);
Chris@426 415 m_selectedTransform = m_labels[i-1]->objectName();
Chris@426 416 }
Chris@426 417 return;
Chris@426 418 }
Chris@426 419 }
Chris@426 420 }
Chris@426 421
Chris@426 422 void
Chris@426 423 TransformFinder::down()
Chris@426 424 {
Chris@807 425 for (int i = 0; i < (int)m_labels.size(); ++i) {
Chris@426 426 if (!m_labels[i]->isVisible()) continue;
Chris@426 427 if (m_labels[i]->objectName() == m_selectedTransform) {
Chris@807 428 if (i+1 < (int)m_labels.size() &&
Chris@426 429 m_labels[i+1]->isVisible()) {
Chris@426 430 m_labels[i]->setSelected(false);
Chris@426 431 m_labels[i+1]->setSelected(true);
Chris@426 432 m_selectedTransform = m_labels[i+1]->objectName();
Chris@426 433 }
Chris@426 434 return;
Chris@426 435 }
Chris@426 436 }
Chris@426 437 }
Chris@426 438