1 //=============================================================================
2 //  MuseScore
3 //  Linux Music Score Editor
4 //
5 //  Copyright (C) 2009-2012 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 "musescore.h"
21 #include "config.h"
22 #include "preferences.h"
23 #include "qmlplugin.h"
24 #include "qmlpluginengine.h"
25 #include "pluginManager.h"
26 
27 namespace Ms {
28 
29 //---------------------------------------------------------
30 //   registerPlugin
31 //---------------------------------------------------------
32 
registerPlugin(PluginDescription * plugin)33 void MuseScore::registerPlugin(PluginDescription* plugin)
34       {
35       QString _pluginPath = plugin->path;
36       QFileInfo np(_pluginPath);
37       if (np.suffix() != "qml")
38             return;
39       QString baseName = np.completeBaseName();
40 
41       foreach(QString s, plugins) {
42             QFileInfo fi(s);
43             if (fi.completeBaseName() == baseName) {
44                   if (MScore::debugMode)
45                         qDebug("  Plugin <%s> already registered", qPrintable(_pluginPath));
46                   return;
47                   }
48             }
49 
50       QFile f(_pluginPath);
51       if (!f.open(QIODevice::ReadOnly)) {
52             if (MScore::debugMode)
53                   qDebug("Loading Plugin <%s> failed", qPrintable(_pluginPath));
54             return;
55             }
56       if (MScore::debugMode)
57             qDebug("Register Plugin <%s>", qPrintable(_pluginPath));
58       f.close();
59       QObject* obj = 0;
60       QQmlComponent component(getPluginEngine(), QUrl::fromLocalFile(_pluginPath));
61       obj = component.create();
62       if (obj == 0) {
63             qDebug("creating component <%s> failed", qPrintable(_pluginPath));
64             foreach(QQmlError e, component.errors()) {
65                   qDebug("   line %d: %s", e.line(), qPrintable(e.description()));
66                   }
67             return;
68             }
69       //
70       // load translation
71       //
72       QLocale locale;
73       QString t = np.absolutePath() + "/translations/locale_" + MuseScore::getLocaleISOCode().left(2) + ".qm";
74       QTranslator* translator = new QTranslator;
75       if (!translator->load(t)) {
76 //            qDebug("cannot load qml translations <%s>", qPrintable(t));
77             delete translator;
78             }
79       else {
80 //            qDebug("load qml translations <%s>", qPrintable(t));
81             qApp->installTranslator(translator);
82             }
83 
84       QmlPlugin* item = qobject_cast<QmlPlugin*>(obj);
85       QString menuPath = item->menuPath();
86       plugin->menuPath = menuPath;
87       plugins.append(_pluginPath);
88       createMenuEntry(plugin);
89 
90       QAction* a = plugin->shortcut.action();
91       pluginActions.append(a);
92       connect(a, &QAction::triggered, mscore, [_pluginPath]() { mscore->pluginTriggered(_pluginPath); });
93 
94       delete obj;
95       }
96 
unregisterPlugin(PluginDescription * plugin)97 void MuseScore::unregisterPlugin(PluginDescription* plugin)
98       {
99       QString _pluginPath = plugin->path;
100       QFileInfo np(_pluginPath);
101       if (np.suffix() != "qml")
102             return;
103       QString baseName = np.completeBaseName();
104 
105       bool found = false;
106       foreach(QString s, plugins) {
107             QFileInfo fi(s);
108             if (fi.completeBaseName() == baseName) {
109                   found = true;
110                   break;
111                   }
112             }
113       if (!found) {
114             if (MScore::debugMode)
115                   qDebug("  Plugin <%s> not registered", qPrintable(_pluginPath));
116             return;
117             }
118       plugins.removeAll(_pluginPath);
119 
120 
121       removeMenuEntry(plugin);
122       QAction* a = plugin->shortcut.action();
123       pluginActions.removeAll(a);
124 
125       disconnect(a, 0, mscore, 0);
126       }
127 
128 //---------------------------------------------------------
129 //   createMenuEntry
130 //    syntax: "entry.entry.entry"
131 //---------------------------------------------------------
132 
createMenuEntry(PluginDescription * plugin)133 void MuseScore::createMenuEntry(PluginDescription* plugin)
134       {
135       QString menu = plugin->menuPath;
136       QStringList ml;
137       QString s;
138       bool escape = false;
139       foreach (QChar c, menu) {
140             if (escape) {
141                   escape = false;
142                   s += c;
143                   }
144             else {
145                   if (c == '\\')
146                         escape = true;
147                   else {
148                         if (c == '.') {
149                               ml += s;
150                               s = "";
151                               }
152                         else {
153                               s += c;
154                               }
155                         }
156                   }
157             }
158       if (!s.isEmpty())
159             ml += s;
160 
161       int n            = ml.size();
162       QWidget* curMenu = menuBar();
163 
164       for(int i = 0; i < n; ++i) {
165             QString m  = ml[i];
166             bool found = false;
167             QList<QObject*> ol = curMenu->children();
168             foreach(QObject* o, ol) {
169                   QMenu* cmenu = qobject_cast<QMenu*>(o);
170                   if (!cmenu)
171                         continue;
172                   if (cmenu->objectName() == m || cmenu->title() == m) {
173                         curMenu = cmenu;
174                         found = true;
175                         break;
176                         }
177                   }
178             if (!found) {
179                   if (i == 0) {
180                         curMenu = new QMenu(m, menuBar());
181                         curMenu->setObjectName(m);
182                         menuBar()->insertMenu(menuBar()->actions().back(), (QMenu*)curMenu);
183                         if (MScore::debugMode)
184                               qDebug("add Menu <%s>", qPrintable(m));
185                         }
186                   else if (i + 1 == n) {
187                         QStringList sl = m.split(":");
188                         QAction* a = plugin->shortcut.action();
189                         QMenu* cm = static_cast<QMenu*>(curMenu);
190                         if (sl.size() == 2) {
191                               QList<QAction*> al = cm->actions();
192                               QAction* ba = 0;
193                               foreach(QAction* ia, al) {
194                                     if (ia->text() == sl[0]) {
195                                           ba = ia;
196                                           break;
197                                           }
198                                     }
199                               a->setText(sl[1]);
200                               cm->insertAction(ba, a);
201                               }
202                         else {
203                               a->setText(m);
204                               cm->addAction(a);
205                               }
206 
207                         if (MScore::debugMode)
208                               qDebug("plugins: add action <%s>", qPrintable(m));
209                         }
210                   else {
211                         curMenu = ((QMenu*)curMenu)->addMenu(m);
212                         if (MScore::debugMode)
213                               qDebug("add menu <%s>", qPrintable(m));
214                         }
215                   }
216             }
217       }
218 
219 //---------------------------------------------------------
220 //   addPluginMenuEntries
221 //---------------------------------------------------------
222 
addPluginMenuEntries()223 void MuseScore::addPluginMenuEntries()
224       {
225       for (int i = 0; i < pluginManager->pluginCount(); ++i) {
226             PluginDescription* d = pluginManager->getPluginDescription(i);
227             if (d->load)
228                   createMenuEntry(d);
229             }
230       }
231 
removeMenuEntry(PluginDescription * plugin)232 void MuseScore::removeMenuEntry(PluginDescription* plugin)
233       {
234       QString menu = plugin->menuPath;
235       QStringList ml;
236       QString s;
237       bool escape = false;
238       foreach (QChar c, menu) {
239             if (escape) {
240                   escape = false;
241                   s += c;
242                   }
243             else {
244                   if (c == '\\')
245                         escape = true;
246                   else {
247                         if (c == '.') {
248                               ml += s;
249                               s = "";
250                               }
251                         else {
252                               s += c;
253                               }
254                         }
255                   }
256             }
257       if (!s.isEmpty())
258             ml += s;
259 
260       if(ml.isEmpty())
261             return;
262 
263       int n            = ml.size();
264       QWidget* curMenu = menuBar();
265 
266       for(int i = 0; i < n-1; ++i) {
267             QString m  = ml[i];
268             QList<QObject*> ol = curMenu->children();
269             foreach(QObject* o, ol) {
270                   QMenu* cmenu = qobject_cast<QMenu*>(o);
271                   if (!cmenu)
272                         continue;
273                   if (cmenu->objectName() == m || cmenu->title() == m) {
274                         curMenu = cmenu;
275                         break;
276                         }
277                   }
278             }
279       QString m  = ml[n-1];
280       QStringList sl = m.split(":");
281       QAction* a = plugin->shortcut.action();
282       QMenu* cm = static_cast<QMenu*>(curMenu);
283       cm->removeAction(a);
284       for(int i = n-2; i >= 0; --i) {
285 
286             QMenu* cmenu = qobject_cast<QMenu*>(cm->parent());
287             if (cm->isEmpty())
288                   delete cm;
289 
290             cm = cmenu;
291             }
292       }
293 
294 //---------------------------------------------------------
295 //   pluginIdxFromPath
296 //---------------------------------------------------------
297 
pluginIdxFromPath(QString _pluginPath)298 int MuseScore::pluginIdxFromPath(QString _pluginPath) {
299       QFileInfo np(_pluginPath);
300       QString baseName = np.completeBaseName();
301       int idx = 0;
302       foreach(QString s, plugins) {
303             QFileInfo fi(s);
304             if (fi.completeBaseName() == baseName)
305                   return idx;
306             idx++;
307             }
308       return -1;
309       }
310 
311 //---------------------------------------------------------
312 //   loadPlugins
313 //---------------------------------------------------------
314 
loadPlugins()315 void MuseScore::loadPlugins()
316       {
317       for (int i = 0; i < pluginManager->pluginCount(); ++i) {
318             PluginDescription* d = pluginManager->getPluginDescription(i);
319             if (d->load)
320                   registerPlugin(d);
321             }
322       }
323 
324 //---------------------------------------------------------
325 //   unloadPlugins
326 //---------------------------------------------------------
327 
unloadPlugins()328 void MuseScore::unloadPlugins()
329       {
330       for (int idx = 0; idx < plugins.size() ; idx++) {
331             ; // TODO
332             }
333       }
334 
335 //---------------------------------------------------------
336 //   loadPlugin
337 //---------------------------------------------------------
338 
loadPlugin(const QString & filename)339 bool MuseScore::loadPlugin(const QString& filename)
340       {
341       bool result = false;
342 
343       QDir pluginDir(mscoreGlobalShare + "plugins");
344       if (MScore::debugMode)
345             qDebug("Plugin Path <%s>", qPrintable(mscoreGlobalShare + "plugins"));
346 
347       if (filename.endsWith(".qml")){
348             QFileInfo fi(pluginDir, filename);
349             if (!fi.exists())
350                   fi = QFileInfo(preferences.getString(PREF_APP_PATHS_MYPLUGINS), filename);
351             if (fi.exists()) {
352                   QString path(fi.filePath());
353                   PluginDescription* p = new PluginDescription;
354                   p->path = path;
355                   p->load = false;
356                   if (collectPluginMetaInformation(p))
357                         registerPlugin(p);
358                   result = true;
359                   }
360             }
361       return result;
362       }
363 
364 //---------------------------------------------------------
365 //   pluginTriggered
366 //---------------------------------------------------------
367 
pluginTriggered(int idx)368 void MuseScore::pluginTriggered(int idx)
369       {
370       if (plugins.size() > idx)
371             pluginTriggered(plugins[idx]);
372       }
373 
pluginTriggered(QString pp)374 void MuseScore::pluginTriggered(QString pp)
375       {
376       QmlPluginEngine* engine = getPluginEngine();
377 
378       QQmlComponent component(engine);
379       component.loadUrl(QUrl::fromLocalFile(pp));
380       QObject* obj = component.create();
381       if (obj == 0) {
382             foreach(QQmlError e, component.errors())
383                   qDebug("   line %d: %s", e.line(), qPrintable(e.description()));
384             return;
385             }
386 
387       QmlPlugin* p = qobject_cast<QmlPlugin*>(obj);
388       if(MuseScoreCore::mscoreCore->currentScore() == nullptr && p->requiresScore() == true) {
389             QMessageBox::information(0,
390                   QMessageBox::tr("MuseScore"),
391                   QMessageBox::tr("No score open.\n"
392                   "This plugin requires an open score to run.\n"),
393                   QMessageBox::Ok, QMessageBox::NoButton);
394             delete obj;
395             return;
396             }
397 
398       if (p->pluginType() == "dock" || p->pluginType() == "dialog") {
399             QQuickView* view = new QQuickView(engine, 0);
400             view->setSource(QUrl::fromLocalFile(pp));
401             if (QmlPlugin* viewPluginInstance = qobject_cast<QmlPlugin*>(view->rootObject())) {
402                   // a new plugin instance was created by view, use it instead.
403                   delete p;
404                   p = viewPluginInstance;
405                   }
406             view->setTitle(p->menuPath().mid(p->menuPath().lastIndexOf(".") + 1));
407             view->setColor(QApplication::palette().color(QPalette::Window));
408             //p->setParentItem(view->contentItem());
409             //view->setWidth(p->width());
410             //view->setHeight(p->height());
411             view->setResizeMode(QQuickView::SizeRootObjectToView);
412             if (p->pluginType() == "dock") {
413                   QDockWidget* dock = new QDockWidget(view->title(), 0);
414                   dock->setAttribute(Qt::WA_DeleteOnClose);
415                   Qt::DockWidgetArea area = Qt::RightDockWidgetArea;
416                   if (p->dockArea() == "left")
417                         area = Qt::LeftDockWidgetArea;
418                   else if (p->dockArea() == "top")
419                         area = Qt::TopDockWidgetArea;
420                   else if (p->dockArea() == "bottom")
421                         area = Qt::BottomDockWidgetArea;
422                   QWidget* w = QWidget::createWindowContainer(view);
423                   dock->setWidget(w);
424                   addDockWidget(area, dock);
425                   const Qt::Orientation orientation =
426                      (area == Qt::RightDockWidgetArea || area == Qt::LeftDockWidgetArea)
427                      ? Qt::Vertical
428                      : Qt::Horizontal;
429                   const int size = (orientation == Qt::Vertical) ? view->initialSize().height() : view->initialSize().width();
430                   resizeDocks({ dock }, { size }, orientation);
431                   connect(engine, SIGNAL(quit()), dock, SLOT(close()));
432                   dock->show();
433                   }
434             else {
435                   connect(engine, SIGNAL(quit()), view, SLOT(close()));
436                   view->show();
437                   }
438             }
439 
440       connect(engine, &QmlPluginEngine::endCmd, p, &QmlPlugin::endCmd);
441 
442       p->setFilePath(pp.section('/', 0, -2));
443 
444       // don’t call startCmd for non modal dialog
445       if (cs && p->pluginType() != "dock")
446             cs->startCmd();
447       p->runPlugin();
448       if (cs && p->pluginType() != "dock")
449             cs->endCmd();
450 //      endCmd();
451       }
452 
453 //---------------------------------------------------------
454 //   collectPluginMetaInformation
455 ///   returns false if loading a plugin for the given
456 ///   description has failed
457 //---------------------------------------------------------
458 
collectPluginMetaInformation(PluginDescription * d)459 bool collectPluginMetaInformation(PluginDescription* d)
460       {
461       qDebug("Collect meta for <%s>", qPrintable(d->path));
462 
463       QQmlComponent component(mscore->getPluginEngine(), QUrl::fromLocalFile(d->path));
464       QObject* obj = component.create();
465       if (obj == 0) {
466             qDebug("creating component <%s> failed", qPrintable(d->path));
467             foreach(QQmlError e, component.errors()) {
468                   qDebug("   line %d: %s", e.line(), qPrintable(e.description()));
469                   }
470             return false;
471             }
472       QmlPlugin* item = qobject_cast<QmlPlugin*>(obj);
473       const bool isQmlPlugin = bool(item);
474       if (item) {
475             d->version      = item->version();
476             d->description  = item->description();
477             }
478       delete obj;
479       return isQmlPlugin;
480       }
481 }
482 
483