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@423
|
29 #include <QDesktopWidget>
|
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@417
|
35 m_resultsFrame(0),
|
Chris@417
|
36 m_resultsLayout(0)
|
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> 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@423
|
95 QDesktopWidget *desktop = QApplication::desktop();
|
Chris@423
|
96 QRect available = desktop->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@448
|
172 // std::cerr << "text is " << text.toStdString() << std::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@448
|
190 // std::cerr << results.size() << " result(s)..." << std::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@424
|
204 if (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@424
|
212 for (int j = m_labels.size(); j > 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@448
|
228 0, 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@448
|
232 0, sorted.size()).arg(text));
|
Chris@448
|
233 }
|
Chris@448
|
234
|
Chris@424
|
235 return;
|
Chris@416
|
236 }
|
Chris@416
|
237
|
Chris@425
|
238 if (m_upToDateCount >= m_sortedResults.size()) return;
|
Chris@425
|
239
|
Chris@425
|
240 while (m_upToDateCount < m_sortedResults.size()) {
|
Chris@417
|
241
|
Chris@424
|
242 int i = m_upToDateCount;
|
Chris@416
|
243
|
Chris@431
|
244 // std::cerr << "sorted size = " << m_sortedResults.size() << std::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@436
|
288 selectedText += tr("<small>");
|
Chris@431
|
289 if (desc.type != "") {
|
Chris@436
|
290 selectedText += tr("<br> — Plugin type: %1")
|
Chris@431
|
291 .arg(XmlExportable::encodeEntities(desc.type));
|
Chris@431
|
292 }
|
Chris@431
|
293 if (desc.category != "") {
|
Chris@436
|
294 selectedText += tr("<br> — Category: %1")
|
Chris@431
|
295 .arg(XmlExportable::encodeEntities(desc.category));
|
Chris@431
|
296 }
|
Chris@436
|
297 selectedText += tr("<br> — System identifier: %1")
|
Chris@424
|
298 .arg(XmlExportable::encodeEntities(desc.identifier));
|
Chris@436
|
299 if (desc.infoUrl != "") {
|
Chris@436
|
300 selectedText += tr("<br> — More information: <a href=\"%1\">%1</a>")
|
Chris@436
|
301 .arg(desc.infoUrl);
|
Chris@436
|
302 }
|
Chris@426
|
303 selectedText += tr("</small>");
|
Chris@419
|
304
|
Chris@417
|
305 if (i >= m_labels.size()) {
|
Chris@419
|
306 SelectableLabel *label = new SelectableLabel(m_resultsFrame);
|
Chris@421
|
307 m_resultsLayout->addWidget(label);
|
Chris@420
|
308 connect(label, SIGNAL(selectionChanged()), this,
|
Chris@420
|
309 SLOT(selectedLabelChanged()));
|
Chris@424
|
310 connect(label, SIGNAL(doubleClicked()), this,
|
Chris@448
|
311 SLOT(labelDoubleClicked()));
|
Chris@421
|
312 QPalette palette = label->palette();
|
Chris@421
|
313 label->setPalette(palette);
|
Chris@417
|
314 m_labels.push_back(label);
|
Chris@417
|
315 }
|
Chris@421
|
316
|
Chris@420
|
317 m_labels[i]->setObjectName(desc.identifier);
|
Chris@423
|
318 m_labels[i]->setFixedWidth(this->width() - 40);
|
Chris@419
|
319 m_labels[i]->setUnselectedText(labelText);
|
Chris@425
|
320
|
Chris@425
|
321 // std::cerr << "selected text: " << selectedText.toStdString() << std::endl;
|
Chris@419
|
322 m_labels[i]->setSelectedText(selectedText);
|
Chris@423
|
323
|
Chris@424
|
324 m_labels[i]->setSelected(m_selectedTransform == desc.identifier);
|
Chris@425
|
325
|
Chris@425
|
326 if (!m_labels[i]->isVisible()) m_labels[i]->show();
|
Chris@417
|
327
|
Chris@424
|
328 ++m_upToDateCount;
|
Chris@424
|
329
|
Chris@425
|
330 if (i == 0) break;
|
Chris@416
|
331 }
|
Chris@425
|
332
|
Chris@425
|
333 m_resultsFrame->resize(m_resultsFrame->sizeHint());
|
Chris@416
|
334 }
|
Chris@416
|
335
|
Chris@420
|
336 void
|
Chris@420
|
337 TransformFinder::selectedLabelChanged()
|
Chris@420
|
338 {
|
Chris@420
|
339 QObject *s = sender();
|
Chris@420
|
340 m_selectedTransform = "";
|
Chris@420
|
341 for (int i = 0; i < m_labels.size(); ++i) {
|
Chris@420
|
342 if (!m_labels[i]->isVisible()) continue;
|
Chris@420
|
343 if (m_labels[i] == s) {
|
Chris@420
|
344 if (m_labels[i]->isSelected()) {
|
Chris@420
|
345 m_selectedTransform = m_labels[i]->objectName();
|
Chris@420
|
346 }
|
Chris@420
|
347 } else {
|
Chris@420
|
348 if (m_labels[i]->isSelected()) {
|
Chris@420
|
349 m_labels[i]->setSelected(false);
|
Chris@420
|
350 }
|
Chris@420
|
351 }
|
Chris@420
|
352 }
|
Chris@420
|
353 std::cerr << "selectedLabelChanged: selected transform is now \""
|
Chris@420
|
354 << m_selectedTransform.toStdString() << "\"" << std::endl;
|
Chris@420
|
355 }
|
Chris@420
|
356
|
Chris@448
|
357 void
|
Chris@448
|
358 TransformFinder::labelDoubleClicked()
|
Chris@448
|
359 {
|
Chris@448
|
360 // The first click should have selected the label already
|
Chris@448
|
361 if (TransformFactory::getInstance()->getTransformInstallStatus
|
Chris@448
|
362 (m_selectedTransform) ==
|
Chris@448
|
363 TransformFactory::TransformInstalled) {
|
Chris@448
|
364 accept();
|
Chris@448
|
365 }
|
Chris@448
|
366 }
|
Chris@448
|
367
|
Chris@416
|
368 TransformId
|
Chris@416
|
369 TransformFinder::getTransform() const
|
Chris@416
|
370 {
|
Chris@424
|
371 return m_selectedTransform;
|
Chris@416
|
372 }
|
Chris@416
|
373
|
Chris@426
|
374 void
|
Chris@426
|
375 TransformFinder::up()
|
Chris@426
|
376 {
|
Chris@426
|
377 for (int i = 0; i < m_labels.size(); ++i) {
|
Chris@426
|
378 if (!m_labels[i]->isVisible()) continue;
|
Chris@426
|
379 if (m_labels[i]->objectName() == m_selectedTransform) {
|
Chris@426
|
380 if (i > 0) {
|
Chris@426
|
381 m_labels[i]->setSelected(false);
|
Chris@426
|
382 m_labels[i-1]->setSelected(true);
|
Chris@426
|
383 m_selectedTransform = m_labels[i-1]->objectName();
|
Chris@426
|
384 }
|
Chris@426
|
385 return;
|
Chris@426
|
386 }
|
Chris@426
|
387 }
|
Chris@426
|
388 }
|
Chris@426
|
389
|
Chris@426
|
390 void
|
Chris@426
|
391 TransformFinder::down()
|
Chris@426
|
392 {
|
Chris@426
|
393 for (int i = 0; i < m_labels.size(); ++i) {
|
Chris@426
|
394 if (!m_labels[i]->isVisible()) continue;
|
Chris@426
|
395 if (m_labels[i]->objectName() == m_selectedTransform) {
|
Chris@426
|
396 if (i+1 < m_labels.size() &&
|
Chris@426
|
397 m_labels[i+1]->isVisible()) {
|
Chris@426
|
398 m_labels[i]->setSelected(false);
|
Chris@426
|
399 m_labels[i+1]->setSelected(true);
|
Chris@426
|
400 m_selectedTransform = m_labels[i+1]->objectName();
|
Chris@426
|
401 }
|
Chris@426
|
402 return;
|
Chris@426
|
403 }
|
Chris@426
|
404 }
|
Chris@426
|
405 }
|
Chris@426
|
406
|