1 //=============================================================================
2 //  MuseScore
3 //  Linux Music Score Editor
4 //
5 //  Copyright (C) 2011 Werner Schweer 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 "workspace.h"
21 #include "musescore.h"
22 #include "libmscore/score.h"
23 #include "libmscore/imageStore.h"
24 #include "libmscore/xml.h"
25 #include "thirdparty/qzip/qzipreader_p.h"
26 #include "thirdparty/qzip/qzipwriter_p.h"
27 #include "preferences.h"
28 #include "palette.h"
29 #include "palette/paletteworkspace.h"
30 #include "extension.h"
31 
32 #if defined(FOR_WINSTORE)  // or even just Q_OS_WIN ?
33 extern Q_CORE_EXPORT int qt_ntfs_permission_lookup;
34 #else
35 int qt_ntfs_permission_lookup;
36 #endif
37 
38 namespace Ms {
39 
40 bool WorkspacesManager::isWorkspacesListDirty = true;
41 Workspace* WorkspacesManager::m_currentWorkspace = nullptr;
42 QList<Workspace*> WorkspacesManager::m_workspaces {};
43 QList<Workspace*> WorkspacesManager::m_visibleWorkspaces {};
44 
45 QList<QPair<QAction*, QString>> Workspace::actionToStringList {};
46 QList<QPair<QMenu*  , QString>> Workspace::menuToStringList   {};
47 
48 std::vector<QString> WorkspacesManager::defaultWorkspaces {
49       QT_TRANSLATE_NOOP("Ms::Workspace", "Basic"),
50       QT_TRANSLATE_NOOP("Ms::Workspace", "Advanced"),
51       };
52 
53 std::vector<QString> WorkspacesManager::defaultEditedWorkspaces {
54       QT_TRANSLATE_NOOP("Ms::Workspace", "Basic edited"),
55       QT_TRANSLATE_NOOP("Ms::Workspace", "Advanced edited"),
56       };
57 
58 //---------------------------------------------------------
59 //   editedWorkspaceName
60 //---------------------------------------------------------
61 
editedWorkspaceTranslatableName(const QString & oldWorkspaceTranslatableName)62 static QString editedWorkspaceTranslatableName(const QString& oldWorkspaceTranslatableName)
63       {
64       if (oldWorkspaceTranslatableName.isEmpty())
65             return QString();
66 
67       const auto it = std::find(WorkspacesManager::defaultWorkspaces.begin(), WorkspacesManager::defaultWorkspaces.end(), oldWorkspaceTranslatableName);
68 
69       if (it != WorkspacesManager::defaultWorkspaces.end()) {
70             const int idx = it - WorkspacesManager::defaultWorkspaces.begin();
71             if (idx < int(WorkspacesManager::defaultEditedWorkspaces.size()))
72                   return WorkspacesManager::defaultEditedWorkspaces[idx];
73             }
74 
75       return QString();
76       }
77 
78 //---------------------------------------------------------
79 //   editedWorkspaceName
80 //---------------------------------------------------------
81 
defaultWorkspaceTranslatableName(const QString & editedWorkspaceName)82 QString WorkspacesManager::defaultWorkspaceTranslatableName(const QString& editedWorkspaceName)
83       {
84       const auto it = std::find(WorkspacesManager::defaultEditedWorkspaces.begin(), WorkspacesManager::defaultEditedWorkspaces.end(), editedWorkspaceName);
85 
86       if (it != WorkspacesManager::defaultEditedWorkspaces.end()) {
87             const int idx = it - WorkspacesManager::defaultEditedWorkspaces.begin();
88             if (idx < int(WorkspacesManager::defaultWorkspaces.size()))
89                   return WorkspacesManager::defaultWorkspaces[idx];
90             }
91 
92       return QString();
93       }
94 
95 /**
96  *   Reverts current workspace to the state of the `source` workspace
97  */
resetWorkspace()98 void MuseScore::resetWorkspace()
99       {
100       //if currentWorkspace is the `edited` Basic or Advanced one, remove the edited and show the source one
101       if (WorkspacesManager::isDefaultEditedWorkspace(WorkspacesManager::currentWorkspace())) {
102             const QString& currWorkspaceName = WorkspacesManager::currentWorkspace()->translatableName();
103             const QString& defaultWorkspaceName = WorkspacesManager::defaultWorkspaceTranslatableName(currWorkspaceName);
104             Q_ASSERT(!defaultWorkspaceName.isEmpty());
105             Workspace* defaultWorkspace = WorkspacesManager::findByTranslatableName(defaultWorkspaceName);
106             Workspace* currWorkspace = WorkspacesManager::currentWorkspace();
107             changeWorkspace(defaultWorkspace);
108             WorkspacesManager::remove(currWorkspace);
109             }
110       //else if currentWorkspace is a custom workspace, reset all palettes, toolbars, menus and GUI to the values defined in the source workspace
111       else
112             WorkspacesManager::currentWorkspace()->reset();
113       }
114 
115 //---------------------------------------------------------
116 //   showWorkspaceMenu
117 //---------------------------------------------------------
118 
showWorkspaceMenu()119 void MuseScore::showWorkspaceMenu()
120       {
121       if (workspaces == 0) {
122             workspaces = new QActionGroup(this);
123             workspaces->setExclusive(true);
124             connect(workspaces, SIGNAL(triggered(QAction*)), SLOT(changeWorkspace(QAction*)));
125             }
126       else {
127             for (QAction* a : workspaces->actions())
128                   workspaces->removeAction(a);
129             }
130       menuWorkspaces->clear();
131 
132       for (Workspace* p : WorkspacesManager::visibleWorkspaces()) {
133             QAction* a = workspaces->addAction(qApp->translate("Ms::Workspace", p->name().toUtf8()));
134             a->setCheckable(true);
135             a->setData(p->path());
136             a->setChecked(p->name() == preferences.getString(PREF_APP_WORKSPACE));
137             menuWorkspaces->addAction(a);
138             }
139 
140       menuWorkspaces->addSeparator();
141       QAction* a = new QAction(tr("New…"), this);
142       connect(a, SIGNAL(triggered()), SLOT(createNewWorkspace()));
143       menuWorkspaces->addAction(a);
144 
145       a = new QAction(tr("Edit"), this);
146       a->setDisabled(WorkspacesManager::currentWorkspace()->readOnly());
147       connect(a, SIGNAL(triggered()), SLOT(editWorkspace()));
148       menuWorkspaces->addAction(a);
149 
150       a = new QAction(tr("Delete"), this);
151       a->setDisabled(WorkspacesManager::currentWorkspace()->readOnly());
152       connect(a, SIGNAL(triggered()), SLOT(deleteWorkspace()));
153       menuWorkspaces->addAction(a);
154 
155       a = new QAction(tr("Reset workspace"), this);
156       connect(a, SIGNAL(triggered()), SLOT(resetWorkspace()));
157       menuWorkspaces->addAction(a);
158       }
159 
160 //---------------------------------------------------------
161 //   deleteWorkspace
162 //---------------------------------------------------------
163 
deleteWorkspace()164 void MuseScore::deleteWorkspace()
165       {
166       Workspace* workspace = WorkspacesManager::currentWorkspace();
167       if (!workspace)
168             return;
169 
170       QMessageBox::StandardButton reply =
171          (MScore::noGui && MScore::testMode)
172          ? QMessageBox::Yes
173          : QMessageBox::question(0,
174                  QWidget::tr("Are you sure?"),
175                  QWidget::tr("Do you really want to delete the '%1' workspace?").arg(workspace->name()),
176                  QMessageBox::Yes | QMessageBox::No,
177                  QMessageBox::Yes
178                  );
179       if (reply != QMessageBox::Yes)
180             return;
181 
182       WorkspacesManager::remove(workspace);
183       WorkspacesManager::setCurrentWorkspace(WorkspacesManager::workspaces().first());
184       changeWorkspace(WorkspacesManager::currentWorkspace());
185       updateIcons();
186       }
187 
188 //---------------------------------------------------------
189 //   changeWorkspace
190 //---------------------------------------------------------
191 
changeWorkspace(QAction * a)192 void MuseScore::changeWorkspace(QAction* a)
193       {
194       changeWorkspace(a->text());
195       }
196 
197 //---------------------------------------------------------
198 //   changeWorkspace
199 //---------------------------------------------------------
200 
changeWorkspace(const QString & name)201 void MuseScore::changeWorkspace(const QString& name)
202       {
203       for (Workspace* p : WorkspacesManager::workspaces()) {
204             if (qApp->translate("Ms::Workspace", p->name().toUtf8()) == name) {
205                   changeWorkspace(p);
206                   return;
207                   }
208             }
209       qDebug("   workspace \"%s\" not found", qPrintable(name));
210       }
211 
212 //---------------------------------------------------------
213 //   changeWorkspace
214 //---------------------------------------------------------
215 
changeWorkspace(Workspace * p,bool first)216 void MuseScore::changeWorkspace(Workspace* p, bool first)
217       {
218       if (!first)
219             WorkspacesManager::currentWorkspace()->save();
220 
221       if (WorkspacesManager::currentWorkspace())
222             disconnect(getPaletteWorkspace(), &PaletteWorkspace::userPaletteChanged, WorkspacesManager::currentWorkspace(), QOverload<>::of(&Workspace::setDirty));
223 
224       p->read();
225       WorkspacesManager::setCurrentWorkspace(p);
226       if (!first) {
227             updateIcons();
228             preferencesChanged(true);
229             }
230 
231       connect(getPaletteWorkspace(), &PaletteWorkspace::userPaletteChanged, WorkspacesManager::currentWorkspace(), QOverload<>::of(&Workspace::setDirty), Qt::UniqueConnection);
232 
233       preferences.setPreference(PREF_APP_WORKSPACE, p->id());
234       emit mscore->workspacesChanged();
235       }
236 
237 //---------------------------------------------------------
238 //   updateIcons
239 //---------------------------------------------------------
240 
updateIcons()241 void MuseScore::updateIcons()
242       {
243       setIconSize(QSize(preferences.getInt(PREF_UI_THEME_ICONWIDTH) * guiScaling, preferences.getInt(PREF_UI_THEME_ICONHEIGHT) * guiScaling));
244       for (QAction* a : fileTools->actions()) {
245             QWidget* widget = fileTools->widgetForAction(a);
246             QString className = widget->metaObject()->className();
247             if (className != "Ms::AccessibleToolButton" && className != "QToolBarSeparator")
248                   widget->setFixedHeight(preferences.getInt(PREF_UI_THEME_ICONHEIGHT) + 8);  // hack
249                   // apparently needed for viewModeCombo, see MuseScore::populateFileOperations
250             }
251       for (QAction* a : cpitchTools->actions()) {
252             QWidget* widget = cpitchTools->widgetForAction(a);
253             if (widget->property("iconic-text") == true)
254                   widget->setFixedHeight(preferences.getInt(PREF_UI_THEME_ICONHEIGHT) + 8);  // hack
255                   // so that toolbar buttons with text but no icon can match
256                   // the height of other toolbar buttons
257             }
258       }
259 
isDefaultWorkspace(Workspace * workspace)260 bool WorkspacesManager::isDefaultWorkspace(Workspace* workspace)
261       {
262       //returns true if @workspace's name is one of the "Basic/Advanced"
263       return std::find(defaultWorkspaces.begin(), defaultWorkspaces.end(), workspace->translatableName()) != defaultWorkspaces.end();
264       }
265 
isDefaultEditedWorkspace(Workspace * workspace)266 bool WorkspacesManager::isDefaultEditedWorkspace(Workspace* workspace)
267       {
268       //returns true if @workspace's name is one of the "Basic edited/Advanced edited"
269       return std::find(WorkspacesManager::defaultEditedWorkspaces.begin(), WorkspacesManager::defaultEditedWorkspaces.end(), workspace->translatableName()) != WorkspacesManager::defaultEditedWorkspaces.end();
270       }
271 
initCurrentWorkspace()272 void WorkspacesManager::initCurrentWorkspace()
273       {
274       initWorkspaces();
275 
276       QString workspaceName = preferences.getString(PREF_APP_WORKSPACE);
277       m_currentWorkspace = findByName(workspaceName);
278       Q_ASSERT(!workspaces().empty());
279       if (!m_currentWorkspace) {
280           m_currentWorkspace = findByTranslatableName(workspaceName);
281           if (!m_currentWorkspace) {
282               m_currentWorkspace = workspaces().at(0);
283           }
284       }
285       }
286 
remove(Workspace * workspace)287 void WorkspacesManager::remove(Workspace* workspace)
288       {
289       m_workspaces.removeOne(findByName(workspace->name()));
290       QFile f(workspace->path());
291       f.remove();
292       delete workspace;
293       isWorkspacesListDirty = true;
294       initWorkspaces();
295       emit mscore->workspacesChanged();
296       }
297 
298 //---------------------------------------------------------
299 //   writeFailed
300 //---------------------------------------------------------
301 
writeFailed(const QString & _path)302 static void writeFailed(const QString& _path)
303       {
304       QString s = qApp->translate("Workspace", "Writing Workspace File\n%1\nfailed");
305       QMessageBox::critical(mscore, qApp->translate("Workspace", "Writing Workspace File"), s.arg(_path));
306       }
307 
308 //---------------------------------------------------------
309 //   Workspace
310 //---------------------------------------------------------
311 
Workspace()312 Workspace::Workspace()
313    : QObject(0)
314       {
315       _dirty = false;
316       _readOnly = false;
317       saveComponents = false;
318       saveToolbars = false;
319       saveMenuBar = false;
320 
321       _saveTimer.setInterval(0);
322       _saveTimer.setSingleShot(true);
323       connect(&_saveTimer, &QTimer::timeout, this, &Workspace::ensureWorkspaceSaved);
324       }
325 
id() const326 QString Workspace::id() const
327       {
328       return !_translatableName.isEmpty() ? _translatableName : _name;
329       }
330 
331 //---------------------------------------------------------
332 //   makeUserWorkspacePath
333 ///   Returns path for the workspace with the given \p name
334 ///   creating all the necessary directories.
335 //---------------------------------------------------------
336 
makeUserWorkspacePath(const QString & name)337 QString WorkspacesManager::makeUserWorkspacePath(const QString& name)
338       {
339       const QString ext(".workspace");
340       QDir dir;
341       dir.mkpath(dataPath);
342       QString path(dataPath + "/workspaces");
343       dir.mkpath(path);
344       path += "/" + name + ext;
345       return path;
346       }
347 
348 //---------------------------------------------------------
349 //   write
350 //---------------------------------------------------------
351 
write()352 void Workspace::write()
353       {
354       if (_path.isEmpty())
355             _path = WorkspacesManager::makeUserWorkspacePath(_name);
356 
357       MQZipWriter f(_path);
358       f.setCreationPermissions(
359          QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner
360          | QFile::ReadUser | QFile::WriteUser | QFile::ExeUser
361          | QFile::ReadGroup | QFile::WriteGroup | QFile::ExeGroup
362          | QFile::ReadOther | QFile::WriteOther | QFile::ExeOther);
363 
364       if (f.status() != MQZipWriter::NoError) {
365             writeFailed(_path);
366             return;
367             }
368 
369       QBuffer cbuf;
370       cbuf.open(QIODevice::ReadWrite);
371       XmlWriter xml(gscore, &cbuf);
372       xml.header();
373       xml.stag("container");
374       xml.stag("rootfiles");
375       xml.stag(QString("rootfile full-path=\"%1\"").arg(XmlWriter::xmlString("workspace.xml")));
376       xml.etag();
377       for (ImageStoreItem* ip : imageStore) {
378             if (!ip->isUsed(gscore))
379                   continue;
380             QString dstPath = QString("Pictures/") + ip->hashName();
381             xml.tag("file", dstPath);
382             }
383       xml.etag();
384       xml.etag();
385       cbuf.seek(0);
386       f.addFile("META-INF/container.xml", cbuf.data());
387 
388       // save images
389       for (ImageStoreItem* ip : imageStore) {
390             if (!ip->isUsed(gscore))
391                   continue;
392             QString dstPath = QString("Pictures/") + ip->hashName();
393             f.addFile(dstPath, ip->buffer());
394             }
395       {
396       xml.setClipboardmode(true);
397       xml.header();
398       xml.stag("museScore version=\"" MSC_VERSION "\"");
399       xml.stag("Workspace");
400       // xml.tag("name", _name);
401       if (!_sourceWorkspaceName.isEmpty())
402             xml.tag("source", _sourceWorkspaceName);
403       const PaletteWorkspace* w = mscore->getPaletteWorkspace();
404       w->write(xml);
405 
406       // write toolbar settings
407       if (saveToolbars) {
408             xml.stag("Toolbar name=\"noteInput\"");
409             for (auto i : *mscore->noteInputMenuEntries())
410                   xml.tag("action", i);
411             xml.etag();
412             xml.stag("Toolbar name=\"fileOperation\"");
413             for (auto i : *mscore->fileOperationEntries())
414                   xml.tag("action", i);
415             xml.etag();
416             xml.stag("Toolbar name=\"playbackControl\"");
417             for (auto i : *mscore->playbackControlEntries())
418                   xml.tag("action", i);
419             xml.etag();
420             }
421       else {
422             writeGlobalToolBar();
423             }
424 
425       if (preferences.getUseLocalPreferences()) {
426             xml.stag("Preferences");
427             for (QString pref : preferences.getLocalPreferences().keys()) {
428                   QVariant prefValue = preferences.getLocalPreferences().value(pref);
429                   if (prefValue.isValid())
430                         xml.tag("Preference name=\"" + pref + "\"", preferences.getLocalPreferences().value(pref));
431                   }
432             xml.etag();
433             }
434 
435       if (saveMenuBar)
436             writeMenuBar(xml);
437 
438       if (saveComponents) {
439             QByteArray state_64 = mscore->saveState().toBase64();
440             QString state(state_64);
441             xml.tag("State", state);
442             }
443       else {
444             writeGlobalGUIState();
445             }
446 
447       xml.etag();
448       xml.etag();
449       f.addFile("workspace.xml", cbuf.data());
450       cbuf.close();
451       }
452 
453       if (f.status() != MQZipWriter::NoError)
454             writeFailed(_path);
455       }
456 
457 //---------------------------------------------------------
458 //   writeGlobalMenuBar
459 //   writes global menu bar for workspaces
460 //---------------------------------------------------------
461 
writeGlobalMenuBar(QMenuBar * mb)462 void Workspace::writeGlobalMenuBar(QMenuBar* mb)
463       {
464       QString default_path = "";
465       QDir dir;
466       dir.mkpath(dataPath);
467       default_path = dataPath + "/workspaces";
468       dir.mkpath(default_path);
469       default_path += "/global";
470       dir.mkpath(default_path);
471       default_path += "/menubar.xml";
472 
473       QFile default_menubar (default_path);
474       default_menubar.open(QIODevice::WriteOnly);
475 
476       if (!default_menubar.exists()) {
477             writeFailed(default_path);
478             return;
479             }
480 
481       QBuffer cbuf;
482       cbuf.open(QIODevice::ReadWrite);
483       XmlWriter xml(gscore, &cbuf);
484       xml.setClipboardmode(true);
485       xml.header();
486       xml.stag("museScore version=\"" MSC_VERSION "\"");
487 
488       writeMenuBar(xml, mb);
489 
490       xml.etag();
491       default_menubar.write(cbuf.data());
492       cbuf.close();
493       default_menubar.close();
494       }
495 
496 //---------------------------------------------------------
497 //   writeGlobalToolBar
498 //   writes global tool bar for workspaces
499 //---------------------------------------------------------
500 
writeGlobalToolBar()501 void Workspace::writeGlobalToolBar()
502       {
503       QString default_path = "";
504       QDir dir;
505       dir.mkpath(dataPath);
506       default_path = dataPath + "/workspaces";
507       dir.mkpath(default_path);
508       default_path += "/global";
509       dir.mkpath(default_path);
510       default_path += "/toolbar.xml";
511 
512       QFile default_toolbar (default_path);
513       default_toolbar.open(QIODevice::WriteOnly);
514 
515       if (!default_toolbar.exists()) {
516             writeFailed(default_path);
517             return;
518             }
519 
520       QBuffer cbuf;
521       cbuf.open(QIODevice::ReadWrite);
522       XmlWriter xml(gscore, &cbuf);
523       xml.setClipboardmode(true);
524       xml.header();
525       xml.stag("museScore version=\"" MSC_VERSION "\"");
526 
527       xml.stag("Toolbar name=\"noteInput\"");
528       for (auto i : *mscore->noteInputMenuEntries())
529             xml.tag("action", i);
530       xml.etag();
531       xml.stag("Toolbar name=\"fileOperation\"");
532       for (auto i : *mscore->fileOperationEntries())
533             xml.tag("action", i);
534       xml.etag();
535       xml.stag("Toolbar name=\"playbackControl\"");
536       for (auto i : *mscore->playbackControlEntries())
537             xml.tag("action", i);
538       xml.etag();
539 
540       xml.etag();
541       default_toolbar.write(cbuf.data());
542       cbuf.close();
543       default_toolbar.close();
544       }
545 
546 //---------------------------------------------------------
547 //   writeGlobalGUIState
548 //   writes global GUI state for workspaces
549 //---------------------------------------------------------
550 
writeGlobalGUIState()551 void Workspace::writeGlobalGUIState()
552       {
553       QString default_path = "";
554       QDir dir;
555       dir.mkpath(dataPath);
556       default_path = dataPath + "/workspaces";
557       dir.mkpath(default_path);
558       default_path += "/global";
559       dir.mkpath(default_path);
560       default_path += "/guistate.xml";
561 
562       QFile default_guistate (default_path);
563       default_guistate.open(QIODevice::WriteOnly);
564 
565       if (!default_guistate.exists()) {
566             writeFailed(default_path);
567             return;
568             }
569 
570       QBuffer cbuf;
571       cbuf.open(QIODevice::ReadWrite);
572       XmlWriter xml(gscore, &cbuf);
573       xml.setClipboardmode(true);
574       xml.header();
575       xml.stag("museScore version=\"" MSC_VERSION "\"");
576 
577       QByteArray state_64 = mscore->saveState().toBase64();
578       QString state(state_64);
579       xml.tag("State", state);
580 
581       xml.etag();
582       default_guistate.write(cbuf.data());
583       cbuf.close();
584       default_guistate.close();
585       }
586 
587 //---------------------------------------------------------
588 //   writeMenuBar
589 //---------------------------------------------------------
590 
writeMenuBar(XmlWriter & xml,QMenuBar * mb)591 void Workspace::writeMenuBar(XmlWriter& xml, QMenuBar* mb)
592       {
593       // Loop through each menu in menubar. For each menu, call writeMenu.
594       xml.stag("MenuBar");
595       if (!mb)
596             mb = mscore->menuBar();
597       for (QAction* action : mb->actions()) {
598             if (action->isSeparator())
599                   xml.tag("action", "");
600             else if (action->menu()) {
601                   const QString menuString = findStringFromMenu(action->menu());
602                   if (!menuString.isEmpty()) {
603                         xml.stag("Menu name=\"" + menuString + "\"");
604                         writeMenu(xml, action->menu());
605                         xml.etag();
606                         }
607                   }
608             else {
609                   const QString actionString = findStringFromAction(action);
610                   if (!actionString.isEmpty())
611                         xml.tag("action", actionString);
612                   }
613 
614             }
615       xml.etag();
616       }
617 
618 //---------------------------------------------------------
619 //   writeMenu
620 //---------------------------------------------------------
621 
writeMenu(XmlWriter & xml,QMenu * menu)622 void Workspace::writeMenu(XmlWriter& xml, QMenu* menu)
623       {
624       // Recursively save QMenu
625       for (QAction* action : menu->actions()) {
626             if (action->isSeparator())
627                   xml.tag("action", "");
628             else if (action->menu()) {
629                   const QString menuString = findStringFromMenu(action->menu());
630                   if (!menuString.isEmpty()) {
631                         xml.stag("Menu name=\"" + menuString + "\"");
632                         writeMenu(xml, action->menu());
633                         xml.etag();
634                         }
635                   }
636             else {
637                   const QString actionString = findStringFromAction(action);
638                   if (!actionString.isEmpty())
639                         xml.tag("action", actionString);
640                   }
641             }
642       }
643 
644 extern QString readRootFile(MQZipReader*, QList<QString>&);
645 
646 //---------------------------------------------------------
647 //   read
648 //---------------------------------------------------------
649 
readWorkspaceFile(const QString & path,std::function<void (XmlReader &)> readWorkspace)650 void WorkspacesManager::readWorkspaceFile(const QString& path, std::function<void(XmlReader&)> readWorkspace)
651       {
652       MQZipReader f(path);
653       QList<QString> images;
654       QString rootfile = readRootFile(&f, images);
655       //
656       // load images
657       //
658       for (const QString& s : images)
659             imageStore.add(s, f.fileData(s));
660 
661       if (rootfile.isEmpty()) {
662             qDebug("can't find rootfile in: %s", qPrintable(path));
663             return;
664             }
665 
666       QByteArray ba = f.fileData(rootfile);
667       XmlReader e(ba);
668       e.setPasteMode(true);
669 
670       while (e.readNextStartElement()) {
671             if (e.name() == "museScore") {
672                   while (e.readNextStartElement()) {
673                         if (e.name() == "Workspace")
674                               readWorkspace(e);
675                         else
676                               e.unknown();
677                         }
678                   }
679             }
680       }
681 
read()682 void Workspace::read()
683       {
684       saveToolbars = saveMenuBar = saveComponents = false;
685       preferences.setUseLocalPreferences(false);
686       if (_path.isEmpty() || !QFile(_path).exists()) {
687             qDebug("cannot read workspace <%s>", qPrintable(_path));
688             mscore->setDefaultPalette();
689             readGlobalMenuBar();
690             readGlobalToolBar();
691             readGlobalGUIState();
692             preferences.updateLocalPreferences();
693             return;
694             }
695       QFileInfo fi(_path);
696       qt_ntfs_permission_lookup++;
697       _readOnly = !fi.isWritable();
698       qt_ntfs_permission_lookup--;
699 
700       preferences.updateLocalPreferences();
701 
702       WorkspacesManager::readWorkspaceFile(_path, [this](XmlReader& e) { read(e); });
703       }
704 
705 /**
706       Reset the workspace to the state of the source workspace.
707       Works for Custom workspaces ONLY!
708  */
reset()709 void Workspace::reset()
710       {
711       saveToolbars = saveMenuBar = saveComponents = false;
712       preferences.setUseLocalPreferences(false);
713       preferences.updateLocalPreferences();
714       const Workspace* srcWorkspace = sourceWorkspace();
715       WorkspacesManager::readWorkspaceFile(srcWorkspace->path(), [this](XmlReader& e) { read(e); });
716       save();
717       }
718 
getPaletteTree() const719 std::unique_ptr<PaletteTree> Workspace::getPaletteTree() const
720       {
721       std::unique_ptr<PaletteTree> paletteTree(new PaletteTree);
722       WorkspacesManager::readWorkspaceFile(_path, [&](XmlReader& e) {
723             while (e.readNextStartElement()) {
724                   if (e.name() == "PaletteBox")
725                         paletteTree->read(e);
726                   else
727                         e.skipCurrentElement();
728                   }
729             });
730       return paletteTree;
731       }
732 
read(XmlReader & e)733 void Workspace::read(XmlReader& e)
734       {
735       bool niToolbar = false;
736       bool foToolbar = false;
737       bool pcToolbar = false;
738       while (e.readNextStartElement()) {
739             const QStringRef& tag(e.name());
740             if (tag == "name")
741                   e.readElementText();
742             else if (tag == "source")
743                   _sourceWorkspaceName = e.readElementText();
744             else if (tag == "PaletteBox") {
745                   PaletteWorkspace* w = mscore->getPaletteWorkspace();
746                   w->read(e);
747                   }
748             else if (tag == "Toolbar") {
749                   saveToolbars = true;
750                   QString name = e.attribute("name");
751                   std::list<const char *> toolbarEntries;
752                   if (name == "noteInput")
753                         toolbarEntries = mscore->allNoteInputMenuEntries();
754                   else if (name == "fileOperation")
755                         toolbarEntries = mscore->allFileOperationEntries();
756                   else if (name == "playbackControl")
757                         toolbarEntries = mscore->allPlaybackControlEntries();
758                   else
759                         qDebug() << "Error in loading workspace: " + name + " is not a toolbar";
760 
761                   std::list<const char*> l;
762                   while (e.readNextStartElement()) {
763                         const QStringRef& t(e.name());
764                         if (t == "action") {
765                               QString s = e.readElementText();
766                               for (auto k : toolbarEntries) {
767                                     if (k == s) {
768                                           l.push_back(k);
769                                           break;
770                                           }
771                                     }
772                               }
773                         else
774                               e.unknown();
775                         }
776                   if (name == "noteInput") {
777                         mscore->setNoteInputMenuEntries(l);
778                         mscore->populateNoteInputMenu();
779                         niToolbar = true;
780                         }
781                   else if (name == "fileOperation") {
782                         mscore->setFileOperationEntries(l);
783                         mscore->populateFileOperations();
784                         foToolbar = true;
785                         }
786                   else if (name == "playbackControl") {
787                         mscore->setPlaybackControlEntries(l);
788                         mscore->populatePlaybackControls();
789                         pcToolbar = true;
790                         }
791                   }
792             else if (tag == "Preferences") {
793                   preferences.setUseLocalPreferences(true);
794                   while (e.readNextStartElement()) {
795                         QString preference_name = e.attribute("name");
796                         switch (preferences.defaultValue(preference_name).type()) {
797                               case QVariant::Int:
798                                     {
799                                     int new_int = e.readInt();
800                                     preferences.setLocalPreference(preference_name, QVariant(new_int));
801                                     }
802                                     break;
803                               case QVariant::Color:
804                                     {
805                                     QColor new_color = e.readColor();
806                                     preferences.setLocalPreference(preference_name, QVariant(new_color));
807                                     }
808                                     break;
809                               case QVariant::String:
810                                     {
811                                     QString new_string = e.readXml();
812                                     preferences.setLocalPreference(preference_name, QVariant(new_string));
813                                     }
814                                     break;
815                               case QVariant::Bool:
816                                     {
817                                     bool new_bool = e.readBool();
818                                     preferences.setLocalPreference(preference_name, QVariant(new_bool));
819                                     }
820                                     break;
821                               case QVariant::LongLong:
822                                     {
823                                     bool new_longlong = e.readLongLong();
824                                     preferences.setLocalPreference(preference_name, QVariant(new_longlong));
825                                     break;
826                                     }
827                               default:
828                                     qDebug() << preferences.defaultValue(preference_name).type() << " not handled.";
829                                     e.unknown();
830                               }
831                         }
832                   }
833             else if (tag == "MenuBar") {
834                   saveMenuBar = true;
835                   QMenuBar* mb = mscore->menuBar();
836                   const QObjectList menus(mb->children()); // need a copy
837                   for (QObject* m : menus) {
838                         QMenu* menu = qobject_cast<QMenu*>(m);
839                         if (menu) {
840                               menu->setParent(nullptr);
841                               menu->deleteLater();
842                               }
843                         }
844                   mb->clear();
845                   menuToStringList.clear();
846                   while (e.readNextStartElement()) {
847                         if (e.hasAttribute("name")) { // is a menu
848                               QString menu_id = e.attribute("name");
849                               QMenu* menu = mb->addMenu(menu_id);
850                               addMenuAndString(menu, menu_id);
851                               readMenu(e, menu);
852                               }
853                         else { // is an action
854                               QString action_id = e.readXml();
855                               if (action_id.isEmpty())
856                                     mb->addSeparator();
857                               else {
858                                     QAction* action = findActionFromString(action_id);
859                                     mb->addAction(action);
860                                     }
861                               }
862                         }
863                   mscore->updateMenus();
864                   }
865             else if (tag == "State") {
866                   saveComponents = true;
867                   QString state_string = e.readXml();
868                   QByteArray state_byte_array_64(state_string.toUtf8());
869                   QByteArray state_byte_array = QByteArray::fromBase64(state_byte_array_64);
870                   mscore->restoreState(state_byte_array);
871                   }
872             else
873                   e.unknown();
874             }
875       if (saveToolbars) {
876             if (!niToolbar) {
877                   mscore->setNoteInputMenuEntries(mscore->allNoteInputMenuEntries());
878                   mscore->populateNoteInputMenu();
879                   }
880             if (!foToolbar) {
881                   mscore->setFileOperationEntries(mscore->allFileOperationEntries());
882                   mscore->populateFileOperations();
883                   }
884             if (!pcToolbar) {
885                   mscore->setPlaybackControlEntries(mscore->allPlaybackControlEntries());
886                   mscore->populatePlaybackControls();
887                   }
888             }
889       else {
890             readGlobalToolBar();
891             }
892       if (!saveMenuBar)
893             readGlobalMenuBar();
894       if (!saveComponents)
895             readGlobalGUIState();
896 
897       if (const Workspace* src = sourceWorkspace())
898             mscore->getPaletteWorkspace()->setDefaultPaletteTree(src->getPaletteTree());
899       }
900 
901 //---------------------------------------------------------
902 //   readMenu
903 //---------------------------------------------------------
904 
readMenu(XmlReader & e,QMenu * menu)905 void Workspace::readMenu(XmlReader& e, QMenu* menu)
906       {
907       while (e.readNextStartElement()) {
908             if (e.hasAttribute("name")) { // is a menu
909                   QString menu_id = e.attribute("name");
910                   QMenu* new_menu = menu->addMenu(menu_id);
911                   addMenuAndString(new_menu, menu_id);
912                   readMenu(e, new_menu);
913                   }
914             else { // is an action
915                   QString action_id = e.readXml();
916                   if (action_id.isEmpty())
917                         menu->addSeparator();
918                   else {
919                         QAction* action = findActionFromString(action_id);
920                         menu->addAction(action);
921                         }
922                   }
923             }
924       }
925 
926 //---------------------------------------------------------
927 //   readGlobalMenuBar
928 //---------------------------------------------------------
929 
readGlobalMenuBar()930 void Workspace::readGlobalMenuBar()
931       {
932       QString default_path = dataPath + "/workspaces/global/menubar.xml";
933 
934       QFile default_menubar(default_path);
935       default_menubar.open(QIODevice::ReadOnly);
936 
937       QByteArray ba (default_menubar.readAll());
938       XmlReader e(ba);
939       e.setPasteMode(true);
940 
941       while (e.readNextStartElement()) {
942             if (e.name() == "museScore") {
943                   while (e.readNextStartElement()) {
944                         if (e.name() == "MenuBar") {
945                               QMenuBar* mb = mscore->menuBar();
946                               const QObjectList menus(mb->children()); // need a copy
947                               for (QObject* m : menus) {
948                                     QMenu* menu = qobject_cast<QMenu*>(m);
949                                     if (menu) {
950                                           menu->setParent(nullptr);
951                                           menu->deleteLater();
952                                           }
953                                     }
954                               mb->clear();
955                               menuToStringList.clear();
956                               while (e.readNextStartElement()) {
957                                     if (e.hasAttribute("name")) { // is a menu
958                                           QString menu_id = e.attribute("name");
959                                           QMenu* menu = mb->addMenu(menu_id);
960                                           addMenuAndString(menu, menu_id);
961                                           readMenu(e, menu);
962                                           }
963                                     else { // is an action
964                                           QString action_id = e.readXml();
965                                           if (action_id.isEmpty())
966                                                 mb->addSeparator();
967                                           else {
968                                                 QAction* action = findActionFromString(action_id);
969                                                 mb->addAction(action);
970                                                 }
971                                           }
972                                     }
973                               mscore->updateMenus();
974                               }
975                         else
976                               e.unknown();
977                         }
978                   }
979             }
980       }
981 
982 //---------------------------------------------------------
983 //   readGlobalToolBar
984 //---------------------------------------------------------
985 
readGlobalToolBar()986 void Workspace::readGlobalToolBar()
987       {
988       QString default_path = dataPath + "/workspaces/global/toolbar.xml";
989 
990       QFile default_toolbar(default_path);
991       default_toolbar.open(QIODevice::ReadOnly);
992 
993       QByteArray ba (default_toolbar.readAll());
994       XmlReader e(ba);
995       e.setPasteMode(true);
996 
997       while (e.readNextStartElement()) {
998             if (e.name() == "museScore") {
999                   while (e.readNextStartElement()) {
1000                         if (e.name() == "ToolBar") {
1001                               QString name = e.attribute("name");
1002                               std::list<const char *> toolbarEntries;
1003                               if (name == "noteInput")
1004                                     toolbarEntries = mscore->allNoteInputMenuEntries();
1005                               else if (name == "fileOperation")
1006                                     toolbarEntries = mscore->allFileOperationEntries();
1007                               else if (name == "playbackControl")
1008                                     toolbarEntries = mscore->allPlaybackControlEntries();
1009                               else
1010                                     qDebug() << "Error in loading workspace: " + name + " is not a toolbar";
1011 
1012                               std::list<const char*> l;
1013                               while (e.readNextStartElement()) {
1014                                     const QStringRef& tag(e.name());
1015                                     if (tag == "action") {
1016                                           QString s = e.readElementText();
1017                                           for (auto k : toolbarEntries) {
1018                                                 if (k == s) {
1019                                                       l.push_back(k);
1020                                                       break;
1021                                                       }
1022                                                 }
1023                                           }
1024                                     else
1025                                           e.unknown();
1026                                     }
1027                               if (name == "noteInput") {
1028                                     mscore->setNoteInputMenuEntries(l);
1029                                     mscore->populateNoteInputMenu();
1030                                     }
1031                               else if (name == "fileOperation") {
1032                                     mscore->setFileOperationEntries(l);
1033                                     mscore->populateFileOperations();
1034                                     }
1035                               else if (name == "playbackControl") {
1036                                     mscore->setPlaybackControlEntries(l);
1037                                     mscore->populatePlaybackControls();
1038                                     }
1039                               }
1040                         else
1041                               e.unknown();
1042                         }
1043                   }
1044             }
1045       }
1046 
1047 //---------------------------------------------------------
1048 //   readGlobalGUIState
1049 //---------------------------------------------------------
1050 
readGlobalGUIState()1051 void Workspace::readGlobalGUIState()
1052       {
1053       QString default_path = dataPath + "/workspaces/global/guistate.xml";
1054 
1055       QFile default_toolbar(default_path);
1056       default_toolbar.open(QIODevice::ReadOnly);
1057 
1058       QByteArray ba (default_toolbar.readAll());
1059       XmlReader e(ba);
1060       e.setPasteMode(true);
1061 
1062       while (e.readNextStartElement()) {
1063             if (e.name() == "museScore") {
1064                   while (e.readNextStartElement()) {
1065                         if (e.name() == "State") {
1066                               QString state_string = e.readXml();
1067                               QByteArray state_byte_array_64(state_string.toUtf8());
1068                               QByteArray state_byte_array = QByteArray::fromBase64(state_byte_array_64);
1069                               mscore->restoreState(state_byte_array);
1070                               }
1071                         else
1072                               e.unknown();
1073                         }
1074                   }
1075             }
1076       }
1077 
1078 //---------------------------------------------------------
1079 //   ensureWorkspaceSaved
1080 //---------------------------------------------------------
1081 
ensureWorkspaceSaved()1082 void Workspace::ensureWorkspaceSaved()
1083       {
1084       if (!_dirty)
1085             return;
1086 
1087       if (_readOnly) {
1088             setTranslatableName(editedWorkspaceTranslatableName(translatableName()));
1089 
1090             if (translatableName().isEmpty()) {
1091                   /*: Name of the edited read-only workspace, %1 is replaced with the old workspace name */
1092                   setName(tr("%1 edited").arg(name()));
1093                   }
1094             else
1095                   setName(tr(translatableName().toUtf8()));
1096 
1097             _path = WorkspacesManager::makeUserWorkspacePath(translatableName().isEmpty() ? name() : translatableName());
1098 
1099             write();
1100 
1101             const QFileInfo fi(_path);
1102             qt_ntfs_permission_lookup++;
1103             _readOnly = !fi.isWritable();
1104             qt_ntfs_permission_lookup--;
1105             Q_ASSERT(!_readOnly);
1106 
1107             WorkspacesManager::refreshWorkspaces();
1108             preferences.setPreference(PREF_APP_WORKSPACE, id());
1109             emit mscore->workspacesChanged();
1110             }
1111       else
1112             write();
1113       }
1114 
1115 //---------------------------------------------------------
1116 //   setDirty
1117 //---------------------------------------------------------
1118 
setDirty(bool val)1119 void Workspace::setDirty(bool val)
1120       {
1121       _dirty = val;
1122       _saveTimer.start();
1123       }
1124 
1125 //---------------------------------------------------------
1126 //   save
1127 //---------------------------------------------------------
1128 
save()1129 void Workspace::save()
1130       {
1131       if (!saveComponents)
1132             writeGlobalGUIState();
1133       if (!saveToolbars)
1134             writeGlobalToolBar();
1135 
1136       if (_readOnly)
1137             return;
1138 
1139       write();
1140       }
1141 
findWorkspaceFiles()1142 static QStringList findWorkspaceFiles()
1143       {
1144       QStringList path;
1145       path << mscoreGlobalShare + "workspaces";
1146       path << dataPath + "/workspaces";
1147 
1148       QStringList extensionsDir = Extension::getDirectoriesByType(Extension::workspacesDir);
1149       path.append(extensionsDir);
1150 
1151       QStringList nameFilters;
1152       nameFilters << "*.workspace";
1153 
1154       QStringList workspaces;
1155 
1156       for (const QString& s : path) {
1157             QDir dir(s);
1158             QStringList pl = dir.entryList(nameFilters, QDir::Files, QDir::Name);
1159 
1160             for (const QString& entry : pl) {
1161                   const QString workspacePath(s + "/" + entry);
1162                   workspaces << workspacePath;
1163                   }
1164             }
1165 
1166       return workspaces;
1167       }
1168 
initWorkspaces()1169 void WorkspacesManager::initWorkspaces()
1170       {
1171       if (!isWorkspacesListDirty)
1172             return;
1173 
1174       QList<Workspace*> oldWorkspaces(m_workspaces);
1175       QList<Workspace*> editedWorkpaces;
1176 
1177       for (const QString& path : findWorkspaceFiles()) {
1178             Workspace* p = 0;
1179             QFileInfo fi(path);
1180             QString name(fi.completeBaseName());
1181 
1182             const bool isDefault = std::find(WorkspacesManager::defaultWorkspaces.begin(), WorkspacesManager::defaultWorkspaces.end(), name) != WorkspacesManager::defaultWorkspaces.end();
1183             const bool isEditedDefault = std::find(WorkspacesManager::defaultEditedWorkspaces.begin(), WorkspacesManager::defaultEditedWorkspaces.end(), name) != WorkspacesManager::defaultEditedWorkspaces.end();
1184 
1185             const bool translate = isDefault || isEditedDefault;
1186 
1187             for (Workspace* w : m_workspaces) {
1188                   if (w->name() == name || (translate && w->translatableName() == name)) {
1189                         p = w;
1190                         break;
1191                         }
1192                   }
1193 
1194             if (p)
1195                   oldWorkspaces.removeOne(p);
1196             else {
1197                   p = new Workspace;
1198                   m_workspaces.append(p);
1199                   }
1200 
1201             p->setPath(path);
1202             p->setName(name);
1203 
1204             if (translate)
1205                   p->setTranslatableName(name);
1206 
1207             qt_ntfs_permission_lookup++;
1208             p->setReadOnly(!fi.isWritable());
1209             qt_ntfs_permission_lookup--;
1210 
1211             if (isEditedDefault)
1212                   editedWorkpaces.push_back(p);
1213             }
1214 
1215       for (Workspace* old : oldWorkspaces)
1216             m_workspaces.removeOne(old);
1217 
1218       if (m_workspaces.empty())
1219             qFatal("No workspaces found");
1220 
1221       if (oldWorkspaces.contains(WorkspacesManager::currentWorkspace()))
1222             WorkspacesManager::setCurrentWorkspace(m_workspaces.first());
1223 
1224       qDeleteAll(oldWorkspaces);
1225 
1226       // hack
1227       for (int i = 0; i < m_workspaces.size(); i++) {
1228             const QString& trName = m_workspaces[i]->translatableName();
1229             if (trName == WorkspacesManager::defaultWorkspaces[0] || trName == WorkspacesManager::defaultEditedWorkspaces[0]) {
1230                   m_workspaces.move(i, 0);
1231                   break;
1232                   }
1233             }
1234 
1235       retranslate(m_workspaces);
1236 
1237       // Delete default workspaces if there are corresponding user-edited ones
1238       m_visibleWorkspaces = m_workspaces;
1239       for (Workspace* ew : editedWorkpaces) {
1240             const QString uneditedName = defaultWorkspaceTranslatableName(ew->translatableName());
1241             if (uneditedName.isEmpty())
1242                   continue;
1243 
1244             for (auto it = m_visibleWorkspaces.begin(); it != m_visibleWorkspaces.end(); ++it) {
1245                   Workspace* w = *it;
1246                   if (w->translatableName() == uneditedName) {
1247                         m_visibleWorkspaces.erase(it);
1248                         break;
1249                         }
1250                   }
1251             }
1252 
1253       isWorkspacesListDirty = false;
1254       }
1255 
1256 //---------------------------------------------------------
1257 //   refreshWorkspaces
1258 //---------------------------------------------------------
1259 
refreshWorkspaces()1260 void WorkspacesManager::refreshWorkspaces()
1261       {
1262       isWorkspacesListDirty = true;
1263       initWorkspaces();
1264       }
1265 
sourceWorkspace() const1266 const Workspace* Workspace::sourceWorkspace() const
1267       {
1268       const QString sourceName = _sourceWorkspaceName.isEmpty() ? WorkspacesManager::defaultWorkspaces[0] : _sourceWorkspaceName;
1269 
1270       if (translatableName() == sourceName || name() == sourceName)
1271             return this;
1272 
1273       Workspace* sourceWorkspace = WorkspacesManager::findByName(sourceName);
1274       if (!sourceWorkspace)
1275             sourceWorkspace = WorkspacesManager::findByTranslatableName(sourceName);
1276 
1277       return !!sourceWorkspace ? sourceWorkspace : WorkspacesManager::workspaces()[0];
1278       }
1279 
retranslate(QList<Workspace * > & workspacesList)1280 void WorkspacesManager::retranslate(QList<Workspace*>& workspacesList)
1281       {
1282       for (auto w : workspacesList) {
1283             if (!w->translatableName().isEmpty()) {
1284                   auto transName = qApp->translate("Ms::Workspace", w->translatableName().toLatin1().constData());
1285                   w->setName(transName);
1286                   }
1287             }
1288       }
1289 
retranslateAll()1290 void WorkspacesManager::retranslateAll()
1291       {
1292       retranslate(m_workspaces);
1293       }
1294 
1295 //---------------------------------------------------------
1296 //   createNewWorkspace
1297 //---------------------------------------------------------
1298 
createNewWorkspace(const QString & name)1299 Workspace* WorkspacesManager::createNewWorkspace(const QString& name)
1300       {
1301       Workspace* w = new Workspace;
1302       w->setName(name);
1303       w->setPath("");
1304       w->setDirty(false);
1305       w->setReadOnly(false);
1306       w->write();
1307       w->setSourceWorkspaceName(WorkspacesManager::currentWorkspace()->sourceWorkspaceName());
1308 
1309       m_workspaces.append(w);
1310       m_visibleWorkspaces.append(w);
1311       return w;
1312       }
1313 
1314 //---------------------------------------------------------
1315 //   clearWorkspaces
1316 //---------------------------------------------------------
1317 
clearWorkspaces()1318 void WorkspacesManager::clearWorkspaces()
1319       {
1320       m_currentWorkspace = nullptr;
1321       for (Workspace* w : m_workspaces)
1322             w->deleteLater();
1323       m_workspaces.clear();
1324       m_visibleWorkspaces.clear();
1325       isWorkspacesListDirty = true;
1326       }
1327 
1328 //---------------------------------------------------------
1329 //   addActionAndString
1330 //---------------------------------------------------------
1331 
addActionAndString(QAction * action,QString string)1332 void Workspace::addActionAndString(QAction* action, QString string)
1333       {
1334       QPair<QAction*, QString> pair;
1335       pair.first = action;
1336       pair.second = string;
1337       actionToStringList.append(pair);
1338       }
1339 
1340 //---------------------------------------------------------
1341 //   addRemainingFromMenuBar
1342 //---------------------------------------------------------
1343 
addRemainingFromMenuBar(QMenuBar * mb)1344 void Workspace::addRemainingFromMenuBar(QMenuBar* mb)
1345       {
1346       // Loop through each menu in menubar. For each menu, call writeMenu.
1347       for (QAction* action : mb->actions()) {
1348             if (action->isSeparator())
1349                   continue;
1350             else if (action->menu())
1351                   addRemainingFromMenu(action->menu());
1352             else if (!action->data().toString().isEmpty())
1353                   addActionAndString(action, action->data().toString());
1354             }
1355       }
1356 
1357 //---------------------------------------------------------
1358 //   addRemainingFromMenu
1359 //---------------------------------------------------------
1360 
addRemainingFromMenu(QMenu * menu)1361 void Workspace::addRemainingFromMenu(QMenu* menu)
1362       {
1363       // Recursively save QMenu
1364       for (QAction* action : menu->actions()) {
1365             if (action->isSeparator())
1366                   continue;
1367             else if (action->menu())
1368                   addRemainingFromMenu(action->menu());
1369             else if (!action->data().toString().isEmpty())
1370                   addActionAndString(action, action->data().toString());
1371             }
1372       }
1373 
1374 //---------------------------------------------------------
1375 //   findActionFromString
1376 //---------------------------------------------------------
1377 
findActionFromString(QString string)1378 QAction* Workspace::findActionFromString(QString string)
1379       {
1380       for (auto pair : actionToStringList) {
1381             if (pair.second == string)
1382                   return pair.first;
1383             }
1384       return 0;
1385       }
1386 
1387 //---------------------------------------------------------
1388 //   findStringFromAction
1389 //---------------------------------------------------------
1390 
findStringFromAction(QAction * action)1391 QString Workspace::findStringFromAction(QAction* action)
1392       {
1393       for (auto pair : actionToStringList) {
1394             if (pair.first == action)
1395                   return pair.second;
1396             }
1397       return 0;
1398       }
1399 
1400 //---------------------------------------------------------
1401 //   addMenuAndString
1402 //---------------------------------------------------------
1403 
addMenuAndString(QMenu * menu,QString string)1404 void Workspace::addMenuAndString(QMenu* menu, QString string)
1405       {
1406       QPair<QMenu*, QString> pair;
1407       pair.first = menu;
1408       pair.second = string;
1409       menuToStringList.append(pair);
1410       }
1411 
1412 //---------------------------------------------------------
1413 //   findMenuFromString
1414 //---------------------------------------------------------
1415 
findMenuFromString(QString string)1416 QMenu* Workspace::findMenuFromString(QString string)
1417       {
1418       for (auto pair : menuToStringList) {
1419             if (pair.second == string)
1420                   return pair.first;
1421             }
1422       return 0;
1423       }
1424 
1425 //---------------------------------------------------------
1426 //   findStringFromMenu
1427 //---------------------------------------------------------
1428 
findStringFromMenu(QMenu * menu)1429 QString Workspace::findStringFromMenu(QMenu* menu)
1430       {
1431       for (auto pair : menuToStringList) {
1432             if (pair.first == menu)
1433                   return pair.second;
1434             }
1435       return 0;
1436       }
1437 
1438 //---------------------------------------------------------
1439 //   rename
1440 //---------------------------------------------------------
1441 
rename(const QString & s)1442 void Workspace::rename(const QString& s)
1443       {
1444       const QString newPath = WorkspacesManager::makeUserWorkspacePath(s);
1445 
1446       QFile file(_path);
1447       if (file.exists())
1448             file.rename(newPath);
1449 
1450       setName(s);
1451       _path = newPath;
1452       save();
1453       }
1454 }
1455