new/app/qfm/mimeutils.cpp
2025-05-10 21:58:58 +08:00

319 lines
9.5 KiB
C++

#include "mimeutils.h"
#include <QApplication>
#include <QDir>
#include <QDirIterator>
#include <QProcess>
#include <QDebug>
#include <QMessageBox>
#include <QMimeDatabase>
#include <QMimeType>
#define MIME_APPS1 "/.local/share/applications/mimeapps.list"
#define MIME_APPS2 "/usr/share/applications/mimeapps.list"
/**
* @brief Creates mime utils
* @param parent
*/
MimeUtils::MimeUtils(QObject *parent) : QObject(parent) {
QString path(QDir::homePath() + MIME_APPS1);
QFileInfo fi(path);
if(fi.exists()){
defaultsFileName = path;
} else {
QFileInfo ff(MIME_APPS2);
if(ff.exists())
defaultsFileName = MIME_APPS2;
}
getProperties();
load(defaultsFileName, "Default Applications");
defaultsChanged = false;
}
MimeUtils::~MimeUtils() {
}
//---------------------------------------------------------------------------
/**
* @brief Loads property file
* @param fileName
* @param group
* @return true if load was successful
*/
bool MimeUtils::load(const QString &fileName, const QString &group) {
// NOTE: This class is used for reading of property files instead of QSettings
// class, which considers separator ';' as comment
// Try open file
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
return false;
}
// Clear old data
data.clear();
// Indicator whether group was found or not, if name of group was not
// specified, groupFound is always true
bool groupFound = group.isEmpty();
// Read propeties
QTextStream in(&file);
while (!in.atEnd()) {
// Read new line
QString line = in.readLine();
// Skip empty line or line with invalid format
if (line.trimmed().isEmpty()) {
continue;
}
// Read group
// NOTE: symbols '[' and ']' can be found not only in group names, but
// only group can start with '['
if (!group.isEmpty() && line.trimmed().startsWith("[")) {
QString tmp = line.trimmed().replace("[", "").replace("]", "");
groupFound = group.trimmed().compare(tmp) == 0;
}
// If we are in correct group and line contains assignment then read data
if (groupFound && line.contains("=")) {
QStringList tmp = line.split("=");
data.insert(tmp.at(0), tmp.at(1));
}
}
file.close();
return true;
}
QVariant MimeUtils::value(const QString &key, const QVariant &defaultValue) {
return data.value(key, defaultValue);
}
void MimeUtils::getProperties(const QString &fileName, const QString &group) {
if (!fileName.isEmpty()) {
load(fileName, group);
}
}
/**
* @brief Returns mime type of given file
* @note This operation is slow, prevent its mass application
* @param path path to file
* @return mime type
*/
QString MimeUtils::getMimeType(const QString &path) {
QMimeDatabase db;
QMimeType type = db.mimeTypeForFile(path);
//qDebug() << "mime type" << type.name() << path;
return type.name();
}
void MimeUtils::getDesktopFile(const QString &fileName) {
// Store file name
this->fileName = fileName;
// File validity
if (!QFile::exists(fileName)) {
return;
}
// Loads .desktop file (read from 'Desktop Entry' group)
getProperties(fileName, "Desktop Entry");
name = value("Name", "").toString();
genericName = value("GenericName", "").toString();
exec = value("Exec", "").toString();
icon = value("Icon", "").toString();
type = value("Type", "Application").toString();
no_display = value("NoDisplay", false).toBool();
terminal = value("Terminal", false).toBool();
categories = value("Categories").toString().remove(" ").split(";");
mimeType = value("MimeType").toString().remove(" ").split(";");
// Fix categories
if (categories.first().compare("") == 0) {
categories.removeFirst();
}
}
//---------------------------------------------------------------------------
QStringList MimeUtils::applicationLocations(QString appPath)
{
QStringList result;
result << QString("%1/.local/share/applications").arg(QDir::homePath());
result << QString("%1/../share/applications").arg(appPath);
result << "/usr/share/applications" << "/usr/local/share/applications";
return result;
}
QString MimeUtils::findApplication(QString appPath, QString desktopFile)
{
QString result;
if (desktopFile.isEmpty()) { return result; }
QStringList apps = applicationLocations(appPath);
for (int i=0;i<apps.size();++i) {
QDirIterator it(apps.at(i), QStringList("*.desktop"), QDir::Files|QDir::NoDotAndDotDot);
while (it.hasNext()) {
QString found = it.next();
if (found.split("/").takeLast()==desktopFile) {
//qDebug() << "found app" << found;
return found;
}
}
}
return result;
}
void MimeUtils::checkAndKillRunningApp(QString &appName)
{
QProcess p;
QString s;
s = "ps -aux";
p.start(s);
p.waitForStarted();
p.waitForFinished();
QByteArray b = p.readAll();
QString out = QString::fromLocal8Bit(b);
if(out.contains(appName)){
QString str = appName.mid(appName.lastIndexOf('/') + 1, s.size());
s = "killall " + str;
qDebug() << "killing existing app: " + str;
p.start(s);
p.waitForFinished();
}
}
/**
* @brief Opens file in a default application
* @param file
* @param processOwner
*/
void MimeUtils::openInApp(const QFileInfo &file, QString termCmd) {
qDebug() << "openInApp without app";
QString mime = getMimeType(file.absoluteFilePath());
load(defaultsFileName, "Default Applications");
QString app = value(mime).toString().split(";").first();
if (app.isEmpty() && mime.startsWith("text/") && mime != "text/plain") {
// fallback for text
app = value("text/plain").toString().split(";").first();
}
QString desktop = findApplication(qApp->applicationFilePath(), app);
qDebug() << "openInApp" << file.absoluteFilePath() << termCmd << mime << app << desktop;
if (!desktop.isEmpty()) {
getDesktopFile(desktop);
if (!terminal) { termCmd.clear(); }
else {
if (termCmd.isEmpty()) { termCmd = "xterm"; }
}
openInApp(exec, file, termCmd);
} else {
QString title = tr("No default application");
QString msg = tr("No default application for mime: %1!").arg(mime);
QMessageBox::warning(Q_NULLPTR, title, msg);
}
}
//---------------------------------------------------------------------------
/**
* @brief Opens file in a given application
* @param exe name of application to be executed
* @param file to be opened in executed application
* @param processOwner process owner (default NULL)
*/
void MimeUtils::openInApp(QString exe, const QFileInfo &file,
QString termCmd) {
qDebug() << "openInApp" << exe << file.absoluteFilePath() << termCmd;
// This is not the right the solution, but qpdfview won't start otherwise
// TODO: Repair it correctly
if (exe.contains("qpdfview")) {
exe = "qpdfview";
}
// Separate application name from its arguments
QStringList split = exe.split(" ");
QString name = split.takeAt(0);
QString args = split.join(" ");
// Get relative path
//args = args.split(QDir::separator()).last();
// Replace parameters with file name. If there are no parameters simply append
// file name to the end of argument list
if (args.toLower().contains("%f")) {
args.replace("%f", "\"" + file.filePath() + "\"", Qt::CaseInsensitive);
} else if (args.toLower().contains("%u")) {
args.replace("%u", "\"" + file.filePath() + "\"", Qt::CaseInsensitive);
} else {
args.append(args.isEmpty() ? "" : " ");
args.append("\"" + file.filePath() + "\"");
}
qDebug() << "qprocess start detached" << name << args;
checkAndKillRunningApp(name);
// Start application
/* QProcess *myProcess = new QProcess(processOwner);
myProcess->startDetached(name, QStringList() << args);
myProcess->waitForFinished(1000);
//myProcess->terminate();*/
//Q_UNUSED(processOwner)
QString cmd = name;
if (termCmd.isEmpty()) {
cmd.append(" ");
cmd.append(args);
} else {
cmd = QString("%1 -e \"%2 %3\"").arg(termCmd).arg(name).arg(args);
}
qDebug() << "running:" << cmd;
QProcess::startDetached(cmd);
}
void MimeUtils::openFiles(const QStringList &files) {
qDebug() << "openInApp without app";
QString mime = getMimeType(files[0]);
load(defaultsFileName, "Default Applications");
QString app = value(mime).toString().split(";").first();
QString desktop = findApplication(qApp->applicationFilePath(), app);
QString file;
for(int i = 0; i < files.size(); i++){
file.append(files[i]).append(" ");
}
qDebug() << "openInApp" << file << mime << app << desktop;
if (!desktop.isEmpty()) {
getDesktopFile(desktop);
// openInApp(exec, file, termCmd);
qDebug() << "openInApp" << exec << file;
// Separate application name from its arguments
QStringList split = exec.split(" ");
QString name = split.takeAt(0);
QString args = split.join(" ");
if (args.toLower().contains("%f")) {
args.replace("%f", file, Qt::CaseInsensitive);
} else if (args.toLower().contains("%u")) {
args.replace("%u", file, Qt::CaseInsensitive);
} else {
args.append(args.isEmpty() ? "" : " ");
args.append(file);
}
qDebug() << "qprocess start detached" << name << args;
checkAndKillRunningApp(name);
QString cmd = name;
cmd.append(" ");
cmd.append(args);
qDebug() << "running:" << cmd;
QProcess::startDetached(cmd);
} else {
QString title = tr("No default application");
QString msg = tr("No default application for mime: %1!").arg(mime);
QMessageBox::warning(Q_NULLPTR, title, msg);
}
}