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