1 /*
2 * Cantata
3 *
4 * Copyright (c) 2011-2020 Craig Drummond <craig.p.drummond@gmail.com>
5 *
6 * ----
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; see the file COPYING. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24 #include "customactions.h"
25 #include "mainwindow.h"
26 #include "support/globalstatic.h"
27 #include "support/configuration.h"
28 #include <QMenu>
29 #include <QProcess>
30 #include <algorithm>
31
32 GLOBAL_STATIC(CustomActions, instance)
33
34 #include <QDebug>
35 static bool debugIsEnabled=false;
36 #define DBUG if (debugIsEnabled) qWarning() << "CustomActions" << __FUNCTION__
37
enableDebug()38 void CustomActions::enableDebug()
39 {
40 debugIsEnabled=true;
41 }
42
operator <(const Command & o) const43 bool CustomActions::Command::operator<(const Command &o) const
44 {
45 int c=name.localeAwareCompare(o.name);
46 if (c<0) {
47 return true;
48 }
49 if (c==0) {
50 return cmd.localeAwareCompare(o.cmd)<0;
51 }
52 return false;
53 }
54
CustomActions()55 CustomActions::CustomActions()
56 : Action(tr("Custom Actions"), nullptr)
57 , mainWindow(nullptr)
58 {
59 QMenu *m=new QMenu(nullptr);
60 setMenu(m);
61 Configuration cfg(metaObject()->className());
62 int count=cfg.get("count", 0);
63 for (int i=0; i<count; ++i) {
64 Command cmd(cfg.get(QString::number(i)+QLatin1String("_name"), QString()),
65 cfg.get(QString::number(i)+QLatin1String("_cmd"), QString()));
66 if (!cmd.name.isEmpty() && !cmd.cmd.isEmpty()) {
67 cmd.act=new Action(cmd.name, this);
68 m->addAction(cmd.act);
69 commands.append(cmd);
70 connect(cmd.act, SIGNAL(triggered()), this, SLOT(doAction()));
71 }
72 }
73 setVisible(!commands.isEmpty());
74 }
75
set(QList<Command> cmds)76 void CustomActions::set(QList<Command> cmds)
77 {
78 std::sort(cmds.begin(), cmds.end());
79 bool diff=cmds.length()!=commands.length();
80
81 if (!diff) {
82 for (int i=0; i<cmds.length() && !diff; ++i) {
83 if (commands[i]!=cmds[i]) {
84 diff=true;
85 }
86 }
87 }
88 QMenu *m=menu();
89 if (diff) {
90 for (const Command &cmd: commands) {
91 m->removeAction(cmd.act);
92 disconnect(cmd.act, SIGNAL(triggered()), this, SLOT(doAction()));
93 cmd.act->deleteLater();
94 }
95 commands.clear();
96
97 for (const Command &cmd: cmds) {
98 Command c(cmd);
99 c.act=new Action(c.name, this);
100 m->addAction(c.act);
101 commands.append(c);
102 connect(c.act, SIGNAL(triggered()), this, SLOT(doAction()));
103 }
104
105 Configuration cfg;
106 cfg.removeGroup(metaObject()->className());
107 if (!commands.isEmpty()) {
108 cfg.beginGroup(metaObject()->className());
109 cfg.set("count", commands.count());
110 for (int i=0; i<commands.count(); ++i) {
111 cfg.set(QString::number(i)+QLatin1String("_name"), commands[i].name);
112 cfg.set(QString::number(i)+QLatin1String("_cmd"), commands[i].cmd);
113 }
114 }
115 }
116
117 setVisible(!commands.isEmpty());
118 }
119
doAction()120 void CustomActions::doAction()
121 {
122 if (!mainWindow) {
123 DBUG << "No main window?";
124 return;
125 }
126 Action *act=qobject_cast<Action *>(sender());
127 if (!act) {
128 DBUG << "No action";
129 return;
130 }
131 QString mpdDir;
132 for (const Command &cmd: commands) {
133 if (cmd.act==act) {
134 QList<Song> songs=mainWindow->selectedSongs();
135 if (songs.isEmpty()) {
136 DBUG << "No selected songs?";
137 return;
138 }
139
140 if (!songs.at(0).isLocalFile()) {
141 if (!MPDConnection::self()->getDetails().dirReadable) {
142 DBUG << "MPD dir is not readable";
143 return;
144 }
145 mpdDir=MPDConnection::self()->getDetails().dir;
146 }
147 QStringList items;
148 if (cmd.cmd.contains("%d")) {
149 QSet<QString> used;
150 for (const Song &s: songs) {
151 if (Song::Playlist!=s.type) {
152 QString dir=Utils::getDir(s.file);
153 if (!used.contains(dir)) {
154 used.insert(dir);
155 items.append(mpdDir+dir);
156 }
157 }
158 }
159 } else {
160 for (const Song &s: songs) {
161 if (Song::Playlist!=s.type) {
162 items.append(mpdDir+s.file);
163 }
164 }
165 }
166
167 if (!items.isEmpty()) {
168 QStringList parts=cmd.cmd.split(' ');
169 bool added=false;
170 QString cmd=parts.takeFirst();
171 QStringList args;
172 for (const QString &part: parts) {
173 if (part.startsWith('%')) {
174 args+=items;
175 added=true;
176 } else {
177 args+=part;
178 }
179 }
180 if (!added) {
181 args+=items;
182 }
183 DBUG << "Start" << cmd << args;
184 QProcess::startDetached(cmd, args);
185 }
186 return;
187 }
188 }
189 DBUG << "Command not found";
190 }
191
192 #include "moc_customactions.cpp"
193