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 "templateBrowser.h"
14 #include "libmscore/score.h"
15 
16 namespace Ms {
17 
18 //---------------------------------------------------------
19 //   TemplateItem
20 //---------------------------------------------------------
21 
22 class TemplateItem : public QTreeWidgetItem
23       {
24       ScoreInfo _info;
25 
26    public:
27       TemplateItem(const ScoreInfo& i, QTreeWidgetItem* parent = 0);
info() const28       const ScoreInfo& info() const { return _info; }
29       };
30 
TemplateItem(const ScoreInfo & i,QTreeWidgetItem * parent)31 TemplateItem::TemplateItem(const ScoreInfo &i, QTreeWidgetItem *parent) : QTreeWidgetItem(parent, 0), _info(i)
32       {
33       setFlags(Qt::ItemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemNeverHasChildren));
34       }
35 
36 //---------------------------------------------------------
37 //   TemplateCategory
38 //---------------------------------------------------------
39 
40 class TemplateCategory : public QTreeWidgetItem
41       {
42 
43    public:
44       TemplateCategory(QString name, QTreeWidget* parent = 0);
45       };
46 
TemplateCategory(QString name,QTreeWidget * parent)47 TemplateCategory::TemplateCategory(QString name, QTreeWidget *parent)
48    : QTreeWidgetItem(parent, 0)
49       {
50       const int nameCol = 0;
51       setText(nameCol, name);
52       // provide feedback to blind users that they have selected a category rather than a template
53       setData(nameCol, Qt::AccessibleTextRole, QVariant(QObject::tr("%1 category").arg(name))); // spoken by screen readers
54       QFont nameFont = QFont();
55       nameFont.setBold(true);
56       setFont(nameCol, nameFont);
57       setFlags(Qt::ItemIsEnabled);
58       }
59 
60 //---------------------------------------------------------
61 //   TemplateBrowser
62 //---------------------------------------------------------
63 
TemplateBrowser(QWidget * parent)64 TemplateBrowser::TemplateBrowser(QWidget* parent)
65    : QWidget(parent)
66       {
67       setupUi(this);
68       if (_showPreview) {
69             preview->displayInfo(false);
70             connect(preview, SIGNAL(doubleClicked(QString)), SIGNAL(scoreActivated(QString)));
71             }
72       else
73             preview->setVisible(false);
74       connect(templateTree, &QTreeWidget::itemSelectionChanged, this, &TemplateBrowser::scoreClicked);
75       connect(templateTree, &QTreeWidget::itemActivated, this, &TemplateBrowser::handleItemActivated);
76       templateSearch->setFilterableView(templateTree);
77       }
78 
79 //---------------------------------------------------------
80 //   genTemplateItem
81 //---------------------------------------------------------
82 
genTemplateItem(QTreeWidgetItem * p,const QFileInfo & fi)83 TemplateItem* TemplateBrowser::genTemplateItem(QTreeWidgetItem* p, const QFileInfo& fi)
84       {
85       ScoreInfo si(fi);
86       QPixmap pm;
87       if (!QPixmapCache::find(fi.filePath(), &pm)) {
88             if (fi.completeBaseName() == "00-Blank" || fi.completeBaseName() == "Create_New_Score") {
89                   // draw a custom thumbnail for these special templates
90                   const QSize thumbnailSize = QSize(181,256); // A4 aspect ratio
91                   const qreal pixelRatio = qApp->devicePixelRatio();
92                   pm = QPixmap(thumbnailSize * pixelRatio);
93                   pm.setDevicePixelRatio(pixelRatio);
94                   pm.fill(Qt::transparent); // transparent at corners
95                   QPainter painter(&pm);
96                   painter.setRenderHint(QPainter::Antialiasing);
97                   painter.setPen(QPen(QColor(0, 0, 0, 128), 1));
98                   painter.setBrush(Qt::white);
99                   const qreal cornerRadius = 12.0;
100                   painter.drawRoundedRect(QRect(QPoint(0, 0), thumbnailSize), cornerRadius, cornerRadius);
101                   painter.end();
102 
103                   QPixmapCache::insert(fi.filePath(), pm);
104                   }
105             }
106 
107       si.setPixmap(pm);
108       TemplateItem* item = new TemplateItem(si, p);
109 
110       if (fi.completeBaseName() == "00-Blank") {
111             item->setText(0, tr("Choose Instruments"));
112             }
113       else if (fi.completeBaseName() == "Create_New_Score") {
114             item->setText(0, tr("Create New Score…"));
115             }
116       else {
117             QString s(si.completeBaseName());
118             if (!s.isEmpty() && s[0].isNumber() && _stripNumbers)
119                   s = s.mid(3);
120             s = s.replace('_', ' ');
121             item->setText(0, qApp->translate("Templates", s.toUtf8().constData()));
122             }
123       return item;
124       }
125 
126 //---------------------------------------------------------
127 //   setScores
128 //---------------------------------------------------------
129 
setScores(QFileInfoList & s)130 void TemplateBrowser::setScores(QFileInfoList& s)
131       {
132       QStringList filter = { "*.mscz", "*.mscx" };
133       templateTree->clear(); // empty the tree
134 
135       if (_showCustomCategory)
136             std::sort(s.begin(), s.end(), [](QFileInfo a, QFileInfo b)->bool { return a.fileName() < b.fileName(); });
137 
138       TemplateCategory* customCategory = new TemplateCategory(tr("Custom Templates"));
139 
140       QSet<QString> entries; //to avoid duplicates
141       for (const QFileInfo& fil : s) {
142             if (fil.isDir()) {
143                   QString st(fil.fileName());
144                   if (!st.isEmpty() && st[0].isNumber() && _stripNumbers)
145                         st = st.mid(3);
146                   st = st.replace('_', ' ');
147                   TemplateCategory* category = new TemplateCategory(qApp->translate("Templates", st.toUtf8().data()), templateTree);
148                   QDir dir(fil.filePath());
149                   unsigned childCount = 0; //nbr of entries added
150                   for (const QFileInfo& fi : dir.entryInfoList(filter, QDir::Files, QDir::Name)) {
151                         if (entries.contains(fi.filePath()))
152                               continue;
153                         genTemplateItem(category, fi);
154                         childCount++;
155                         entries.insert(fi.filePath());
156                         }
157                   if (childCount == 0) {
158                         // delete any unnecessary categories
159                         delete category;
160                         }
161                   }
162             else if (fil.isFile()) {
163                   QString st = fil.filePath();
164                   if (entries.contains(st))
165                         continue;
166                   if (st.endsWith(".mscz") || st.endsWith(".mscx")) {
167                         genTemplateItem(customCategory, fil);
168                         entries.insert(st);
169                         }
170                   }
171             }
172       if (customCategory->childCount() > 0)
173             templateTree->insertTopLevelItem(1, customCategory);
174       templateTree->toInitialState();
175       templateTree->selectFirst();
176       }
177 
178 //---------------------------------------------------------
179 //   filter
180 //      filter which scores are visible based on searchString
181 //---------------------------------------------------------
filter(const QString & searchString)182 void TemplateBrowser::filter(const QString &searchString)
183       {
184       templateTree->filter(searchString);
185       }
186 
187 //---------------------------------------------------------
188 //   scoreClicked
189 //---------------------------------------------------------
190 
scoreClicked()191 void TemplateBrowser::scoreClicked()
192       {
193       QList<QTreeWidgetItem*> selectedItems = templateTree->selectedItems();
194       if (!selectedItems.isEmpty()) {
195             QTreeWidgetItem* selectedItem = selectedItems.first();
196             if (selectedItem) {
197                   TemplateItem* selectedScore = static_cast<TemplateItem*>(selectedItem);
198                   if (_showPreview)
199                         preview->setScore(selectedScore->info());
200                   emit scoreSelected(selectedScore->info().filePath());
201                   return;
202                   }
203             }
204       if (_showPreview)
205             preview->unsetScore();
206       emit scoreSelected(""); // no score selected
207       }
208 
209 //---------------------------------------------------------
210 //   handleItemActivated
211 //---------------------------------------------------------
212 
handleItemActivated(QTreeWidgetItem * item)213 void TemplateBrowser::handleItemActivated(QTreeWidgetItem* item)
214       {
215       if (item->flags() & Qt::ItemIsSelectable)
216             emit scoreActivated(static_cast<TemplateItem*>(item)->info().filePath());
217       }
218 }
219