1 #include "mimeutils.h"
2 #include <QApplication>
3 #include <QDir>
4 #include <QDirIterator>
5 #include <QProcess>
6 #include <QDebug>
7 #include <QMessageBox>
8 #include <QMimeDatabase>
9 #include <QMimeType>
10
11 #define MIME_APPS1 "/.local/share/applications/mimeapps.list"
12 #define MIME_APPS2 "/usr/share/applications/mimeapps.list"
13 /**
14 * @brief Creates mime utils
15 * @param parent
16 */
MimeUtils(QObject * parent)17 MimeUtils::MimeUtils(QObject *parent) : QObject(parent) {
18 QString path(QDir::homePath() + MIME_APPS1);
19 QFileInfo fi(path);
20 if(fi.exists()){
21 defaultsFileName = path;
22 } else {
23 QFileInfo ff(MIME_APPS2);
24 if(ff.exists())
25 defaultsFileName = MIME_APPS2;
26 }
27
28 getProperties();
29 load(defaultsFileName, "Default Applications");
30 defaultsChanged = false;
31 }
32
~MimeUtils()33 MimeUtils::~MimeUtils() {
34 }
35 //---------------------------------------------------------------------------
36
37 /**
38 * @brief Loads property file
39 * @param fileName
40 * @param group
41 * @return true if load was successful
42 */
load(const QString & fileName,const QString & group)43 bool MimeUtils::load(const QString &fileName, const QString &group) {
44
45 // NOTE: This class is used for reading of property files instead of QSettings
46 // class, which considers separator ';' as comment
47
48 // Try open file
49 QFile file(fileName);
50 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
51 return false;
52 }
53
54 // Clear old data
55 data.clear();
56
57 // Indicator whether group was found or not, if name of group was not
58 // specified, groupFound is always true
59 bool groupFound = group.isEmpty();
60
61 // Read propeties
62 QTextStream in(&file);
63 while (!in.atEnd()) {
64
65 // Read new line
66 QString line = in.readLine();
67
68 // Skip empty line or line with invalid format
69 if (line.trimmed().isEmpty()) {
70 continue;
71 }
72
73 // Read group
74 // NOTE: symbols '[' and ']' can be found not only in group names, but
75 // only group can start with '['
76 if (!group.isEmpty() && line.trimmed().startsWith("[")) {
77 QString tmp = line.trimmed().replace("[", "").replace("]", "");
78 groupFound = group.trimmed().compare(tmp) == 0;
79 }
80
81 // If we are in correct group and line contains assignment then read data
82 if (groupFound && line.contains("=")) {
83 QStringList tmp = line.split("=");
84 data.insert(tmp.at(0), tmp.at(1));
85 }
86 }
87 file.close();
88 return true;
89 }
90
value(const QString & key,const QVariant & defaultValue)91 QVariant MimeUtils::value(const QString &key, const QVariant &defaultValue) {
92 return data.value(key, defaultValue);
93 }
94
getProperties(const QString & fileName,const QString & group)95 void MimeUtils::getProperties(const QString &fileName, const QString &group) {
96 if (!fileName.isEmpty()) {
97 load(fileName, group);
98 }
99 }
100
101 /**
102 * @brief Returns mime type of given file
103 * @note This operation is slow, prevent its mass application
104 * @param path path to file
105 * @return mime type
106 */
getMimeType(const QString & path)107 QString MimeUtils::getMimeType(const QString &path) {
108 QMimeDatabase db;
109 QMimeType type = db.mimeTypeForFile(path);
110 //qDebug() << "mime type" << type.name() << path;
111 return type.name();
112 }
113
114
getDesktopFile(const QString & fileName)115 void MimeUtils::getDesktopFile(const QString &fileName) {
116
117 // Store file name
118 this->fileName = fileName;
119
120 // File validity
121 if (!QFile::exists(fileName)) {
122 return;
123 }
124
125 // Loads .desktop file (read from 'Desktop Entry' group)
126 getProperties(fileName, "Desktop Entry");
127 name = value("Name", "").toString();
128 genericName = value("GenericName", "").toString();
129 exec = value("Exec", "").toString();
130 icon = value("Icon", "").toString();
131 type = value("Type", "Application").toString();
132 no_display = value("NoDisplay", false).toBool();
133 terminal = value("Terminal", false).toBool();
134 categories = value("Categories").toString().remove(" ").split(";");
135 mimeType = value("MimeType").toString().remove(" ").split(";");
136
137 // Fix categories
138 if (categories.first().compare("") == 0) {
139 categories.removeFirst();
140 }
141 }
142 //---------------------------------------------------------------------------
143
applicationLocations(QString appPath)144 QStringList MimeUtils::applicationLocations(QString appPath)
145 {
146 QStringList result;
147 result << QString("%1/.local/share/applications").arg(QDir::homePath());
148 result << QString("%1/../share/applications").arg(appPath);
149 result << "/usr/share/applications" << "/usr/local/share/applications";
150 return result;
151 }
152
findApplication(QString appPath,QString desktopFile)153 QString MimeUtils::findApplication(QString appPath, QString desktopFile)
154 {
155 QString result;
156 if (desktopFile.isEmpty()) { return result; }
157 QStringList apps = applicationLocations(appPath);
158 for (int i=0;i<apps.size();++i) {
159 QDirIterator it(apps.at(i), QStringList("*.desktop"), QDir::Files|QDir::NoDotAndDotDot);
160 while (it.hasNext()) {
161 QString found = it.next();
162 if (found.split("/").takeLast()==desktopFile) {
163 //qDebug() << "found app" << found;
164 return found;
165 }
166 }
167 }
168 return result;
169 }
170
checkAndKillRunningApp(QString & appName)171 void MimeUtils::checkAndKillRunningApp(QString &appName)
172 {
173 QProcess p;
174 QString s;
175
176 s = "ps -aux";
177 p.start(s);
178 p.waitForStarted();
179 p.waitForFinished();
180 QByteArray b = p.readAll();
181 QString out = QString::fromLocal8Bit(b);
182 if(out.contains(appName)){
183 QString str = appName.mid(appName.lastIndexOf('/') + 1, s.size());
184 s = "killall " + str;
185 qDebug() << "killing existing app: " + str;
186 p.start(s);
187 p.waitForFinished();
188 }
189 }
190
191 /**
192 * @brief Opens file in a default application
193 * @param file
194 * @param processOwner
195 */
openInApp(const QFileInfo & file,QString termCmd)196 void MimeUtils::openInApp(const QFileInfo &file, QString termCmd) {
197 qDebug() << "openInApp without app";
198 QString mime = getMimeType(file.absoluteFilePath());
199 load(defaultsFileName, "Default Applications");
200 QString app = value(mime).toString().split(";").first();
201 if (app.isEmpty() && mime.startsWith("text/") && mime != "text/plain") {
202 // fallback for text
203 app = value("text/plain").toString().split(";").first();
204 }
205 QString desktop = findApplication(qApp->applicationFilePath(), app);
206 qDebug() << "openInApp" << file.absoluteFilePath() << termCmd << mime << app << desktop;
207 if (!desktop.isEmpty()) {
208 getDesktopFile(desktop);
209 if (!terminal) { termCmd.clear(); }
210 else {
211 if (termCmd.isEmpty()) { termCmd = "xterm"; }
212 }
213 openInApp(exec, file, termCmd);
214 } else {
215 QString title = tr("No default application");
216 QString msg = tr("No default application for mime: %1!").arg(mime);
217 QMessageBox::warning(Q_NULLPTR, title, msg);
218 }
219 }
220 //---------------------------------------------------------------------------
221
222 /**
223 * @brief Opens file in a given application
224 * @param exe name of application to be executed
225 * @param file to be opened in executed application
226 * @param processOwner process owner (default NULL)
227 */
openInApp(QString exe,const QFileInfo & file,QString termCmd)228 void MimeUtils::openInApp(QString exe, const QFileInfo &file,
229 QString termCmd) {
230
231 qDebug() << "openInApp" << exe << file.absoluteFilePath() << termCmd;
232
233 // This is not the right the solution, but qpdfview won't start otherwise
234 // TODO: Repair it correctly
235 if (exe.contains("qpdfview")) {
236 exe = "qpdfview";
237 }
238
239 // Separate application name from its arguments
240 QStringList split = exe.split(" ");
241 QString name = split.takeAt(0);
242 QString args = split.join(" ");
243
244 // Get relative path
245 //args = args.split(QDir::separator()).last();
246
247 // Replace parameters with file name. If there are no parameters simply append
248 // file name to the end of argument list
249 if (args.toLower().contains("%f")) {
250 args.replace("%f", "\"" + file.filePath() + "\"", Qt::CaseInsensitive);
251 } else if (args.toLower().contains("%u")) {
252 args.replace("%u", "\"" + file.filePath() + "\"", Qt::CaseInsensitive);
253 } else {
254 args.append(args.isEmpty() ? "" : " ");
255 args.append("\"" + file.filePath() + "\"");
256 }
257
258 qDebug() << "qprocess start detached" << name << args;
259 checkAndKillRunningApp(name);
260 // Start application
261 /* QProcess *myProcess = new QProcess(processOwner);
262 myProcess->startDetached(name, QStringList() << args);
263 myProcess->waitForFinished(1000);
264 //myProcess->terminate();*/
265 //Q_UNUSED(processOwner)
266 QString cmd = name;
267 if (termCmd.isEmpty()) {
268 cmd.append(" ");
269 cmd.append(args);
270 } else {
271 cmd = QString("%1 -e \"%2 %3\"").arg(termCmd).arg(name).arg(args);
272 }
273 qDebug() << "running:" << cmd;
274 QProcess::startDetached(cmd);
275 }
276
openFiles(const QStringList & files)277 void MimeUtils::openFiles(const QStringList &files) {
278 qDebug() << "openInApp without app";
279
280 QString mime = getMimeType(files[0]);
281 load(defaultsFileName, "Default Applications");
282 QString app = value(mime).toString().split(";").first();
283 QString desktop = findApplication(qApp->applicationFilePath(), app);
284 QString file;
285 for(int i = 0; i < files.size(); i++){
286 file.append(files[i]).append(" ");
287 }
288 qDebug() << "openInApp" << file << mime << app << desktop;
289 if (!desktop.isEmpty()) {
290 getDesktopFile(desktop);
291 // openInApp(exec, file, termCmd);
292 qDebug() << "openInApp" << exec << file;
293 // Separate application name from its arguments
294 QStringList split = exec.split(" ");
295 QString name = split.takeAt(0);
296 QString args = split.join(" ");
297
298 if (args.toLower().contains("%f")) {
299 args.replace("%f", file, Qt::CaseInsensitive);
300 } else if (args.toLower().contains("%u")) {
301 args.replace("%u", file, Qt::CaseInsensitive);
302 } else {
303 args.append(args.isEmpty() ? "" : " ");
304 args.append(file);
305 }
306 qDebug() << "qprocess start detached" << name << args;
307 checkAndKillRunningApp(name);
308 QString cmd = name;
309 cmd.append(" ");
310 cmd.append(args);
311 qDebug() << "running:" << cmd;
312 QProcess::startDetached(cmd);
313 } else {
314 QString title = tr("No default application");
315 QString msg = tr("No default application for mime: %1!").arg(mime);
316 QMessageBox::warning(Q_NULLPTR, title, msg);
317 }
318 }
319