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