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