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