1 //=============================================================================
2 //  MuseScore
3 //  Linux Music Score Editor
4 //
5 //  Copyright (C) 2020 MuseScore BVBA and others
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 //
10 //  This program is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU General Public License for more details.
14 //
15 //  You should have received a copy of the GNU General Public License
16 //  along with this program; if not, write to the Free Software
17 //  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 //=============================================================================
19 
20 #include "exportdialog.h"
21 #include "musescore.h"
22 #include "preferences.h"
23 
24 // Supported export formats
25 // ------------------------
26 //  - PDF
27 //  - PNG Bitmap Graphic (*.png)
28 //  - Scalable Vector Graphics (*.svg)
29 //  - MP3 Audio (*.mp3)
30 //  - Wave Audio (*.wav)
31 //  - FLAC Audio (*.flac)
32 //  - Ogg Vorbis Audio (*.ogg)
33 //  - MIDI
34 //  - MusicXML:
35 //      - Compressed (*.mxl)
36 //      - Uncompressed (*.musicxml)
37 //      - Uncompressed (outdated) (*.xml)
38 //  - Uncompressed MuseScore File (*.mscx)
39 
40 namespace Ms {
41 
42 //---------------------------------------------------------
43 //   ExportScoreItem
44 //---------------------------------------------------------
45 
ExportScoreItem(Score * s,QListWidget * parent)46 ExportScoreItem::ExportScoreItem(Score* s, QListWidget* parent)
47    : QListWidgetItem(parent)
48       {
49       _score = s;
50       setText(s->isMaster() ? ExportDialog::tr("Full Score") : s->title());
51       }
52 
53 //---------------------------------------------------------
54 //   ExportDialog
55 //---------------------------------------------------------
56 
ExportDialog(Score * s,QWidget * parent)57 ExportDialog::ExportDialog(Score* s, QWidget* parent)
58    : AbstractDialog(parent), cs(s)
59       {
60       setObjectName("ExportDialog");
61       setupUi(this);
62       setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
63 
64       connect(listWidget, &QListWidget::itemChanged, this, &ExportDialog::setOkButtonEnabled);
65       connect(fileTypeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(fileTypeChosen(int)));
66 
67       pdfSeparateOrSingleFiles = new QButtonGroup(this);
68       pdfSeparateOrSingleFiles->addButton(pdfSeparateFilesRadioButton, 0);
69       pdfSeparateOrSingleFiles->addButton(pdfOneFileRadioButton, 1);
70 
71 #if !defined(HAS_AUDIOFILE) || !defined(USE_LAME)
72       // Disable audio options that are unavailable
73       // Source: https://stackoverflow.com/a/38915478
74       QStandardItemModel* fileTypeComboBoxModel = qobject_cast<QStandardItemModel*>(fileTypeComboBox->model());
75       Q_ASSERT(fileTypeComboBoxModel != nullptr);
76 # ifndef USE_LAME
77       // Disable .mp3 option if unavailable
78       QStandardItem* mp3Item = fileTypeComboBoxModel->item(3);
79       mp3Item->setFlags(audioItem->flags() & ~Qt::ItemIsEnabled);
80 # endif
81 # ifndef HAS_AUDIOFILE
82       // Disable .wav, .flac and .ogg options if unavailable
83       for (int i = 4; i < 7; i++) {
84             QStandardItem* audioItem = fileTypeComboBoxModel->item(i);
85             audioItem->setFlags(audioItem->flags() & ~Qt::ItemIsEnabled);
86             }
87 # endif
88 #endif
89 
90       fileTypeComboBox->setCurrentIndex(0);
91       pageStack->setCurrentIndex(0);
92 
93       pdfSeparateFilesRadioButton->setChecked(true);
94 
95       audioSampleRate->clear();
96       audioSampleRate->addItem(tr("32000"), 32000);
97       audioSampleRate->addItem(tr("44100"), 44100); // default
98       audioSampleRate->addItem(tr("48000"), 48000);
99 
100       mp3BitRate->clear();
101       mp3BitRate->addItem( tr("32"),  32);
102       mp3BitRate->addItem( tr("40"),  40);
103       mp3BitRate->addItem( tr("48"),  48);
104       mp3BitRate->addItem( tr("56"),  56);
105       mp3BitRate->addItem( tr("64"),  64);
106       mp3BitRate->addItem( tr("80"),  80);
107       mp3BitRate->addItem( tr("96"),  96);
108       mp3BitRate->addItem(tr("112"), 112);
109       mp3BitRate->addItem(tr("128"), 128); // default
110       mp3BitRate->addItem(tr("160"), 160);
111       mp3BitRate->addItem(tr("192"), 192);
112       mp3BitRate->addItem(tr("224"), 224);
113       mp3BitRate->addItem(tr("256"), 256);
114       mp3BitRate->addItem(tr("320"), 320);
115 
116       retranslate();
117       MuseScore::restoreGeometry(this);
118       }
119 
120 //---------------------------------------------------------
121 //   retranslate
122 //---------------------------------------------------------
123 
retranslate()124 void ExportDialog::retranslate()
125       {
126       retranslateUi(this);
127 
128       if (listWidget->item(0))
129             listWidget->item(0)->setText(tr("Full Score"));
130 
131       fileTypeComboBox->setItemText(0, tr("PDF File"));
132       fileTypeComboBox->setItemText(1, tr("PNG Images"));
133       fileTypeComboBox->setItemText(2, tr("SVG Images"));
134       fileTypeComboBox->setItemText(3, tr("MP3 Audio"));
135       fileTypeComboBox->setItemText(4, tr("WAV Audio"));
136       fileTypeComboBox->setItemText(5, tr("FLAC Audio"));
137       fileTypeComboBox->setItemText(6, tr("OGG Audio"));
138       fileTypeComboBox->setItemText(7, tr("MIDI"));
139       fileTypeComboBox->setItemText(8, tr("MusicXML"));
140       fileTypeComboBox->setItemText(9, tr("Uncompressed MuseScore File") + " (*.mscx)");
141 
142       musicxmlFileTypeComboBox->setItemText(0, tr("Compressed") + " (*.mxl)");
143       musicxmlFileTypeComboBox->setItemText(1, tr("Uncompressed") + " (*.musicxml)");
144       musicxmlFileTypeComboBox->setItemText(2, tr("Uncompressed (outdated)") + " (*.xml)");
145 
146       buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Export…"));
147       }
148 
149 //---------------------------------------------------------
150 //   loadValues
151 ///   Load the preferences values into the UI
152 //---------------------------------------------------------
153 
loadValues()154 void ExportDialog::loadValues()
155       {
156       pdfDpiSpinbox->setValue(preferences.getInt(PREF_EXPORT_PDF_DPI));
157 
158       pngDpiSpinbox->setValue(preferences.getDouble(PREF_EXPORT_PNG_RESOLUTION));
159       pngTransparentBackgroundCheckBox->setChecked(preferences.getBool(PREF_EXPORT_PNG_USETRANSPARENCY));
160 
161       audioNormaliseCheckBox->setChecked(preferences.getBool(PREF_EXPORT_AUDIO_NORMALIZE));
162       int audioSampleRateIndex = audioSampleRate->findData(preferences.getInt(PREF_EXPORT_AUDIO_SAMPLERATE));
163       if (audioSampleRateIndex == -1)
164             audioSampleRateIndex = audioSampleRate->findData(preferences.defaultValue(PREF_EXPORT_AUDIO_SAMPLERATE));
165       audioSampleRate->setCurrentIndex(audioSampleRateIndex);
166       int mp3BitRateIndex = mp3BitRate->findData(preferences.getInt(PREF_EXPORT_MP3_BITRATE));
167       if (mp3BitRateIndex == -1)
168             mp3BitRateIndex = mp3BitRate->findData(preferences.defaultValue(PREF_EXPORT_MP3_BITRATE));
169       mp3BitRate->setCurrentIndex(mp3BitRateIndex);
170 
171       midiExpandRepeats->setChecked(preferences.getBool(PREF_IO_MIDI_EXPANDREPEATS));
172       midiExportRPNs->setChecked(preferences.getBool(PREF_IO_MIDI_EXPORTRPNS));
173 
174       if (preferences.getBool(PREF_EXPORT_MUSICXML_EXPORTLAYOUT)) {
175             musicxmlExportAllLayout->setChecked(true);
176       } else {
177             switch (preferences.musicxmlExportBreaks()) {
178                   case MusicxmlExportBreaks::ALL:
179                         musicxmlExportAllBreaks->setChecked(true);
180                         break;
181                   case MusicxmlExportBreaks::MANUAL:
182                         musicxmlExportManualBreaks->setChecked(true);
183                         break;
184                   case MusicxmlExportBreaks::NO:
185                         musicxmlExportNoBreaks->setChecked(true);
186                         break;
187                   }
188             }
189       }
190 
191 //---------------------------------------------------------
192 //   loadScoreAndPartsList
193 //---------------------------------------------------------
194 
loadScoreAndPartsList()195 void ExportDialog::loadScoreAndPartsList()
196       {
197       listWidget->clear();
198 
199       ExportScoreItem* scoreItem = new ExportScoreItem(cs->masterScore()->score());
200       listWidget->addItem(scoreItem);
201 
202       for (Excerpt* e : cs->masterScore()->excerpts()) {
203             Score* s = e->partScore();
204             ExportScoreItem* item = new ExportScoreItem(s);
205             item->setChecked(s == cs);
206             listWidget->addItem(item);
207             }
208       }
209 
210 //---------------------------------------------------------
211 //   selection
212 //    Change the selection in the list of scores/parts.
213 //---------------------------------------------------------
214 
215 // Select all parts and the score
selectAll()216 void ExportDialog::selectAll()
217       {
218       for (int i = 0; i < listWidget->count(); i++) {
219             ExportScoreItem* item = static_cast<ExportScoreItem*>(listWidget->item(i));
220             item->setChecked(true);
221             }
222       }
223 
224 // Select the currently opened score/part
selectCurrent()225 void ExportDialog::selectCurrent()
226       {
227       for (int i = 0; i < listWidget->count(); i++) {
228             ExportScoreItem* item = static_cast<ExportScoreItem*>(listWidget->item(i));
229             item->setChecked(item->score() == cs);
230             }
231       }
232 
233 // Select the score
selectScore()234 void ExportDialog::selectScore()
235       {
236       for (int i = 0; i < listWidget->count(); i++) {
237             ExportScoreItem* item = static_cast<ExportScoreItem*>(listWidget->item(i));
238             item->setChecked(item->score()->isMaster());
239             }
240       }
241 
242 // Select the parts
selectParts()243 void ExportDialog::selectParts()
244       {
245       for (int i = 0; i < listWidget->count(); i++) {
246             ExportScoreItem* item = static_cast<ExportScoreItem*>(listWidget->item(i));
247             item->setChecked(!item->score()->isMaster());
248             }
249       }
250 
251 // Select nothing
clearSelection()252 void ExportDialog::clearSelection()
253       {
254       for (int i = 0; i < listWidget->count(); i++) {
255             ExportScoreItem* item = static_cast<ExportScoreItem*>(listWidget->item(i));
256             item->setChecked(false);
257             }
258       }
259 
260 //---------------------------------------------------------
261 //   setOkButtonEnabled
262 //---------------------------------------------------------
263 
setOkButtonEnabled()264 void ExportDialog::setOkButtonEnabled()
265       {
266       bool isAnythingSelected = false;
267       for (int i = 0; i < listWidget->count(); i++) {
268             ExportScoreItem* item = static_cast<ExportScoreItem*>(listWidget->item(i));
269             if (item->isChecked()) {
270                   isAnythingSelected = true;
271                   break;
272                   }
273             }
274       buttonBox->button(QDialogButtonBox::Ok)->setEnabled(isAnythingSelected);
275       }
276 
277 //---------------------------------------------------------
278 //   fileTypeChosen
279 //---------------------------------------------------------
280 
fileTypeChosen(int index)281 void ExportDialog::fileTypeChosen(int index)
282       {
283       if (index <= 2) // Pdf, png and svg
284             pageStack->setCurrentIndex(index);
285       else if (index <= 6) { // Audio formats share their page (because they share many settings)
286             pageStack->setCurrentWidget(audioPage);
287             mp3BitRateLabel->setVisible(index == 3);
288             mp3BitRate->setVisible(index == 3);
289             mp3kBitSLabel->setVisible(index == 3);
290             }
291       else // And others have their own page again
292             pageStack->setCurrentIndex(index - 3);
293       }
294 
295 //---------------------------------------------------------
296 //   setType
297 //---------------------------------------------------------
298 
setType(const QString & type)299 void ExportDialog::setType(const QString& type)
300       {
301       if (type == "pdf")
302             fileTypeComboBox->setCurrentIndex(0);
303       else if (type == "png")
304             fileTypeComboBox->setCurrentIndex(1);
305       else if (type == "svg")
306             fileTypeComboBox->setCurrentIndex(2);
307 #ifdef USE_LAME
308       else if (type == "mp3")
309             fileTypeComboBox->setCurrentIndex(3);
310 #endif
311 #ifdef HAS_AUDIOFILE
312       else if (type == "wav")
313             fileTypeComboBox->setCurrentIndex(4);
314       else if (type == "flac")
315             fileTypeComboBox->setCurrentIndex(5);
316       else if (type == "ogg")
317             fileTypeComboBox->setCurrentIndex(6);
318 #endif
319       else if (type == "midi")
320             fileTypeComboBox->setCurrentIndex(7);
321       else if (type == "musicxml")
322             fileTypeComboBox->setCurrentIndex(8);
323       else if (type == "uncompressed")
324             fileTypeComboBox->setCurrentIndex(9);
325       else // Default
326             return setType("pdf");
327       }
328 
329 //---------------------------------------------------------
330 //   showExportDialog
331 //---------------------------------------------------------
332 
showExportDialog(const QString & type)333 void MuseScore::showExportDialog(const QString& type)
334       {
335       if (!exportDialog)
336             exportDialog = new ExportDialog(cs, this);
337       else
338             exportDialog->setScore(cs);
339 
340       if (type.size() > 0)
341             exportDialog->setType(type);
342       exportDialog->exec();
343       }
344 
345 //---------------------------------------------------------
346 //   accept
347 //---------------------------------------------------------
348 
accept()349 void ExportDialog::accept()
350       {
351       // Collect which scores to export
352       QList<Score*> scores;
353       bool containsMasterScore = false;
354       for (int i = 0; i < listWidget->count(); i++) {
355             ExportScoreItem* item = static_cast<ExportScoreItem*>(listWidget->item(i));
356             if (item->isChecked()) {
357                   scores.append(item->score());
358                   if (item->score()->isMaster())
359                         containsMasterScore = true;
360                   }
361             }
362       if (scores.isEmpty())
363             return; // Should never happen
364 
365       // Ask for save name and location
366       QString saveDirectory;
367       if (cs->masterScore()->fileInfo()->exists())
368             saveDirectory = cs->masterScore()->fileInfo()->dir().path();
369       else {
370             QSettings set;
371             if (mscore->lastSaveCopyDirectory.isEmpty())
372                   mscore->lastSaveCopyDirectory = set.value("lastSaveCopyDirectory", preferences.getString(PREF_APP_PATHS_MYSCORES)).toString();
373             saveDirectory = mscore->lastSaveCopyDirectory;
374             }
375       if (saveDirectory.isEmpty())
376             saveDirectory = preferences.getString(PREF_APP_PATHS_MYSCORES);
377 
378       QString saveFormat;
379       int currentIndex = fileTypeComboBox->currentIndex();
380       if (currentIndex == 0) {
381             saveFormat = "pdf";
382             if (pdfDpiSpinbox->value() != preferences.getInt(PREF_EXPORT_PDF_DPI))
383                   preferences.setPreference(PREF_EXPORT_PDF_DPI, pdfDpiSpinbox->value());
384       } else if (currentIndex == 1) {
385             saveFormat = "png";
386             if (pngDpiSpinbox->value() != preferences.getDouble(PREF_EXPORT_PNG_RESOLUTION))
387                   preferences.setPreference(PREF_EXPORT_PNG_RESOLUTION, pngDpiSpinbox->value());
388             if (pngTransparentBackgroundCheckBox->isChecked() != preferences.getBool(PREF_EXPORT_PNG_USETRANSPARENCY))
389                   preferences.setPreference(PREF_EXPORT_PNG_USETRANSPARENCY, pngTransparentBackgroundCheckBox->isChecked());
390       } else if (currentIndex == 2) {
391             saveFormat = "svg";
392       } else if (currentIndex <= 6) { // The audio formats share some settings
393             if (currentIndex == 3) {
394                   saveFormat = "mp3";
395                   if (mp3BitRate->currentData() != preferences.getInt(PREF_EXPORT_MP3_BITRATE))
396                         preferences.setPreference(PREF_EXPORT_MP3_BITRATE, mp3BitRate->currentData());
397             } else if (currentIndex == 4) {
398                   saveFormat = "wav";
399             } else if (currentIndex == 5) {
400                   saveFormat = "flac";
401             } else if (currentIndex == 6) {
402                   saveFormat = "ogg";
403             }
404             if (audioSampleRate->currentData() != preferences.getInt(PREF_EXPORT_AUDIO_SAMPLERATE))
405                   preferences.setPreference(PREF_EXPORT_AUDIO_SAMPLERATE, audioSampleRate->currentData());
406             if (audioNormaliseCheckBox->isChecked() != preferences.getBool(PREF_EXPORT_AUDIO_NORMALIZE))
407                   preferences.setPreference(PREF_EXPORT_AUDIO_NORMALIZE, audioNormaliseCheckBox->isChecked());
408       } else if (currentIndex == 7) {
409             saveFormat = "mid";
410             if (midiExpandRepeats->isChecked() != preferences.getBool(PREF_IO_MIDI_EXPANDREPEATS))
411                   preferences.setPreference(PREF_IO_MIDI_EXPANDREPEATS, midiExpandRepeats->isChecked());
412             if (midiExportRPNs->isChecked() != preferences.getBool(PREF_IO_MIDI_EXPORTRPNS))
413                   preferences.setPreference(PREF_IO_MIDI_EXPORTRPNS, midiExportRPNs->isChecked());
414       } else if (currentIndex == 8) {
415             if (musicxmlFileTypeComboBox->currentIndex() == 1)
416                   saveFormat = "musicxml";
417             else if (musicxmlFileTypeComboBox->currentIndex() == 2)
418                   saveFormat = "xml";
419             else
420                   saveFormat = "mxl";
421             if (musicxmlExportAllLayout->isChecked() && !preferences.getBool(PREF_EXPORT_MUSICXML_EXPORTLAYOUT))
422                   preferences.setPreference(PREF_EXPORT_MUSICXML_EXPORTLAYOUT, true);
423             else if (musicxmlExportAllBreaks->isChecked() && preferences.musicxmlExportBreaks() != MusicxmlExportBreaks::ALL)
424                   preferences.setCustomPreference<MusicxmlExportBreaks>(PREF_EXPORT_MUSICXML_EXPORTBREAKS, MusicxmlExportBreaks::ALL);
425             else if (musicxmlExportManualBreaks->isChecked() && preferences.musicxmlExportBreaks() != MusicxmlExportBreaks::MANUAL)
426                   preferences.setCustomPreference<MusicxmlExportBreaks>(PREF_EXPORT_MUSICXML_EXPORTBREAKS, MusicxmlExportBreaks::MANUAL);
427             else if (musicxmlExportNoBreaks->isChecked() && preferences.musicxmlExportBreaks() != MusicxmlExportBreaks::NO)
428                   preferences.setCustomPreference<MusicxmlExportBreaks>(PREF_EXPORT_MUSICXML_EXPORTBREAKS, MusicxmlExportBreaks::NO);
429       } else if (currentIndex == 9)
430             saveFormat = "mscx";
431 
432       // If only one file will be created during export, the filename will be exactly
433       // as the user types in the SaveDialog, so therefore, we can have the SaveDialog
434       // responsible for asking whether the user wants to replace any existing files.
435       // Otherwise, we will ask that per file, here below.
436       bool oneScore = scores.size() == 1;
437       bool singlePDF = saveFormat == "pdf" && pdfOneFileRadioButton->isChecked();
438       bool oneFile = (oneScore && saveFormat != "png" && saveFormat != "svg") || singlePDF;
439 
440       QString filter;
441       if (saveFormat == "mid")
442             filter = "*.mid;*.midi";
443       else
444             filter = QString("*.%1").arg(saveFormat);
445 
446       QString name = QString("%1/%2").arg(saveDirectory, cs->masterScore()->fileInfo()->completeBaseName());
447       if (oneScore) {
448             Score* score = scores.first();
449             name.append(QString("%1").arg(score->isMaster() ? "" : "-" + mscore->saveFilename(score->title())));
450             }
451       else if (singlePDF)
452             name.append(QString("-%1").arg(containsMasterScore ? tr("Score_and_Parts") : tr("Parts")));
453 
454 #ifdef Q_OS_WIN
455       if (QOperatingSystemVersion::current() > QOperatingSystemVersion(QOperatingSystemVersion::Windows, 5, 1))    // XP
456 #endif
457             name.append(QString(".%1").arg(saveFormat));
458 
459       QString filename = mscore->getSaveScoreName(tr("Export"), name, filter, /*selectFolder*/ false, /*askOverwrite*/ oneFile);
460       if (filename.isEmpty())
461             return; // User cancels; keep Export dialog open
462 
463       QFileInfo fileinfo(filename);
464       mscore->lastSaveCopyDirectory = fileinfo.absolutePath();
465       mscore->lastSaveCopyFormat = fileinfo.suffix();
466 
467       QString suffix = fileinfo.suffix();
468       if (suffix.isEmpty()) {
469             QMessageBox::critical(this, tr("Export"), tr("Cannot determine file type."));
470             return; // Error occurred; keep Export dialog open
471             }
472 
473       // At this moment, close the dialog
474       QDialog::accept();
475 
476       if (singlePDF)
477             // Export the selected scores as one pdf file, directly with the filename the user typed
478             mscore->savePdf(scores, filename);
479       else if (oneScore)
480             // Export the selected score, directly with the filename the user typed
481             mscore->saveAs(scores.first(), true, filename, suffix);
482       else {
483             // Export the selected scores as separate files, appending the part names to the filename
484             SaveReplacePolicy replacePolicy = SaveReplacePolicy::NO_CHOICE;
485 
486             for (Score* score : scores) {
487                   QString definitiveFilename = QString("%1/%2%3.%4")
488                         .arg(fileinfo.absolutePath(),
489                              fileinfo.completeBaseName(),
490                              score->isMaster() ? "" : "-" + mscore->saveFilename(score->title()),
491                              suffix);
492                   if (saveFormat != "png" && saveFormat != "svg" && QFileInfo(definitiveFilename).exists()) {
493                         // Png and Svg export functions change the filename, so they
494                         // are responsible for asking the user about overwriting.
495                         switch (replacePolicy) {
496                               case SaveReplacePolicy::NO_CHOICE:
497                                     {
498                                     int responseCode = mscore->askOverwriteAll(definitiveFilename);
499                                     if (responseCode == QMessageBox::YesToAll) {
500                                           replacePolicy = SaveReplacePolicy::REPLACE_ALL;
501                                           break; // Break out of the switch; go on and replace the existing file
502                                           }
503                                     else if (responseCode == QMessageBox::NoToAll) {
504                                           replacePolicy = SaveReplacePolicy::SKIP_ALL;
505                                           continue; // Continue in the `for` loop
506                                           }
507                                     else if (responseCode == QMessageBox::No)
508                                           continue;
509                                     break;
510                                     }
511                               case SaveReplacePolicy::SKIP_ALL:
512                                     continue;
513                               case SaveReplacePolicy::REPLACE_ALL:
514                                     break;
515                               }
516                         }
517                   mscore->saveAs(score, true, definitiveFilename, suffix, &replacePolicy);
518                   }
519             }
520       }
521 
522 //---------------------------------------------------------
523 //   showEvent
524 //---------------------------------------------------------
525 
showEvent(QShowEvent * event)526 void ExportDialog::showEvent(QShowEvent* event)
527       {
528       loadValues();
529       loadScoreAndPartsList();
530       selectScore();
531 
532       QDialog::showEvent(event);
533       }
534 
535 //---------------------------------------------------------
536 //   hideEvent
537 //---------------------------------------------------------
538 
hideEvent(QHideEvent * event)539 void ExportDialog::hideEvent(QHideEvent* event)
540       {
541       MuseScore::saveGeometry(this);
542       QDialog::hideEvent(event);
543       }
544 }
545