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