annotate widgets/TransformFinder.cpp @ 1605:ae2d5f8ff005

When asked to render the whole view width, we need to wait for the layers to be ready before we can determine what the width is
author Chris Cannam
date Thu, 30 Apr 2020 14:47:13 +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