xref: /OK3568_Linux_fs/app/qfm/mimeutils.cpp (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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