1
2 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
3
4 /*
5 Rosegarden
6 A MIDI and audio sequencer and musical notation editor.
7 Copyright 2000-2021 the Rosegarden development team.
8
9 Other copyrights also apply to some parts of this work. Please
10 see the AUTHORS file and individual file headers for details.
11
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License as
14 published by the Free Software Foundation; either version 2 of the
15 License, or (at your option) any later version. See the file
16 COPYING included with this distribution for more information.
17 */
18
19 #define RG_MODULE_STRING "[HeadersConfigurationPage]"
20
21 #include "HeadersConfigurationPage.h"
22
23 #include "misc/ConfigGroups.h"
24 #include "document/RosegardenDocument.h"
25 #include "document/MetadataHelper.h"
26 #include "document/io/LilyPondExporter.h"
27 #include "gui/widgets/CollapsingFrame.h"
28 #include "misc/Strings.h"
29 #include "misc/Debug.h"
30 #include "gui/widgets/LineEdit.h"
31 #include "gui/configuration/CommentsConfigurationPage.h"
32 #include "gui/dialogs/ConfigureDialogBase.h"
33
34 #include <QApplication>
35 #include <QSettings>
36 #include <QListWidget>
37 #include <QTableWidget>
38 #include <QTableWidgetItem>
39 #include <QGroupBox>
40 #include <QLabel>
41 #include <QLayout>
42 #include <QPushButton>
43 #include <QString>
44 #include <QTabWidget>
45 #include <QToolTip>
46 #include <QWidget>
47 #include <QVBoxLayout>
48 #include <QFont>
49
50
51 namespace Rosegarden
52 {
53
HeadersConfigurationPage(QWidget * parent,RosegardenDocument * doc,ConfigureDialogBase * parentDialog)54 HeadersConfigurationPage::HeadersConfigurationPage(
55 QWidget *parent, RosegardenDocument *doc,
56 ConfigureDialogBase *parentDialog) :
57 QWidget(parent),
58 m_doc(doc),
59 m_parentDialog(parentDialog)
60 {
61 QVBoxLayout *layout = new QVBoxLayout;
62
63 //
64 // LilyPond export: Printable headers
65 //
66
67 QGroupBox *headersBox = new QGroupBox(tr("Printable headers"), this);
68 QHBoxLayout *headersBoxLayout = new QHBoxLayout;
69 layout->addWidget(headersBox);
70 QFrame *frameHeaders = new QFrame(headersBox);
71 frameHeaders->setContentsMargins(10, 10, 10, 10);
72 QGridLayout *layoutHeaders = new QGridLayout(frameHeaders);
73 layoutHeaders->setSpacing(5);
74 headersBoxLayout->addWidget(frameHeaders);
75 headersBox->setLayout(headersBoxLayout);
76
77 // grab user headers from metadata
78 MetadataHelper mh(m_doc);
79 std::map<QString, QString> headers = mh.getHeaders();
80
81 std::vector<PropertyName> fixedKeys = CompositionMetadataKeys::getFixedKeys();
82
83 std::set<QString> shown;
84
85 // For each Composition metadata key
86 for (unsigned int index = 0; index < fixedKeys.size(); index++) {
87 std::string key = fixedKeys[index].getName();
88 QString value = headers[strtoqstr(key)];
89 std::string header = qstrtostr(value);
90 //@@@ dtb: tr() only works with char* now, so I'm going to try
91 // using header directly instead of a QString version of header.
92 QString headerStr = QObject::tr(header.c_str());
93
94 unsigned int row = 0, col = 0, width = 1;
95 LineEdit *editHeader = new LineEdit(headerStr, frameHeaders);
96 if (m_parentDialog) {
97 connect(editHeader, &QLineEdit::textEdited,
98 m_parentDialog, &ConfigureDialogBase::slotActivateApply);
99 }
100 QString trName;
101 if (key == headerDedication()) {
102 m_editDedication = editHeader;
103 row = 0; col = 2; width = 2;
104 trName = tr("Dedication");
105 } else if (key == headerTitle()) {
106 m_editTitle = editHeader;
107 row = 1; col = 1; width = 4;
108 trName = tr("Title");
109 } else if (key == headerSubtitle()) {
110 m_editSubtitle = editHeader;
111 row = 2; col = 1; width = 4;
112 trName = tr("Subtitle");
113 } else if (key == headerSubsubtitle()) {
114 m_editSubsubtitle = editHeader;
115 row = 3; col = 2; width = 2;
116 trName = tr("Subsubtitle");
117 } else if (key == headerPoet()) {
118 m_editPoet = editHeader;
119 row = 4; col = 0; width = 2;
120 trName = tr("Poet");
121 } else if (key == headerInstrument()) {
122 m_editInstrument = editHeader;
123 row = 4; col = 2; width = 2;
124 trName = tr("Instrument");
125 } else if (key == headerComposer()) {
126 m_editComposer = editHeader;
127 row = 4; col = 4; width = 2;
128 trName = tr("Composer");
129 } else if (key == headerMeter()) {
130 m_editMeter = editHeader;
131 row = 5; col = 0; width = 3;
132 trName = tr("Meter");
133 } else if (key == headerArranger()) {
134 m_editArranger = editHeader;
135 row = 5; col = 3; width = 3;
136 trName = tr("Arranger");
137 } else if (key == headerPiece()) {
138 m_editPiece = editHeader;
139 row = 6; col = 0; width = 3;
140 trName = tr("Piece");
141 } else if (key == headerOpus()) {
142 m_editOpus = editHeader;
143 row = 6; col = 3; width = 3;
144 trName = tr("Opus");
145 } else if (key == headerCopyright()) {
146 m_editCopyright = editHeader;
147 row = 8; col = 1; width = 4;
148 trName = tr("Copyright");
149 } else if (key == headerTagline()) {
150 m_editTagline = editHeader;
151 row = 9; col = 1; width = 4;
152 trName = tr("Tagline");
153 }
154
155 // editHeader->setReadOnly(true);
156 editHeader->setAlignment((col == 0 ? Qt::AlignLeft :
157 (col >= 3 ? Qt::AlignRight : Qt::AlignCenter)));
158
159 layoutHeaders->addWidget(editHeader, row, col, 1, width);
160
161 //
162 // ToolTips
163 //
164 editHeader->setToolTip(trName);
165
166 shown.insert(strtoqstr(key));
167 }
168
169 QLabel *separator = new QLabel(tr("The composition comes here."), frameHeaders);
170 separator->setAlignment(Qt::AlignCenter);
171 layoutHeaders->addWidget(separator, 7, 1, 1, 4 - 1+1);
172
173 frameHeaders->setLayout(layoutHeaders);
174
175
176
177 //
178 // LilyPond export: Non-printable headers
179 //
180
181 CollapsingFrame *otherHeadersBox = new CollapsingFrame
182 (tr("Additional headers"), this, "nonprintableheaders", false);
183
184 layout->addWidget(otherHeadersBox);
185 setLayout(layout);
186
187 QFrame *frameOtherHeaders = new QFrame(otherHeadersBox);
188 otherHeadersBox->setWidgetFill(true);
189 QFont font(otherHeadersBox->font());
190 font.setBold(false);
191 otherHeadersBox->setFont(font);
192 otherHeadersBox->setWidget(frameOtherHeaders);
193
194 frameOtherHeaders->setContentsMargins(10, 10, 10, 10);
195 QGridLayout *layoutOtherHeaders = new QGridLayout(frameOtherHeaders);
196 layoutOtherHeaders->setSpacing(5);
197
198 m_metadata = new QTableWidget( 2, 2, frameOtherHeaders ); // rows, columns
199 if (m_parentDialog) {
200 connect(m_metadata, &QTableWidget::cellChanged,
201 m_parentDialog, &ConfigureDialogBase::slotActivateApply);
202 }
203 m_metadata->setObjectName("StyledTable");
204 m_metadata->setAlternatingRowColors(true);
205
206 m_metadata->setHorizontalHeaderItem( 0, new QTableWidgetItem(tr("Name")) ); // column, item
207 m_metadata->setHorizontalHeaderItem( 1, new QTableWidgetItem(tr("Value")) ); // column, item
208 m_metadata->setMinimumSize(40, 40); // width, height
209
210
211 QTableWidgetItem* tabItem;
212 int row = 0;
213
214 for (std::map<QString, QString>::iterator it = headers.begin();
215 it != headers.end(); ++it) {
216
217 if (shown.find(it->first) != shown.end()) continue;
218
219 m_metadata->setRowCount(row + 1);
220
221 QString name(it->first);
222
223 // property names stored in lower case
224 name = name.left(1).toUpper() + name.right(name.length() - 1);
225
226 tabItem = new QTableWidgetItem(name);
227 m_metadata->setItem(row, 0, tabItem);
228 tabItem = new QTableWidgetItem(it->second);
229 m_metadata->setItem(row, 1, tabItem);
230
231 row++;
232 }
233
234 layoutOtherHeaders->addWidget(m_metadata, 0, 0, 1, 2);
235
236 QPushButton* addPropButton = new QPushButton(tr("Add New Property"),
237 frameOtherHeaders);
238 layoutOtherHeaders->addWidget(addPropButton, 1, 0, Qt::AlignHCenter);
239
240 QPushButton* deletePropButton = new QPushButton(tr("Delete Property"),
241 frameOtherHeaders);
242 layoutOtherHeaders->addWidget(deletePropButton, 1, 1, Qt::AlignHCenter);
243
244 frameOtherHeaders->setLayout(layoutOtherHeaders);
245
246 connect(addPropButton, &QAbstractButton::clicked,
247 this, &HeadersConfigurationPage::slotAddNewProperty);
248
249 connect(deletePropButton, &QAbstractButton::clicked,
250 this, &HeadersConfigurationPage::slotDeleteProperty);
251
252 if (m_parentDialog) {
253 m_parentDialog->deactivateApply();
254 }
255 }
256
257 void
slotAddNewProperty()258 HeadersConfigurationPage::slotAddNewProperty()
259 {
260 QString propertyName;
261 int i = 0;
262
263 while (1) {
264 propertyName =
265 (i > 0 ? tr("{new property %1}").arg(i) : tr("{new property}"));
266 QList<QTableWidgetItem*> foundItems = m_metadata->findItems(
267 propertyName, Qt::MatchContains | Qt::MatchCaseSensitive);
268
269 if (!m_doc->getComposition().getMetadata().has(qstrtostr(propertyName)) &&
270 foundItems.isEmpty()){
271 break;
272 }
273 ++i;
274 }
275
276 int rc = m_metadata->rowCount();
277 m_metadata->setRowCount(rc + 1);
278 m_metadata->setItem(rc, 0, new QTableWidgetItem(propertyName));
279 m_metadata->setItem(rc, 1, new QTableWidgetItem()); // empty value
280
281 if (m_parentDialog) {
282 m_parentDialog->slotActivateApply();
283 }
284 }
285
286 void
slotDeleteProperty()287 HeadersConfigurationPage::slotDeleteProperty()
288 {
289 m_metadata->removeRow(m_metadata->currentRow());
290
291 if (m_parentDialog) {
292 m_parentDialog->slotActivateApply();
293 }
294 }
295
apply()296 void HeadersConfigurationPage::apply()
297 {
298 // If one of the items still has focus, it won't remember edits.
299 // Switch between two fields in order to lose the current focus.
300 m_editTitle->setFocus();
301 m_metadata->setFocus();
302
303 //
304 // Update header fields
305 //
306 std::map<QString, QString> headers;
307 headers.clear();
308
309 // Remember the current fixed keys metadata
310 headers[strtoqstr(CompositionMetadataKeys::Dedication)] = m_editDedication->text();
311 headers[strtoqstr(CompositionMetadataKeys::Title)] = m_editTitle->text();
312 headers[strtoqstr(CompositionMetadataKeys::Subtitle)] = m_editSubtitle->text();
313 headers[strtoqstr(CompositionMetadataKeys::Subsubtitle)] = m_editSubsubtitle->text();
314 headers[strtoqstr(CompositionMetadataKeys::Poet)] = m_editPoet->text();
315 headers[strtoqstr(CompositionMetadataKeys::Composer)] = m_editComposer->text();
316 headers[strtoqstr(CompositionMetadataKeys::Meter)] = m_editMeter->text();
317 headers[strtoqstr(CompositionMetadataKeys::Opus)] = m_editOpus->text();
318 headers[strtoqstr(CompositionMetadataKeys::Arranger)] = m_editArranger->text();
319 headers[strtoqstr(CompositionMetadataKeys::Instrument)] = m_editInstrument->text();
320 headers[strtoqstr(CompositionMetadataKeys::Piece)] = m_editPiece->text();
321 headers[strtoqstr(CompositionMetadataKeys::Copyright)] = m_editCopyright->text();
322 headers[strtoqstr(CompositionMetadataKeys::Tagline)] = m_editTagline->text();
323
324 // Remember the other metadata
325 for (int r=0; r < m_metadata->rowCount(); r++) {
326 QTableWidgetItem* tabItem = m_metadata->item(r, 0);
327 QTableWidgetItem* tabItem2 = m_metadata->item(r, 1);
328
329 if ((!tabItem) || (!tabItem2)) {
330 RG_DEBUG << "ERROR: Any TableWidgetItem is nullptr in HeadersConfigurationPage::apply() ";
331 continue;
332 }
333
334 headers[tabItem->text().toLower()] = tabItem2->text();
335 }
336
337 // Export headers to metadata
338 MetadataHelper mh(m_doc);
339 mh.setHeaders(headers);
340 }
341
342 }
343