1 //=============================================================================
2 // MuseScore
3 // Music Composition & Notation
4 //
5 // Copyright (C) 2014 Werner Schweer
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License version 2
9 // as published by the Free Software Foundation and appearing in
10 // the file LICENCE.GPL
11 //=============================================================================
12
13 #include "scoreBrowser.h"
14 #include "musescore.h"
15 #include "icons.h"
16 #include "libmscore/score.h"
17
18 namespace Ms {
19
20 //---------------------------------------------------------
21 // sizeHint
22 //---------------------------------------------------------
23
sizeHint() const24 QSize ScoreListWidget::sizeHint() const
25 {
26 int cols = (width()-SPACE) / (CELLW + SPACE);
27 int n = 0;
28 for (int i = 0; i < count(); ++i) {
29 if (!item(i)->isHidden())
30 n++;
31 }
32
33 int rows = 1;
34 if (cols > 0)
35 rows = (n+cols-1) / cols;
36 if (rows <= 0)
37 rows = 1;
38 return QSize(cols * CELLW, rows * (CELLH + SPACE) + SPACE);
39 }
40
41 //---------------------------------------------------------
42 // ScoreItem
43 //---------------------------------------------------------
44
45 class ScoreItem : public QListWidgetItem
46 {
47 ScoreInfo _info;
48
49 public:
ScoreItem(const ScoreInfo & i)50 ScoreItem(const ScoreInfo& i) : QListWidgetItem(), _info(i) {}
info() const51 const ScoreInfo& info() const { return _info; }
52 };
53
54 //---------------------------------------------------------
55 // ScoreBrowser
56 //---------------------------------------------------------
57
ScoreBrowser(QWidget * parent)58 ScoreBrowser::ScoreBrowser(QWidget* parent)
59 : QWidget(parent)
60 {
61 setupUi(this);
62 scoreList->setLayout(new QVBoxLayout);
63 scoreList->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
64 scoreList->layout()->setMargin(0);
65 _noMatchedScoresLabel = new QLabel(tr("There are no templates matching the current search."));
66 _noMatchedScoresLabel->setHidden(true);
67 _noMatchedScoresLabel->setObjectName("noMatchedScoresLabel");
68 scoreList->layout()->addWidget(_noMatchedScoresLabel);
69 connect(preview, SIGNAL(doubleClicked(QString)), SIGNAL(scoreActivated(QString)));
70 if (!_showPreview)
71 preview->setVisible(false);
72 }
73
74 //---------------------------------------------------------
75 // createScoreList
76 //---------------------------------------------------------
77
createScoreList()78 ScoreListWidget* ScoreBrowser::createScoreList()
79 {
80 ScoreListWidget* sl = new ScoreListWidget;
81 sl->setWrapping(true);
82 sl->setViewMode(QListView::IconMode);
83 sl->setIconSize(QSize(sl->cellWidth(), sl->cellHeight() - 30));
84 sl->setSpacing(sl->space());
85 sl->setResizeMode(QListView::Adjust);
86 sl->setFlow(QListView::LeftToRight);
87 sl->setMovement(QListView::Static);
88 sl->setTextElideMode(Qt::ElideRight);
89 sl->setWordWrap(true);
90 sl->setUniformItemSizes(true);
91 sl->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
92 sl->setLineWidth(0);
93 sl->setFrameStyle(QFrame::NoFrame | QFrame::Plain);
94 sl->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
95 sl->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
96 sl->setLayoutMode(QListView::SinglePass);
97 sl->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
98 if (!_showPreview)
99 sl->setSelectionMode(QAbstractItemView::NoSelection);
100
101 if (!style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick)) {
102 // Set our handler for item clicks only if Qt
103 // doesn't treat a click as an item activation.
104 connect(sl, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(scoreClicked(QListWidgetItem*)), Qt::QueuedConnection);
105 }
106 connect(sl, SIGNAL(itemActivated(QListWidgetItem*)), SLOT(setScoreActivated(QListWidgetItem*)));
107 scoreLists.append(sl);
108 return sl;
109 }
110
111 //---------------------------------------------------------
112 // genScoreItem
113 //---------------------------------------------------------
114
genScoreItem(const QFileInfo & fi,ScoreListWidget * l)115 ScoreItem* ScoreBrowser::genScoreItem(const QFileInfo& fi, ScoreListWidget* l)
116 {
117 ScoreInfo si(fi);
118
119 QPixmap pm(l->iconSize() * qApp->devicePixelRatio());
120 if (!QPixmapCache::find(fi.filePath(), &pm)) {
121 //load and scale pixmap
122 QPixmap pixmap = mscore->extractThumbnail(fi.filePath());
123 if (pixmap.isNull())
124 pixmap = icons[int(Icons::file_ICON)]->pixmap(QSize(50,60));
125 pixmap = pixmap.scaled(pm.width() - 2, pm.height() - 2, Qt::KeepAspectRatio, Qt::SmoothTransformation);
126 // draw pixmap and add border
127 pm.fill(Qt::transparent);
128 QPainter painter( &pm );
129 painter.setRenderHint(QPainter::Antialiasing);
130 painter.setRenderHint(QPainter::TextAntialiasing);
131 painter.drawPixmap(0, 0, pixmap);
132 painter.setPen(QPen(QColor(0, 0, 0, 128), 1));
133 painter.setBrush(Qt::white);
134 if (fi.completeBaseName() == "00-Blank" || fi.completeBaseName() == "Create_New_Score") {
135 qreal round = 8.0 * qApp->devicePixelRatio();
136 painter.drawRoundedRect(QRectF(0, 0, pm.width() - 1 , pm.height() - 1), round, round);
137 }
138 else
139 painter.drawRect(0, 0, pm.width() - 1, pm.height() - 1);
140 if (fi.completeBaseName() != "00-Blank")
141 painter.drawPixmap(1, 1, pixmap);
142 painter.end();
143 QPixmapCache::insert(fi.filePath(), pm);
144 }
145
146 si.setPixmap(pm);
147 ScoreItem* item = new ScoreItem(si);
148 item->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
149
150 QFont f = item->font();
151 f.setPixelSize(11);
152 f.setBold(_boldTitle);
153 if (fi.completeBaseName() == "00-Blank") {
154 item->setText(tr("Choose Instruments"));
155 f.setBold(true);
156 }
157 else if (fi.completeBaseName() == "Create_New_Score") {
158 item->setText(tr("Create New Score…"));
159 f.setBold(true);
160 }
161 else {
162 QString s(si.completeBaseName());
163 if (!s.isEmpty() && s[0].isNumber() && _stripNumbers)
164 s = s.mid(3);
165 s = s.replace('_', ' ');
166 item->setText(s);
167 }
168 item->setFont(f);
169 item->setTextAlignment(Qt::AlignHCenter | Qt::AlignTop);
170 item->setIcon(QIcon(pm));
171 item->setSizeHint(l->cellSize());
172 return item;
173 }
174
175 //---------------------------------------------------------
176 // setScores
177 //---------------------------------------------------------
178
setScores(QFileInfoList & s)179 void ScoreBrowser::setScores(QFileInfoList& s)
180 {
181 qDeleteAll(scoreLists);
182 scoreLists.clear();
183
184 QVBoxLayout* l = static_cast<QVBoxLayout*>(scoreList->layout());
185 QLayoutItem* child;
186 while (l->count()) {
187 child = l->takeAt(0);
188 if (child->widget() != 0) {
189 if (child->widget()->objectName() == "noMatchedScoresLabel") // do not delete
190 continue;
191 delete child->widget();
192 }
193 delete child;
194 }
195
196 ScoreListWidget* sl = 0;
197
198 QStringList filter = { "*.mscz", "*.mscx" };
199
200 if (_showCustomCategory)
201 std::sort(s.begin(), s.end(), [](QFileInfo a, QFileInfo b)->bool { return a.fileName() < b.fileName(); });
202
203 QSet<QString> entries; //to avoid duplicates
204 for (const QFileInfo& fil : s) {
205 if (fil.isDir()) {
206 QString st(fil.fileName());
207 if (!st.isEmpty() && st[0].isNumber() && _stripNumbers)
208 st = st.mid(3);
209 st = st.replace('_', ' ');
210 QLabel* label = new QLabel(st);
211 QFont f = label->font();
212 f.setBold(true);
213 label->setFont(f);
214 static_cast<QVBoxLayout*>(l)->addWidget(label);
215 QDir dir(fil.filePath());
216 sl = createScoreList();
217 l->addWidget(sl);
218 unsigned count = 0; //nbr of entries added
219 for (const QFileInfo& fi : dir.entryInfoList(filter, QDir::Files, QDir::Name)) {
220 if (entries.contains(fi.filePath()))
221 continue;
222 sl->addItem(genScoreItem(fi, sl));
223 count++;
224 entries.insert(fi.filePath());
225 }
226 if (count == 0) {
227 delete label;
228 delete sl;
229 }
230 sl = 0;
231 }
232 }
233 for (const QFileInfo& fi : s) {
234 if (fi.isFile()) {
235 QString st = fi.filePath();
236 if (entries.contains(st))
237 continue;
238 if (st.endsWith(".mscz") || st.endsWith(".mscx")) {
239 if (!sl) {
240 if (_showCustomCategory) {
241 QLabel* label = new QLabel(tr("Custom Templates"));
242 QFont f = label->font();
243 f.setBold(true);
244 label->setFont(f);
245 l->insertWidget(2,label);
246 }
247 sl = createScoreList();
248 l->insertWidget(3,sl);
249 }
250 sl->addItem(genScoreItem(fi, sl));
251 entries.insert(st);
252 }
253 }
254 }
255 }
256
257 //---------------------------------------------------------
258 // selectFirst
259 //---------------------------------------------------------
260
selectFirst()261 void ScoreBrowser::selectFirst()
262 {
263 if (scoreLists.isEmpty())
264 return;
265 ScoreListWidget* w = scoreLists.front();
266 if (w->count() == 0)
267 return;
268 ScoreItem* item = static_cast<ScoreItem*>(w->item(0));
269 w->setCurrentItem(item);
270 preview->setScore(item->info());
271 }
272
273 //---------------------------------------------------------
274 // selectLast
275 //---------------------------------------------------------
276
selectLast()277 void ScoreBrowser::selectLast()
278 {
279 if (scoreLists.isEmpty())
280 return;
281 ScoreListWidget* w = scoreLists.front();
282 if (w->count() == 0)
283 return;
284 ScoreItem* item = static_cast<ScoreItem*>(w->item(w->count()-1));
285 w->setCurrentItem(item);
286 preview->setScore(item->info());
287 }
288
289 //---------------------------------------------------------
290 // filter
291 // filter which scores are visible based on searchString
292 //---------------------------------------------------------
filter(const QString & searchString)293 void ScoreBrowser::filter(const QString &searchString)
294 {
295 int numCategoriesWithMathingScores = 0;
296
297 for (ScoreListWidget* list : scoreLists) {
298 int numMatchedScores = 0;
299
300 for (int i = 0; i < list->count(); ++i) {
301 ScoreItem* score = static_cast<ScoreItem*>(list->item(i));
302 QString title = score->text();
303 bool isMatch = title.contains(searchString, Qt::CaseInsensitive);
304
305 score->setHidden(!isMatch);
306
307 if (isMatch)
308 numMatchedScores++;
309 }
310
311 int listindex = scoreList->layout()->indexOf(list);
312 QLayoutItem *label = scoreList->layout()->itemAt(listindex - 1);
313 if (label && label->widget()) {
314 label->widget()->setHidden(numMatchedScores == 0);
315 }
316
317 list->setHidden(numMatchedScores == 0);
318 if (numMatchedScores > 0) {
319 numCategoriesWithMathingScores++;
320 }
321 }
322
323 _noMatchedScoresLabel->setHidden(numCategoriesWithMathingScores > 0);
324 }
325
326 //---------------------------------------------------------
327 // scoreClicked
328 //---------------------------------------------------------
329
scoreClicked(QListWidgetItem * current)330 void ScoreBrowser::scoreClicked(QListWidgetItem* current)
331 {
332 if (!current)
333 return;
334
335 ScoreItem* item = static_cast<ScoreItem*>(current);
336 if (!_showPreview)
337 emit scoreActivated(item->info().filePath());
338 else {
339 preview->setScore(item->info());
340 emit scoreSelected(item->info().filePath());
341
342 for (ScoreListWidget* sl : scoreLists) {
343 if (static_cast<QListWidget*>(sl) != item->listWidget()) {
344 sl->clearSelection();
345 }
346 }
347 }
348 }
349
350 //---------------------------------------------------------
351 // setScoreActivated
352 //---------------------------------------------------------
353
setScoreActivated(QListWidgetItem * val)354 void ScoreBrowser::setScoreActivated(QListWidgetItem* val)
355 {
356 ScoreItem* item = static_cast<ScoreItem*>(val);
357 emit scoreActivated(item->info().filePath());
358 }
359
360 }
361
362