mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 14:17:12 +02:00
Platform-dependent file methods called async.
Some major platform-dependent file operations refactoring. All methods like "open file", "open file with", "show in folder" were moved to core/file_utilities module with platform-dependent backends. All methods interacting with DesktopServices made async.
This commit is contained in:
parent
6f0cf30b12
commit
f8318177b9
56 changed files with 1254 additions and 1088 deletions
|
@ -28,7 +28,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "mainwindow.h"
|
||||
#include "lang.h"
|
||||
#include "boxes/confirmbox.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "ui/widgets/tooltip.h"
|
||||
#include "langloaderplain.h"
|
||||
#include "localstorage.h"
|
||||
|
|
|
@ -28,7 +28,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "boxes/contactsbox.h"
|
||||
#include "boxes/confirmbox.h"
|
||||
#include "boxes/photocropbox.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
|
|
|
@ -21,7 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#pragma once
|
||||
|
||||
#include "boxes/abstractbox.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
|
||||
class ConfirmBox;
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "messenger.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "ui/widgets/multi_select.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "ui/effects/widget_slide_wrap.h"
|
||||
|
|
|
@ -23,7 +23,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
|
||||
#include "lang.h"
|
||||
#include "localstorage.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "pspecific.h"
|
||||
|
@ -100,19 +100,19 @@ void DownloadPathBox::onChange() {
|
|||
}
|
||||
|
||||
void DownloadPathBox::onEditPath() {
|
||||
filedialogInit();
|
||||
QString path, lastPath = cDialogLastPath();
|
||||
if (!Global::DownloadPath().isEmpty() && Global::DownloadPath() != qstr("tmp")) {
|
||||
cSetDialogLastPath(Global::DownloadPath().left(Global::DownloadPath().size() - (Global::DownloadPath().endsWith('/') ? 1 : 0)));
|
||||
}
|
||||
if (filedialogGetDir(path, lang(lng_download_path_choose))) {
|
||||
if (!path.isEmpty()) {
|
||||
_path = path + '/';
|
||||
auto initialPath = [] {
|
||||
if (!Global::DownloadPath().isEmpty() && Global::DownloadPath() != qstr("tmp")) {
|
||||
return Global::DownloadPath().left(Global::DownloadPath().size() - (Global::DownloadPath().endsWith('/') ? 1 : 0));
|
||||
}
|
||||
return QString();
|
||||
};
|
||||
FileDialog::GetFolder(lang(lng_download_path_choose), initialPath(), base::lambda_guarded(this, [this](const QString &result) {
|
||||
if (!result.isEmpty()) {
|
||||
_path = result + '/';
|
||||
_pathBookmark = psDownloadPathBookmark(_path);
|
||||
setPathText(QDir::toNativeSeparators(_path));
|
||||
}
|
||||
}
|
||||
cSetDialogLastPath(lastPath);
|
||||
}));
|
||||
}
|
||||
|
||||
void DownloadPathBox::save() {
|
||||
|
|
|
@ -25,7 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "localstorage.h"
|
||||
#include "mainwidget.h"
|
||||
#include "history/history_media_types.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
|
|
|
@ -28,6 +28,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "core/qthelp_url.h"
|
||||
#include "localstorage.h"
|
||||
#include "ui/widgets/tooltip.h"
|
||||
#include "core/file_utilities.h"
|
||||
|
||||
QString UrlClickHandler::copyToClipboardContextItemText() const {
|
||||
return lang(isEmail() ? lng_context_copy_email : lng_context_copy_link);
|
||||
|
@ -68,10 +69,7 @@ void UrlClickHandler::doOpen(QString url) {
|
|||
Ui::Tooltip::Hide();
|
||||
|
||||
if (isEmail(url)) {
|
||||
QUrl u(qstr("mailto:") + url);
|
||||
if (!QDesktopServices::openUrl(u)) {
|
||||
psOpenFile(u.toString(QUrl::FullyEncoded), true);
|
||||
}
|
||||
File::OpenEmailLink(url);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,70 +19,25 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
|||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "stdafx.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
|
||||
#include "mainwindow.h"
|
||||
#include "localstorage.h"
|
||||
#include "platform/platform_file_utilities.h"
|
||||
|
||||
#include "core/task_queue.h"
|
||||
|
||||
void filedialogInit() {
|
||||
if (cDialogLastPath().isEmpty()) {
|
||||
#ifdef Q_OS_WIN
|
||||
// hack to restore previous dir without hurting performance
|
||||
QSettings settings(QSettings::UserScope, qstr("QtProject"));
|
||||
settings.beginGroup(qstr("Qt"));
|
||||
QByteArray sd = settings.value(qstr("filedialog")).toByteArray();
|
||||
QDataStream stream(&sd, QIODevice::ReadOnly);
|
||||
if (!stream.atEnd()) {
|
||||
int version = 3, _QFileDialogMagic = 190;
|
||||
QByteArray splitterState;
|
||||
QByteArray headerData;
|
||||
QList<QUrl> bookmarks;
|
||||
QStringList history;
|
||||
QString currentDirectory;
|
||||
qint32 marker;
|
||||
qint32 v;
|
||||
qint32 viewMode;
|
||||
stream >> marker;
|
||||
stream >> v;
|
||||
if (marker == _QFileDialogMagic && v == version) {
|
||||
stream >> splitterState
|
||||
>> bookmarks
|
||||
>> history
|
||||
>> currentDirectory
|
||||
>> headerData
|
||||
>> viewMode;
|
||||
cSetDialogLastPath(currentDirectory);
|
||||
}
|
||||
}
|
||||
if (cDialogHelperPath().isEmpty()) {
|
||||
QDir temppath(cWorkingDir() + "tdata/tdummy/");
|
||||
if (!temppath.exists()) {
|
||||
temppath.mkpath(temppath.absolutePath());
|
||||
}
|
||||
if (temppath.exists()) {
|
||||
cSetDialogHelperPath(temppath.absolutePath());
|
||||
}
|
||||
}
|
||||
#else // Q_OS_WIN
|
||||
cSetDialogLastPath(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
|
||||
#endif // Q_OS_WIN
|
||||
}
|
||||
}
|
||||
|
||||
namespace FileDialog {
|
||||
namespace internal {
|
||||
|
||||
bool getFiles(QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter, FileDialog::internal::Type type, QString startFile = QString()) {
|
||||
filedialogInit();
|
||||
void InitLastPathDefault() {
|
||||
cSetDialogLastPath(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
|
||||
}
|
||||
|
||||
if (Platform::FileDialog::Supported()) {
|
||||
return Platform::FileDialog::Get(files, remoteContent, caption, filter, type, startFile);
|
||||
bool GetDefault(QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter, FileDialog::internal::Type type, QString startFile = QString()) {
|
||||
if (cDialogLastPath().isEmpty()) {
|
||||
Platform::FileDialog::InitLastPath();
|
||||
}
|
||||
|
||||
#if defined Q_OS_LINUX || defined Q_OS_MAC // use native
|
||||
remoteContent = QByteArray();
|
||||
if (startFile.isEmpty() || startFile.at(0) != '/') {
|
||||
startFile = cDialogLastPath() + '/' + startFile;
|
||||
|
@ -107,122 +62,56 @@ bool getFiles(QStringList &files, QByteArray &remoteContent, const QString &capt
|
|||
files = QStringList();
|
||||
return false;
|
||||
}
|
||||
QString path = QFileInfo(file).absoluteDir().absolutePath();
|
||||
if (!path.isEmpty() && path != cDialogLastPath()) {
|
||||
cSetDialogLastPath(path);
|
||||
Local::writeUserSettings();
|
||||
if (type != Type::ReadFolder) {
|
||||
// Save last used directory for all queries except directory choosing.
|
||||
auto path = QFileInfo(file).absoluteDir().absolutePath();
|
||||
if (!path.isEmpty() && path != cDialogLastPath()) {
|
||||
cSetDialogLastPath(path);
|
||||
Local::writeUserSettings();
|
||||
}
|
||||
}
|
||||
files = QStringList(file);
|
||||
return true;
|
||||
#else // Q_OS_LINUX || Q_OS_MAC
|
||||
|
||||
// A hack for fast dialog create. There was some huge performance problem
|
||||
// if we open a file dialog in some folder with a large amount of files.
|
||||
// Some internal Qt watcher iterated over all of them, querying some information
|
||||
// that forced file icon and maybe other properties being resolved and this was
|
||||
// a blocking operation.
|
||||
auto helperPath = cDialogHelperPathFinal();
|
||||
QFileDialog dialog(App::wnd() ? App::wnd()->filedialogParent() : 0, caption, helperPath, filter);
|
||||
|
||||
dialog.setModal(true);
|
||||
if (type == Type::ReadFile || type == Type::ReadFiles) {
|
||||
dialog.setFileMode((type == Type::ReadFiles) ? QFileDialog::ExistingFiles : QFileDialog::ExistingFile);
|
||||
dialog.setAcceptMode(QFileDialog::AcceptOpen);
|
||||
} else if (type == Type::ReadFolder) { // save dir
|
||||
dialog.setAcceptMode(QFileDialog::AcceptOpen);
|
||||
|
||||
// We use "obsolete" value ::DirectoryOnly instead of ::Directory + ::ShowDirsOnly
|
||||
// because in Windows XP native dialog this one works, while the "preferred" one
|
||||
// shows a native file choose dialog where you can't choose a directory, only open one.
|
||||
dialog.setFileMode(QFileDialog::DirectoryOnly);
|
||||
dialog.setOption(QFileDialog::ShowDirsOnly);
|
||||
} else { // save file
|
||||
dialog.setFileMode(QFileDialog::AnyFile);
|
||||
dialog.setAcceptMode(QFileDialog::AcceptSave);
|
||||
}
|
||||
dialog.show();
|
||||
|
||||
auto realLastPath = cDialogLastPath();
|
||||
if (realLastPath.isEmpty() || realLastPath.endsWith(qstr("/tdummy"))) {
|
||||
realLastPath = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
|
||||
}
|
||||
dialog.setDirectory(realLastPath);
|
||||
|
||||
if (type == Type::WriteFile) {
|
||||
QString toSelect(startFile);
|
||||
#ifdef Q_OS_WIN
|
||||
int32 lastSlash = toSelect.lastIndexOf('/');
|
||||
if (lastSlash >= 0) {
|
||||
toSelect = toSelect.mid(lastSlash + 1);
|
||||
}
|
||||
int32 lastBackSlash = toSelect.lastIndexOf('\\');
|
||||
if (lastBackSlash >= 0) {
|
||||
toSelect = toSelect.mid(lastBackSlash + 1);
|
||||
}
|
||||
#endif
|
||||
dialog.selectFile(toSelect);
|
||||
}
|
||||
|
||||
int res = dialog.exec();
|
||||
|
||||
QString path = dialog.directory().absolutePath();
|
||||
if (path != cDialogLastPath()) {
|
||||
cSetDialogLastPath(path);
|
||||
Local::writeUserSettings();
|
||||
}
|
||||
|
||||
if (res == QDialog::Accepted) {
|
||||
if (type == Type::ReadFiles) {
|
||||
files = dialog.selectedFiles();
|
||||
} else {
|
||||
files = dialog.selectedFiles().mid(0, 1);
|
||||
}
|
||||
if (type == Type::ReadFile || type == Type::ReadFiles) {
|
||||
#ifdef Q_OS_WIN
|
||||
remoteContent = dialog.selectedRemoteContent();
|
||||
#endif // Q_OS_WIN
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
files = QStringList();
|
||||
remoteContent = QByteArray();
|
||||
return false;
|
||||
#endif // Q_OS_WIN
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace FileDialog
|
||||
|
||||
bool filedialogGetOpenFiles(QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter) {
|
||||
return FileDialog::internal::getFiles(files, remoteContent, caption, filter, FileDialog::internal::Type::ReadFiles);
|
||||
return Platform::FileDialog::Get(files, remoteContent, caption, filter, FileDialog::internal::Type::ReadFiles);
|
||||
}
|
||||
|
||||
bool filedialogGetOpenFile(QString &file, QByteArray &remoteContent, const QString &caption, const QString &filter) {
|
||||
QStringList files;
|
||||
bool result = FileDialog::internal::getFiles(files, remoteContent, caption, filter, FileDialog::internal::Type::ReadFile);
|
||||
bool result = Platform::FileDialog::Get(files, remoteContent, caption, filter, FileDialog::internal::Type::ReadFile);
|
||||
file = files.isEmpty() ? QString() : files.at(0);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool filedialogGetSaveFile(QString &file, const QString &caption, const QString &filter, const QString &startName) {
|
||||
bool filedialogGetSaveFile(QString &file, const QString &caption, const QString &filter, const QString &initialPath) {
|
||||
QStringList files;
|
||||
QByteArray remoteContent;
|
||||
bool result = FileDialog::internal::getFiles(files, remoteContent, caption, filter, FileDialog::internal::Type::WriteFile, startName);
|
||||
bool result = Platform::FileDialog::Get(files, remoteContent, caption, filter, FileDialog::internal::Type::WriteFile, initialPath);
|
||||
file = files.isEmpty() ? QString() : files.at(0);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool filedialogGetDir(QString &dir, const QString &caption) {
|
||||
bool filedialogGetDir(QString &dir, const QString &caption, const QString &initialPath) {
|
||||
QStringList files;
|
||||
QByteArray remoteContent;
|
||||
bool result = FileDialog::internal::getFiles(files, remoteContent, caption, QString(), FileDialog::internal::Type::ReadFolder);
|
||||
bool result = Platform::FileDialog::Get(files, remoteContent, caption, QString(), FileDialog::internal::Type::ReadFolder, initialPath);
|
||||
dir = files.isEmpty() ? QString() : files.at(0);
|
||||
return result;
|
||||
}
|
||||
|
||||
QString filedialogDefaultName(const QString &prefix, const QString &extension, const QString &path, bool skipExistance) {
|
||||
filedialogInit();
|
||||
auto directoryPath = path;
|
||||
if (directoryPath.isEmpty()) {
|
||||
if (cDialogLastPath().isEmpty()) {
|
||||
Platform::FileDialog::InitLastPath();
|
||||
}
|
||||
directoryPath = cDialogLastPath();
|
||||
}
|
||||
|
||||
time_t t = time(NULL);
|
||||
struct tm tm;
|
||||
|
@ -235,7 +124,7 @@ QString filedialogDefaultName(const QString &prefix, const QString &extension, c
|
|||
if (skipExistance) {
|
||||
name = base + extension;
|
||||
} else {
|
||||
QDir dir(path.isEmpty() ? cDialogLastPath() : path);
|
||||
QDir dir(directoryPath);
|
||||
QString nameBase = dir.absolutePath() + '/' + base;
|
||||
name = nameBase + extension;
|
||||
for (int i = 0; QFileInfo(name).exists(); ++i) {
|
||||
|
@ -268,6 +157,50 @@ QString filedialogAllFilesFilter() {
|
|||
#endif // Q_OS_WIN
|
||||
}
|
||||
|
||||
namespace File {
|
||||
|
||||
void OpenEmailLink(const QString &email) {
|
||||
base::TaskQueue::Main().Put([email] {
|
||||
Platform::File::UnsafeOpenEmailLink(email);
|
||||
});
|
||||
}
|
||||
|
||||
void OpenWith(const QString &filepath, QPoint menuPosition) {
|
||||
base::TaskQueue::Main().Put([filepath, menuPosition] {
|
||||
if (!Platform::File::UnsafeShowOpenWithDropdown(filepath, menuPosition)) {
|
||||
if (!Platform::File::UnsafeShowOpenWith(filepath)) {
|
||||
Platform::File::UnsafeLaunch(filepath);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Launch(const QString &filepath) {
|
||||
base::TaskQueue::Main().Put([filepath] {
|
||||
Platform::File::UnsafeLaunch(filepath);
|
||||
});
|
||||
}
|
||||
|
||||
void ShowInFolder(const QString &filepath) {
|
||||
base::TaskQueue::Main().Put([filepath] {
|
||||
Platform::File::UnsafeShowInFolder(filepath);
|
||||
});
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
void UnsafeOpenEmailLinkDefault(const QString &email) {
|
||||
auto url = QUrl(qstr("mailto:") + email);
|
||||
QDesktopServices::openUrl(url);
|
||||
}
|
||||
|
||||
void UnsafeLaunchDefault(const QString &filepath) {
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(filepath));
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace File
|
||||
|
||||
namespace FileDialog {
|
||||
namespace {
|
||||
|
||||
|
@ -375,7 +308,7 @@ bool processQuery() {
|
|||
|
||||
case Query::Type::ReadFolder: {
|
||||
QString folder;
|
||||
if (filedialogGetDir(folder, query.caption)) {
|
||||
if (filedialogGetDir(folder, query.caption, query.filePath)) {
|
||||
if (!folder.isEmpty()) {
|
||||
update.filePaths.push_back(folder);
|
||||
}
|
||||
|
@ -394,7 +327,7 @@ base::Observable<QueryUpdate> &QueryDone() {
|
|||
return QueryDoneObservable;
|
||||
}
|
||||
|
||||
void askOpenPath(const QString &caption, const QString &filter, base::lambda<void(const OpenResult &result)> callback, base::lambda<void()> failed) {
|
||||
void GetOpenPath(const QString &caption, const QString &filter, base::lambda<void(const OpenResult &result)> callback, base::lambda<void()> failed) {
|
||||
base::TaskQueue::Main().Put([caption, filter, callback = std::move(callback), failed = std::move(failed)] {
|
||||
auto file = QString();
|
||||
auto remoteContent = QByteArray();
|
||||
|
@ -413,7 +346,7 @@ void askOpenPath(const QString &caption, const QString &filter, base::lambda<voi
|
|||
});
|
||||
}
|
||||
|
||||
void askOpenPaths(const QString &caption, const QString &filter, base::lambda<void(const OpenResult &result)> callback, base::lambda<void()> failed) {
|
||||
void GetOpenPaths(const QString &caption, const QString &filter, base::lambda<void(const OpenResult &result)> callback, base::lambda<void()> failed) {
|
||||
base::TaskQueue::Main().Put([caption, filter, callback = std::move(callback), failed = std::move(failed)] {
|
||||
auto files = QStringList();
|
||||
auto remoteContent = QByteArray();
|
||||
|
@ -431,7 +364,7 @@ void askOpenPaths(const QString &caption, const QString &filter, base::lambda<vo
|
|||
|
||||
}
|
||||
|
||||
void askWritePath(const QString &caption, const QString &filter, const QString &initialPath, base::lambda<void(const QString &result)> callback, base::lambda<void()> failed) {
|
||||
void GetWritePath(const QString &caption, const QString &filter, const QString &initialPath, base::lambda<void(const QString &result)> callback, base::lambda<void()> failed) {
|
||||
base::TaskQueue::Main().Put([caption, filter, initialPath, callback = std::move(callback), failed = std::move(failed)] {
|
||||
auto file = QString();
|
||||
if (filedialogGetSaveFile(file, caption, filter, initialPath)) {
|
||||
|
@ -444,10 +377,10 @@ void askWritePath(const QString &caption, const QString &filter, const QString &
|
|||
});
|
||||
}
|
||||
|
||||
void askFolder(const QString &caption, base::lambda<void(const QString &result)> callback, base::lambda<void()> failed) {
|
||||
base::TaskQueue::Main().Put([caption, callback = std::move(callback), failed = std::move(failed)] {
|
||||
void GetFolder(const QString &caption, const QString &initialPath, base::lambda<void(const QString &result)> callback, base::lambda<void()> failed) {
|
||||
base::TaskQueue::Main().Put([caption, initialPath, callback = std::move(callback), failed = std::move(failed)] {
|
||||
auto folder = QString();
|
||||
if (filedialogGetDir(folder, caption) && !folder.isEmpty()) {
|
||||
if (filedialogGetDir(folder, caption, initialPath) && !folder.isEmpty()) {
|
||||
if (callback) {
|
||||
callback(folder);
|
||||
}
|
|
@ -22,17 +22,37 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
|
||||
#include "core/observer.h"
|
||||
|
||||
void filedialogInit();
|
||||
// legacy
|
||||
bool filedialogGetOpenFiles(QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter);
|
||||
bool filedialogGetOpenFile(QString &file, QByteArray &remoteContent, const QString &caption, const QString &filter);
|
||||
bool filedialogGetSaveFile(QString &file, const QString &caption, const QString &filter, const QString &startName);
|
||||
bool filedialogGetDir(QString &dir, const QString &caption);
|
||||
bool filedialogGetSaveFile(QString &file, const QString &caption, const QString &filter, const QString &initialPath);
|
||||
bool filedialogGetDir(QString &dir, const QString &caption, const QString &initialPath);
|
||||
|
||||
QString filedialogDefaultName(const QString &prefix, const QString &extension, const QString &path = QString(), bool skipExistance = false);
|
||||
QString filedialogNextFilename(const QString &name, const QString &cur, const QString &path = QString());
|
||||
|
||||
QString filedialogAllFilesFilter();
|
||||
|
||||
namespace File {
|
||||
|
||||
// Those functions are async wrappers to Platform::File::Unsafe* calls.
|
||||
void OpenEmailLink(const QString &email);
|
||||
void OpenWith(const QString &filepath, QPoint menuPosition);
|
||||
void Launch(const QString &filepath);
|
||||
void ShowInFolder(const QString &filepath);
|
||||
|
||||
namespace internal {
|
||||
|
||||
inline QString UrlToLocalDefault(const QUrl &url) {
|
||||
return url.toLocalFile();
|
||||
}
|
||||
|
||||
void UnsafeOpenEmailLinkDefault(const QString &email);
|
||||
void UnsafeLaunchDefault(const QString &filepath);
|
||||
|
||||
} // namespace internal
|
||||
} // namespace File
|
||||
|
||||
namespace FileDialog {
|
||||
namespace internal {
|
||||
|
||||
|
@ -43,6 +63,10 @@ enum class Type {
|
|||
WriteFile,
|
||||
};
|
||||
|
||||
void InitLastPathDefault();
|
||||
|
||||
bool GetDefault(QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter, ::FileDialog::internal::Type type, QString startFile);
|
||||
|
||||
} // namespace internal
|
||||
|
||||
using QueryId = uint64;
|
||||
|
@ -69,9 +93,9 @@ struct OpenResult {
|
|||
QStringList paths;
|
||||
QByteArray remoteContent;
|
||||
};
|
||||
void askOpenPath(const QString &caption, const QString &filter, base::lambda<void(const OpenResult &result)> callback, base::lambda<void()> failed = base::lambda<void()>());
|
||||
void askOpenPaths(const QString &caption, const QString &filter, base::lambda<void(const OpenResult &result)> callback, base::lambda<void()> failed = base::lambda<void()>());
|
||||
void askWritePath(const QString &caption, const QString &filter, const QString &initialPath, base::lambda<void(const QString &result)> callback, base::lambda<void()> failed = base::lambda<void()>());
|
||||
void askFolder(const QString &caption, base::lambda<void(const QString &result)> callback, base::lambda<void()> failed = base::lambda<void()>());
|
||||
void GetOpenPath(const QString &caption, const QString &filter, base::lambda<void(const OpenResult &result)> callback, base::lambda<void()> failed = base::lambda<void()>());
|
||||
void GetOpenPaths(const QString &caption, const QString &filter, base::lambda<void(const OpenResult &result)> callback, base::lambda<void()> failed = base::lambda<void()>());
|
||||
void GetWritePath(const QString &caption, const QString &filter, const QString &initialPath, base::lambda<void(const QString &result)> callback, base::lambda<void()> failed = base::lambda<void()>());
|
||||
void GetFolder(const QString &caption, const QString &initialPath, base::lambda<void(const QString &result)> callback, base::lambda<void()> failed = base::lambda<void()>());
|
||||
|
||||
} // namespace FileDialog
|
|
@ -489,10 +489,11 @@ enum DBIPeerReportSpamStatus {
|
|||
dbiprsRequesting = 5, // requesting the cloud setting right now
|
||||
};
|
||||
|
||||
inline QString strMakeFromLetters(const uint32 *letters, int32 len) {
|
||||
template <int Size>
|
||||
inline QString strMakeFromLetters(const uint32 (&letters)[Size]) {
|
||||
QString result;
|
||||
result.reserve(len);
|
||||
for (int32 i = 0; i < len; ++i) {
|
||||
result.reserve(Size);
|
||||
for (int32 i = 0; i < Size; ++i) {
|
||||
result.push_back(QChar((((letters[i] >> 16) & 0xFF) << 8) | (letters[i] & 0xFF)));
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -29,7 +29,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "boxes/confirmbox.h"
|
||||
#include "boxes/send_files_box.h"
|
||||
#include "boxes/sharebox.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/buttons/history_down_button.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
|
@ -1532,7 +1532,7 @@ void HistoryInner::showContextInFolder() {
|
|||
}
|
||||
}
|
||||
if (!filepath.isEmpty()) {
|
||||
psShowInFolder(filepath);
|
||||
File::ShowInFolder(filepath);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#pragma once
|
||||
|
||||
#include "localimageloader.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "ui/widgets/tooltip.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
|
|
|
@ -24,7 +24,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "inline_bots/inline_bot_layout_item.h"
|
||||
#include "inline_bots/inline_bot_send_data.h"
|
||||
#include "mtproto/file_download.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "mainwidget.h"
|
||||
|
||||
namespace InlineBots {
|
||||
|
|
|
@ -23,7 +23,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
|
||||
#include "styles/style_intro.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "boxes/confirmbox.h"
|
||||
#include "lang.h"
|
||||
#include "application.h"
|
||||
|
|
|
@ -23,7 +23,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
|
||||
#include "styles/style_intro.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "boxes/photocropbox.h"
|
||||
#include "boxes/confirmbox.h"
|
||||
#include "lang.h"
|
||||
|
|
|
@ -21,7 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#pragma once
|
||||
|
||||
#include "intro/introwidget.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
|
||||
namespace Ui {
|
||||
class RoundButton;
|
||||
|
|
|
@ -27,7 +27,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "application.h"
|
||||
#include "mainwindow.h"
|
||||
#include "mainwidget.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_widgets.h"
|
||||
#include "styles/style_stickers.h"
|
||||
|
|
|
@ -26,7 +26,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "application.h"
|
||||
#include "fileuploader.h"
|
||||
#include "mainwindow.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "boxes/addcontactbox.h"
|
||||
#include "boxes/confirmbox.h"
|
||||
#include "media/media_audio.h"
|
||||
|
|
|
@ -20,9 +20,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
*/
|
||||
#include "stdafx.h"
|
||||
#include "localimageloader.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "media/media_audio.h"
|
||||
|
||||
#include "core/file_utilities.h"
|
||||
#include "media/media_audio.h"
|
||||
#include "boxes/send_files_box.h"
|
||||
#include "media/media_clip_reader.h"
|
||||
#include "mainwidget.h"
|
||||
|
|
|
@ -1578,7 +1578,7 @@ void MainWidget::handleAudioUpdate(const AudioMsgId &audioId) {
|
|||
auto filepath = document->filepath(DocumentData::FilePathResolveSaveFromData);
|
||||
if (!filepath.isEmpty()) {
|
||||
if (documentIsValidMediaFile(filepath)) {
|
||||
psOpenFile(filepath);
|
||||
File::Launch(filepath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "application.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "media/media_clip_reader.h"
|
||||
|
@ -631,7 +631,7 @@ void MediaView::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool presse
|
|||
}
|
||||
|
||||
void MediaView::showSaveMsgFile() {
|
||||
psShowInFolder(_saveMsgFilename);
|
||||
File::ShowInFolder(_saveMsgFilename);
|
||||
}
|
||||
|
||||
void MediaView::close() {
|
||||
|
@ -908,9 +908,9 @@ void MediaView::onSaveCancel() {
|
|||
void MediaView::onShowInFolder() {
|
||||
if (!_doc) return;
|
||||
|
||||
QString filepath = _doc->filepath(DocumentData::FilePathResolveChecked);
|
||||
auto filepath = _doc->filepath(DocumentData::FilePathResolveChecked);
|
||||
if (!filepath.isEmpty()) {
|
||||
psShowInFolder(filepath);
|
||||
File::ShowInFolder(filepath);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "window/themes/window_theme.h"
|
||||
#include "history/history_location_manager.h"
|
||||
#include "ui/widgets/tooltip.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "serialize/serialize_common.h"
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -25,6 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "mainwindow.h"
|
||||
#include "messenger.h"
|
||||
#include "localstorage.h"
|
||||
#include "platform/platform_file_utilities.h"
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -203,7 +204,7 @@ void FileLoader::localLoaded(const StorageImageSaved &result, const QByteArray &
|
|||
if (_fileIsOpen) {
|
||||
_file.close();
|
||||
_fileIsOpen = false;
|
||||
psPostprocessFile(QFileInfo(_file).absoluteFilePath());
|
||||
Platform::File::PostprocessDownloaded(QFileInfo(_file).absoluteFilePath());
|
||||
}
|
||||
FileDownload::ImageLoaded().notify();
|
||||
|
||||
|
@ -531,7 +532,7 @@ void mtpFileLoader::partLoaded(int32 offset, const MTPupload_File &result, mtpRe
|
|||
if (_fileIsOpen) {
|
||||
_file.close();
|
||||
_fileIsOpen = false;
|
||||
psPostprocessFile(QFileInfo(_file).absoluteFilePath());
|
||||
Platform::File::PostprocessDownloaded(QFileInfo(_file).absoluteFilePath());
|
||||
}
|
||||
removeFromQueue();
|
||||
|
||||
|
@ -693,7 +694,7 @@ void webFileLoader::onFinished(const QByteArray &data) {
|
|||
if (_fileIsOpen) {
|
||||
_file.close();
|
||||
_fileIsOpen = false;
|
||||
psPostprocessFile(QFileInfo(_file).absoluteFilePath());
|
||||
Platform::File::PostprocessDownloaded(QFileInfo(_file).absoluteFilePath());
|
||||
}
|
||||
removeFromQueue();
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
|
||||
#include "styles/style_overview.h"
|
||||
#include "styles/style_history.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "boxes/addcontactbox.h"
|
||||
#include "boxes/confirmbox.h"
|
||||
#include "lang.h"
|
||||
|
|
|
@ -27,7 +27,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "boxes/addcontactbox.h"
|
||||
#include "boxes/confirmbox.h"
|
||||
#include "boxes/photocropbox.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/widgets/tooltip.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
|
@ -1440,7 +1440,7 @@ void OverviewInner::showContextInFolder() {
|
|||
if (auto lnkDocument = dynamic_cast<DocumentClickHandler*>(_contextMenuLnk.data())) {
|
||||
auto filepath = lnkDocument->document()->filepath(DocumentData::FilePathResolveChecked);
|
||||
if (!filepath.isEmpty()) {
|
||||
psShowInFolder(filepath);
|
||||
File::ShowInFolder(filepath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,44 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
QStringList qt_make_filter_list(const QString &filter);
|
||||
|
||||
namespace Platform {
|
||||
namespace File {
|
||||
namespace internal {
|
||||
|
||||
QByteArray EscapeShell(const QByteArray &content) {
|
||||
auto result = QByteArray();
|
||||
|
||||
auto b = content.constData(), e = content.constEnd();
|
||||
for (auto ch = b; ch != e; ++ch) {
|
||||
if (*ch == ' ' || *ch == '"' || *ch == '\'' || *ch == '\\') {
|
||||
if (result.isEmpty()) {
|
||||
result.reserve(content.size() * 2);
|
||||
}
|
||||
if (ch > b) {
|
||||
result.append(b, ch - b);
|
||||
}
|
||||
result.append('\\');
|
||||
b = ch;
|
||||
}
|
||||
}
|
||||
if (result.isEmpty()) {
|
||||
return content;
|
||||
}
|
||||
|
||||
if (e > b) {
|
||||
result.append(b, e - b);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
void UnsafeShowInFolder(const QString &filepath) {
|
||||
Ui::hideLayer(true);
|
||||
system(("xdg-open " + internal::EscapeShell(QFile::encodeName(QFileInfo(filepath).absoluteDir().absolutePath()))).constData());
|
||||
}
|
||||
|
||||
} // namespace File
|
||||
|
||||
namespace FileDialog {
|
||||
namespace {
|
||||
|
||||
|
@ -43,11 +81,9 @@ namespace {
|
|||
constexpr auto kPreviewWidth = 256;
|
||||
constexpr auto kPreviewHeight = 512;
|
||||
|
||||
} // namespace
|
||||
|
||||
using Type = ::FileDialog::internal::Type;
|
||||
|
||||
bool Supported() {
|
||||
bool NativeSupported() {
|
||||
return Platform::internal::GdkHelperLoaded()
|
||||
&& (Libs::gtk_widget_hide_on_delete != nullptr)
|
||||
&& (Libs::gtk_clipboard_store != nullptr)
|
||||
|
@ -84,11 +120,11 @@ bool Supported() {
|
|||
}
|
||||
|
||||
bool PreviewSupported() {
|
||||
return Supported()
|
||||
return NativeSupported()
|
||||
&& (Libs::gdk_pixbuf_new_from_file_at_size != nullptr);
|
||||
}
|
||||
|
||||
bool Get(QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter, Type type, QString startFile) {
|
||||
bool GetNative(QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter, Type type, QString startFile) {
|
||||
auto parent = App::wnd() ? App::wnd()->filedialogParent() : nullptr;
|
||||
internal::GtkFileDialog dialog(parent, caption, QString(), filter);
|
||||
|
||||
|
@ -131,6 +167,15 @@ bool Get(QStringList &files, QByteArray &remoteContent, const QString &caption,
|
|||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool Get(QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter, Type type, QString startFile) {
|
||||
if (NativeSupported()) {
|
||||
return GetNative(files, remoteContent, caption, filter, type, startFile);
|
||||
}
|
||||
return ::FileDialog::internal::GetDefault(files, remoteContent, caption, filter, type, startFile);
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
QGtkDialog::QGtkDialog(GtkWidget *gtkWidget) : gtkWidget(gtkWidget) {
|
||||
|
|
|
@ -31,18 +31,42 @@ extern "C" {
|
|||
|
||||
namespace Platform {
|
||||
namespace File {
|
||||
namespace internal {
|
||||
|
||||
QByteArray EscapeShell(const QByteArray &content);
|
||||
|
||||
} // namespace internal
|
||||
|
||||
inline QString UrlToLocal(const QUrl &url) {
|
||||
return url.toLocalFile();
|
||||
return ::File::internal::UrlToLocalDefault(url);
|
||||
}
|
||||
|
||||
inline void UnsafeOpenEmailLink(const QString &email) {
|
||||
return ::File::internal::UnsafeOpenEmailLinkDefault(email);
|
||||
}
|
||||
|
||||
inline bool UnsafeShowOpenWithDropdown(const QString &filepath, QPoint menuPosition) {
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool UnsafeShowOpenWith(const QString &filepath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void UnsafeLaunch(const QString &filepath) {
|
||||
return ::File::internal::UnsafeLaunchDefault(filepath);
|
||||
}
|
||||
|
||||
inline void PostprocessDownloaded(const QString &filepath) {
|
||||
}
|
||||
|
||||
} // namespace File
|
||||
|
||||
namespace FileDialog {
|
||||
|
||||
bool Supported();
|
||||
|
||||
bool Get(QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter, ::FileDialog::internal::Type type, QString startFile);
|
||||
inline void InitLastPath() {
|
||||
::FileDialog::internal::InitLastPathDefault();
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
|
|
|
@ -374,7 +374,7 @@ void MainWindow::updateIconCounters() {
|
|||
}
|
||||
|
||||
bool MainWindow::psHasNativeNotifications() {
|
||||
return Notifications::supported();
|
||||
return Notifications::Supported();
|
||||
}
|
||||
|
||||
void MainWindow::LibsLoaded() {
|
||||
|
|
|
@ -23,14 +23,25 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "platform/platform_file_utilities.h"
|
||||
|
||||
namespace Platform {
|
||||
namespace File {
|
||||
|
||||
inline void UnsafeOpenEmailLink(const QString &email) {
|
||||
return ::File::internal::UnsafeOpenEmailLinkDefault(email);
|
||||
}
|
||||
|
||||
inline void PostprocessDownloaded(const QString &filepath) {
|
||||
}
|
||||
|
||||
} // namespace File
|
||||
|
||||
namespace FileDialog {
|
||||
|
||||
inline bool Supported() {
|
||||
return false;
|
||||
inline void InitLastPath() {
|
||||
::FileDialog::internal::InitLastPathDefault();
|
||||
}
|
||||
|
||||
inline bool Get(QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter, ::FileDialog::internal::Type type, QString startFile) {
|
||||
return false;
|
||||
return ::FileDialog::internal::GetDefault(files, remoteContent, caption, filter, type, startFile);
|
||||
}
|
||||
|
||||
} // namespace FileDialog
|
||||
|
|
|
@ -22,10 +22,366 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "platform/mac/file_utilities_mac.h"
|
||||
|
||||
#include "platform/mac/mac_utilities.h"
|
||||
#include "styles/style_window.h"
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <CoreFoundation/CFURL.h>
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace Platform;
|
||||
|
||||
QString strNeedToReload() {
|
||||
const uint32 letters[] = { 0x82007746, 0xBB00C649, 0x7E00235F, 0x9A00FE54, 0x4C004542, 0x91001772, 0x8A00D76F, 0xC700B977, 0x7F005F73, 0x34003665, 0x2300D572, 0x72002E54, 0x18001461, 0x14004A62, 0x5100CC6C, 0x83002365, 0x5A002C56, 0xA5004369, 0x26004265, 0x0D006577 };
|
||||
return strMakeFromLetters(letters);
|
||||
}
|
||||
|
||||
QString strNeedToRefresh1() {
|
||||
const uint32 letters[] = { 0xEF006746, 0xF500CE49, 0x1500715F, 0x95001254, 0x3A00CB4C, 0x17009469, 0xB400DA73, 0xDE00C574, 0x9200EC56, 0x3C00A669, 0xFD00D865, 0x59000977 };
|
||||
return strMakeFromLetters(letters);
|
||||
}
|
||||
|
||||
QString strNeedToRefresh2() {
|
||||
const uint32 letters[] = { 0x8F001546, 0xAF007A49, 0xB8002B5F, 0x1A000B54, 0x0D003E49, 0xE0003663, 0x4900796F, 0x0500836E, 0x9A00D156, 0x5E00FF69, 0x5900C765, 0x3D00D177 };
|
||||
return strMakeFromLetters(letters);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@interface OpenWithApp : NSObject {
|
||||
NSString *fullname;
|
||||
NSURL *app;
|
||||
NSImage *icon;
|
||||
}
|
||||
@property (nonatomic, retain) NSString *fullname;
|
||||
@property (nonatomic, retain) NSURL *app;
|
||||
@property (nonatomic, retain) NSImage *icon;
|
||||
@end
|
||||
|
||||
@implementation OpenWithApp
|
||||
@synthesize fullname, app, icon;
|
||||
|
||||
- (void) dealloc {
|
||||
[fullname release];
|
||||
[app release];
|
||||
[icon release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface OpenFileWithInterface : NSObject {
|
||||
}
|
||||
|
||||
- (id) init:(NSString *)file;
|
||||
- (BOOL) popupAtX:(int)x andY:(int)y;
|
||||
- (void) itemChosen:(id)sender;
|
||||
- (void) dealloc;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OpenFileWithInterface {
|
||||
NSString *toOpen;
|
||||
|
||||
NSURL *defUrl;
|
||||
NSString *defBundle, *defName, *defVersion;
|
||||
NSImage *defIcon;
|
||||
|
||||
NSMutableArray *apps;
|
||||
|
||||
NSMenu *menu;
|
||||
}
|
||||
|
||||
- (void) fillAppByUrl:(NSURL*)url bundle:(NSString**)bundle name:(NSString**)name version:(NSString**)version icon:(NSImage**)icon {
|
||||
NSBundle *b = [NSBundle bundleWithURL:url];
|
||||
if (b) {
|
||||
NSString *path = [url path];
|
||||
*name = [[NSFileManager defaultManager] displayNameAtPath: path];
|
||||
if (!*name) *name = (NSString*)[b objectForInfoDictionaryKey:@"CFBundleDisplayName"];
|
||||
if (!*name) *name = (NSString*)[b objectForInfoDictionaryKey:@"CFBundleName"];
|
||||
if (*name) {
|
||||
*bundle = [b bundleIdentifier];
|
||||
if (bundle) {
|
||||
*version = (NSString*)[b objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
|
||||
*icon = [[NSWorkspace sharedWorkspace] iconForFile: path];
|
||||
if (*icon && [*icon isValid]) [*icon setSize: CGSizeMake(16., 16.)];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
*bundle = *name = *version = nil;
|
||||
*icon = nil;
|
||||
}
|
||||
|
||||
- (id) init:(NSString*)file {
|
||||
toOpen = [file retain];
|
||||
if (self = [super init]) {
|
||||
NSURL *url = [NSURL fileURLWithPath:file];
|
||||
defUrl = [[NSWorkspace sharedWorkspace] URLForApplicationToOpenURL:url];
|
||||
if (defUrl) {
|
||||
[self fillAppByUrl:defUrl bundle:&defBundle name:&defName version:&defVersion icon:&defIcon];
|
||||
if (!defBundle || !defName) {
|
||||
defUrl = nil;
|
||||
}
|
||||
}
|
||||
NSArray *appsList = (NSArray*)LSCopyApplicationURLsForURL(CFURLRef(url), kLSRolesAll);
|
||||
NSMutableDictionary *data = [NSMutableDictionary dictionaryWithCapacity:16];
|
||||
int fullcount = 0;
|
||||
for (id app in appsList) {
|
||||
if (fullcount > 15) break;
|
||||
|
||||
NSString *bundle = nil, *name = nil, *version = nil;
|
||||
NSImage *icon = nil;
|
||||
[self fillAppByUrl:(NSURL*)app bundle:&bundle name:&name version:&version icon:&icon];
|
||||
if (bundle && name) {
|
||||
if ([bundle isEqualToString:defBundle] && [version isEqualToString:defVersion]) continue;
|
||||
NSString *key = [[NSArray arrayWithObjects:bundle, name, nil] componentsJoinedByString:@"|"];
|
||||
if (!version) version = @"";
|
||||
|
||||
NSMutableDictionary *versions = (NSMutableDictionary*)[data objectForKey:key];
|
||||
if (!versions) {
|
||||
versions = [NSMutableDictionary dictionaryWithCapacity:2];
|
||||
[data setValue:versions forKey:key];
|
||||
}
|
||||
if (![versions objectForKey:version]) {
|
||||
[versions setValue:[NSArray arrayWithObjects:name, icon, app, nil] forKey:version];
|
||||
++fullcount;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fullcount || defUrl) {
|
||||
apps = [NSMutableArray arrayWithCapacity:fullcount];
|
||||
for (id key in data) {
|
||||
NSMutableDictionary *val = (NSMutableDictionary*)[data objectForKey:key];
|
||||
for (id ver in val) {
|
||||
NSArray *app = (NSArray*)[val objectForKey:ver];
|
||||
OpenWithApp *a = [[OpenWithApp alloc] init];
|
||||
NSString *fullname = (NSString*)[app objectAtIndex:0], *version = (NSString*)ver;
|
||||
BOOL showVersion = ([val count] > 1);
|
||||
if (!showVersion) {
|
||||
NSError *error = NULL;
|
||||
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^\\d+\\.\\d+\\.\\d+(\\.\\d+)?$" options:NSRegularExpressionCaseInsensitive error:&error];
|
||||
showVersion = ![regex numberOfMatchesInString:version options:NSMatchingWithoutAnchoringBounds range:{0,[version length]}];
|
||||
}
|
||||
if (showVersion) fullname = [[NSArray arrayWithObjects:fullname, @" (", version, @")", nil] componentsJoinedByString:@""];
|
||||
[a setFullname:fullname];
|
||||
[a setIcon:(NSImage*)[app objectAtIndex:1]];
|
||||
[a setApp:(NSURL*)[app objectAtIndex:2]];
|
||||
[apps addObject:a];
|
||||
[a release];
|
||||
}
|
||||
}
|
||||
}
|
||||
[apps sortUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"fullname" ascending:YES]]];
|
||||
[appsList release];
|
||||
menu = nil;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL) popupAtX:(int)x andY:(int)y {
|
||||
if (![apps count] && !defName) return NO;
|
||||
menu = [[NSMenu alloc] initWithTitle:@"Open With"];
|
||||
|
||||
int index = 0;
|
||||
if (defName) {
|
||||
NSMenuItem *item = [menu insertItemWithTitle:[[NSArray arrayWithObjects:defName, @" (default)", nil] componentsJoinedByString:@""] action:@selector(itemChosen:) keyEquivalent:@"" atIndex:index++];
|
||||
if (defIcon) [item setImage:defIcon];
|
||||
[item setTarget:self];
|
||||
[menu insertItem:[NSMenuItem separatorItem] atIndex:index++];
|
||||
}
|
||||
if ([apps count]) {
|
||||
for (id a in apps) {
|
||||
OpenWithApp *app = (OpenWithApp*)a;
|
||||
NSMenuItem *item = [menu insertItemWithTitle:[a fullname] action:@selector(itemChosen:) keyEquivalent:@"" atIndex:index++];
|
||||
if ([app icon]) [item setImage:[app icon]];
|
||||
[item setTarget:self];
|
||||
}
|
||||
[menu insertItem:[NSMenuItem separatorItem] atIndex:index++];
|
||||
}
|
||||
NSMenuItem *item = [menu insertItemWithTitle:NSlang(lng_mac_choose_program_menu) action:@selector(itemChosen:) keyEquivalent:@"" atIndex:index++];
|
||||
[item setTarget:self];
|
||||
|
||||
[menu popUpMenuPositioningItem:nil atLocation:CGPointMake(x, y) inView:nil];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void) itemChosen:(id)sender {
|
||||
NSArray *items = [menu itemArray];
|
||||
NSURL *url = nil;
|
||||
for (int i = 0, l = [items count]; i < l; ++i) {
|
||||
if ([items objectAtIndex:i] == sender) {
|
||||
if (defName) i -= 2;
|
||||
if (i < 0) {
|
||||
url = defUrl;
|
||||
} else if (i < int([apps count])) {
|
||||
url = [(OpenWithApp*)[apps objectAtIndex:i] app];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (url) {
|
||||
[[NSWorkspace sharedWorkspace] openFile:toOpen withApplication:[url path]];
|
||||
} else if (!Platform::File::UnsafeShowOpenWith(NS2QString(toOpen))) {
|
||||
Platform::File::UnsafeLaunch(NS2QString(toOpen));
|
||||
}
|
||||
}
|
||||
|
||||
- (void) dealloc {
|
||||
[toOpen release];
|
||||
if (menu) [menu release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface NSURL(CompareUrls)
|
||||
|
||||
- (BOOL) isEquivalent:(NSURL *)aURL;
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSURL(CompareUrls)
|
||||
|
||||
- (BOOL) isEquivalent:(NSURL *)aURL {
|
||||
if ([self isEqual:aURL]) return YES;
|
||||
if ([[self scheme] caseInsensitiveCompare:[aURL scheme]] != NSOrderedSame) return NO;
|
||||
if ([[self host] caseInsensitiveCompare:[aURL host]] != NSOrderedSame) return NO;
|
||||
if ([[self path] compare:[aURL path]] != NSOrderedSame) return NO;
|
||||
if ([[self port] compare:[aURL port]] != NSOrderedSame) return NO;
|
||||
if ([[self query] compare:[aURL query]] != NSOrderedSame) return NO;
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface ChooseApplicationDelegate : NSObject<NSOpenSavePanelDelegate> {
|
||||
}
|
||||
|
||||
- (id) init:(NSArray *)recommendedApps withPanel:(NSOpenPanel *)creator withSelector:(NSPopUpButton *)menu withGood:(NSTextField *)goodLabel withBad:(NSTextField *)badLabel withIcon:(NSImageView *)badIcon withAccessory:(NSView *)acc;
|
||||
- (BOOL) panel:(id)sender shouldEnableURL:(NSURL *)url;
|
||||
- (void) panelSelectionDidChange:(id)sender;
|
||||
- (void) menuDidClose;
|
||||
- (void) dealloc;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ChooseApplicationDelegate {
|
||||
BOOL onlyRecommended;
|
||||
NSArray *apps;
|
||||
NSOpenPanel *panel;
|
||||
NSPopUpButton *selector;
|
||||
NSTextField *good, *bad;
|
||||
NSImageView *icon;
|
||||
NSString *recom;
|
||||
NSView *accessory;
|
||||
}
|
||||
|
||||
- (id) init:(NSArray *)recommendedApps withPanel:(NSOpenPanel *)creator withSelector:(NSPopUpButton *)menu withGood:(NSTextField *)goodLabel withBad:(NSTextField *)badLabel withIcon:(NSImageView *)badIcon withAccessory:(NSView *)acc {
|
||||
if (self = [super init]) {
|
||||
onlyRecommended = YES;
|
||||
recom = [NSlang(lng_mac_recommended_apps) copy];
|
||||
apps = recommendedApps;
|
||||
panel = creator;
|
||||
selector = menu;
|
||||
good = goodLabel;
|
||||
bad = badLabel;
|
||||
icon = badIcon;
|
||||
accessory = acc;
|
||||
[selector setAction:@selector(menuDidClose)];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL) isRecommended:(NSURL *)url {
|
||||
if (apps) {
|
||||
for (id app in apps) {
|
||||
if ([(NSURL*)app isEquivalent:url]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL) panel:(id)sender shouldEnableURL:(NSURL *)url {
|
||||
NSNumber *isDirectory;
|
||||
if ([url getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil] && isDirectory != nil && [isDirectory boolValue]) {
|
||||
if (onlyRecommended) {
|
||||
CFStringRef ext = CFURLCopyPathExtension((CFURLRef)url);
|
||||
NSNumber *isPackage;
|
||||
if ([url getResourceValue:&isPackage forKey:NSURLIsPackageKey error:nil] && isPackage != nil && [isPackage boolValue]) {
|
||||
return [self isRecommended:url];
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void) panelSelectionDidChange:(id)sender {
|
||||
NSArray *urls = [panel URLs];
|
||||
if ([urls count]) {
|
||||
if ([self isRecommended:[urls firstObject]]) {
|
||||
[bad removeFromSuperview];
|
||||
[icon removeFromSuperview];
|
||||
[accessory addSubview:good];
|
||||
} else {
|
||||
[good removeFromSuperview];
|
||||
[accessory addSubview:bad];
|
||||
[accessory addSubview:icon];
|
||||
}
|
||||
} else {
|
||||
[good removeFromSuperview];
|
||||
[bad removeFromSuperview];
|
||||
[icon removeFromSuperview];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) menuDidClose {
|
||||
onlyRecommended = [[[selector selectedItem] title] isEqualToString:recom];
|
||||
[self refreshPanelTable];
|
||||
}
|
||||
|
||||
- (BOOL) refreshDataInViews: (NSArray*)subviews {
|
||||
for (id view in subviews) {
|
||||
NSString *cls = [view className];
|
||||
if ([cls isEqualToString:Q2NSString(strNeedToReload())]) {
|
||||
[view reloadData];
|
||||
} else if ([cls isEqualToString:Q2NSString(strNeedToRefresh1())] || [cls isEqualToString:Q2NSString(strNeedToRefresh2())]) {
|
||||
[view reloadData];
|
||||
return YES;
|
||||
} else {
|
||||
NSArray *next = [view subviews];
|
||||
if ([next count] && [self refreshDataInViews:next]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
- (void) refreshPanelTable {
|
||||
@autoreleasepool {
|
||||
|
||||
[self refreshDataInViews:[[panel contentView] subviews]];
|
||||
[panel validateVisibleColumns];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
- (void) dealloc {
|
||||
if (apps) {
|
||||
[apps release];
|
||||
[recom release];
|
||||
}
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
namespace Platform {
|
||||
namespace File {
|
||||
|
||||
|
@ -40,5 +396,195 @@ QString UrlToLocal(const QUrl &url) {
|
|||
return result;
|
||||
}
|
||||
|
||||
bool UnsafeShowOpenWithDropdown(const QString &filepath, QPoint menuPosition) {
|
||||
@autoreleasepool {
|
||||
|
||||
NSString *file = Q2NSString(filepath);
|
||||
@try {
|
||||
OpenFileWithInterface *menu = [[[OpenFileWithInterface alloc] init:file] autorelease];
|
||||
auto r = QApplication::desktop()->screenGeometry(menuPosition);
|
||||
auto x = menuPosition.x();
|
||||
auto y = r.y() + r.height() - menuPosition.y();
|
||||
return !![menu popupAtX:x andY:y];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
}
|
||||
@finally {
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UnsafeShowOpenWith(const QString &filepath) {
|
||||
@autoreleasepool {
|
||||
|
||||
NSString *file = Q2NSString(filepath);
|
||||
@try {
|
||||
NSURL *url = [NSURL fileURLWithPath:file];
|
||||
NSString *ext = [url pathExtension];
|
||||
NSArray *names = [url pathComponents];
|
||||
NSString *name = [names count] ? [names lastObject] : @"";
|
||||
NSArray *apps = (NSArray*)LSCopyApplicationURLsForURL(CFURLRef(url), kLSRolesAll);
|
||||
|
||||
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
|
||||
|
||||
NSRect fullRect = { { 0., 0. }, { st::macAccessoryWidth, st::macAccessoryHeight } };
|
||||
NSView *accessory = [[NSView alloc] initWithFrame:fullRect];
|
||||
|
||||
[accessory setAutoresizesSubviews:YES];
|
||||
|
||||
NSPopUpButton *selector = [[NSPopUpButton alloc] init];
|
||||
[accessory addSubview:selector];
|
||||
[selector addItemWithTitle:NSlang(lng_mac_recommended_apps)];
|
||||
[selector addItemWithTitle:NSlang(lng_mac_all_apps)];
|
||||
[selector sizeToFit];
|
||||
|
||||
NSTextField *enableLabel = [[NSTextField alloc] init];
|
||||
[accessory addSubview:enableLabel];
|
||||
[enableLabel setStringValue:NSlang(lng_mac_enable_filter)];
|
||||
[enableLabel setFont:[selector font]];
|
||||
[enableLabel setBezeled:NO];
|
||||
[enableLabel setDrawsBackground:NO];
|
||||
[enableLabel setEditable:NO];
|
||||
[enableLabel setSelectable:NO];
|
||||
[enableLabel sizeToFit];
|
||||
|
||||
NSRect selectorFrame = [selector frame], enableFrame = [enableLabel frame];
|
||||
enableFrame.size.width += st::macEnableFilterAdd;
|
||||
enableFrame.origin.x = (fullRect.size.width - selectorFrame.size.width - enableFrame.size.width) / 2.;
|
||||
selectorFrame.origin.x = (fullRect.size.width - selectorFrame.size.width + enableFrame.size.width) / 2.;
|
||||
enableFrame.origin.y = fullRect.size.height - selectorFrame.size.height - st::macEnableFilterTop + (selectorFrame.size.height - enableFrame.size.height) / 2.;
|
||||
selectorFrame.origin.y = fullRect.size.height - selectorFrame.size.height - st::macSelectorTop;
|
||||
[enableLabel setFrame:enableFrame];
|
||||
[enableLabel setAutoresizingMask:NSViewMinXMargin|NSViewMaxXMargin];
|
||||
[selector setFrame:selectorFrame];
|
||||
[selector setAutoresizingMask:NSViewMinXMargin|NSViewMaxXMargin];
|
||||
|
||||
NSButton *button = [[NSButton alloc] init];
|
||||
[accessory addSubview:button];
|
||||
[button setButtonType:NSSwitchButton];
|
||||
[button setFont:[selector font]];
|
||||
[button setTitle:NSlang(lng_mac_always_open_with)];
|
||||
[button sizeToFit];
|
||||
NSRect alwaysRect = [button frame];
|
||||
alwaysRect.origin.x = (fullRect.size.width - alwaysRect.size.width) / 2;
|
||||
alwaysRect.origin.y = selectorFrame.origin.y - alwaysRect.size.height - st::macAlwaysThisAppTop;
|
||||
[button setFrame:alwaysRect];
|
||||
[button setAutoresizingMask:NSViewMinXMargin|NSViewMaxXMargin];
|
||||
#ifdef OS_MAC_STORE
|
||||
[button setHidden:YES];
|
||||
#endif // OS_MAC_STORE
|
||||
NSTextField *goodLabel = [[NSTextField alloc] init];
|
||||
[goodLabel setStringValue:Q2NSString(lng_mac_this_app_can_open(lt_file, NS2QString(name)))];
|
||||
[goodLabel setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
|
||||
[goodLabel setBezeled:NO];
|
||||
[goodLabel setDrawsBackground:NO];
|
||||
[goodLabel setEditable:NO];
|
||||
[goodLabel setSelectable:NO];
|
||||
[goodLabel sizeToFit];
|
||||
NSRect goodFrame = [goodLabel frame];
|
||||
goodFrame.origin.x = (fullRect.size.width - goodFrame.size.width) / 2.;
|
||||
goodFrame.origin.y = alwaysRect.origin.y - goodFrame.size.height - st::macAppHintTop;
|
||||
[goodLabel setFrame:goodFrame];
|
||||
|
||||
NSTextField *badLabel = [[NSTextField alloc] init];
|
||||
[badLabel setStringValue:Q2NSString(lng_mac_not_known_app(lt_file, NS2QString(name)))];
|
||||
[badLabel setFont:[goodLabel font]];
|
||||
[badLabel setBezeled:NO];
|
||||
[badLabel setDrawsBackground:NO];
|
||||
[badLabel setEditable:NO];
|
||||
[badLabel setSelectable:NO];
|
||||
[badLabel sizeToFit];
|
||||
NSImageView *badIcon = [[NSImageView alloc] init];
|
||||
NSImage *badImage = [NSImage imageNamed:NSImageNameCaution];
|
||||
[badIcon setImage:badImage];
|
||||
[badIcon setFrame:NSMakeRect(0, 0, st::macCautionIconSize, st::macCautionIconSize)];
|
||||
|
||||
NSRect badFrame = [badLabel frame], badIconFrame = [badIcon frame];
|
||||
badFrame.origin.x = (fullRect.size.width - badFrame.size.width + badIconFrame.size.width) / 2.;
|
||||
badIconFrame.origin.x = (fullRect.size.width - badFrame.size.width - badIconFrame.size.width) / 2.;
|
||||
badFrame.origin.y = alwaysRect.origin.y - badFrame.size.height - st::macAppHintTop;
|
||||
badIconFrame.origin.y = badFrame.origin.y;
|
||||
[badLabel setFrame:badFrame];
|
||||
[badIcon setFrame:badIconFrame];
|
||||
|
||||
[openPanel setAccessoryView:accessory];
|
||||
|
||||
ChooseApplicationDelegate *delegate = [[ChooseApplicationDelegate alloc] init:apps withPanel:openPanel withSelector:selector withGood:goodLabel withBad:badLabel withIcon:badIcon withAccessory:accessory];
|
||||
[openPanel setDelegate:delegate];
|
||||
|
||||
[openPanel setCanChooseDirectories:NO];
|
||||
[openPanel setCanChooseFiles:YES];
|
||||
[openPanel setAllowsMultipleSelection:NO];
|
||||
[openPanel setResolvesAliases:YES];
|
||||
[openPanel setTitle:NSlang(lng_mac_choose_app)];
|
||||
[openPanel setMessage:Q2NSString(lng_mac_choose_text(lt_file, NS2QString(name)))];
|
||||
|
||||
NSArray *appsPaths = [[NSFileManager defaultManager] URLsForDirectory:NSApplicationDirectory inDomains:NSLocalDomainMask];
|
||||
if ([appsPaths count]) [openPanel setDirectoryURL:[appsPaths firstObject]];
|
||||
[openPanel beginWithCompletionHandler:^(NSInteger result){
|
||||
if (result == NSFileHandlingPanelOKButton) {
|
||||
if ([[openPanel URLs] count] > 0) {
|
||||
NSURL *app = [[openPanel URLs] objectAtIndex:0];
|
||||
NSString *path = [app path];
|
||||
if ([button state] == NSOnState) {
|
||||
NSArray *UTIs = (NSArray *)UTTypeCreateAllIdentifiersForTag(kUTTagClassFilenameExtension,
|
||||
(CFStringRef)ext,
|
||||
nil);
|
||||
for (NSString *UTI in UTIs) {
|
||||
OSStatus result = LSSetDefaultRoleHandlerForContentType((CFStringRef)UTI,
|
||||
kLSRolesAll,
|
||||
(CFStringRef)[[NSBundle bundleWithPath:path] bundleIdentifier]);
|
||||
DEBUG_LOG(("App Info: set default handler for '%1' UTI result: %2").arg(NS2QString(UTI)).arg(result));
|
||||
}
|
||||
|
||||
[UTIs release];
|
||||
}
|
||||
[[NSWorkspace sharedWorkspace] openFile:file withApplication:[app path]];
|
||||
}
|
||||
}
|
||||
[selector release];
|
||||
[button release];
|
||||
[enableLabel release];
|
||||
[goodLabel release];
|
||||
[badLabel release];
|
||||
[badIcon release];
|
||||
[accessory release];
|
||||
[delegate release];
|
||||
}];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[[NSWorkspace sharedWorkspace] openFile:file];
|
||||
}
|
||||
@finally {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
void UnsafeLaunch(const QString &filepath) {
|
||||
@autoreleasepool {
|
||||
|
||||
NSString *file = Q2NSString(filepath);
|
||||
if ([[NSWorkspace sharedWorkspace] openFile:file] == NO) {
|
||||
UnsafeShowOpenWith(filepath);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void UnsafeShowInFolder(const QString &filepath) {
|
||||
auto folder = QFileInfo(filepath).absolutePath();
|
||||
|
||||
@autoreleasepool {
|
||||
|
||||
[[NSWorkspace sharedWorkspace] selectFile:Q2NSString(filepath) inFileViewerRootedAtPath:Q2NSString(folder)];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace File
|
||||
} // namespace Platform
|
||||
|
|
|
@ -293,7 +293,7 @@ void MainWindow::psUpdateWorkmode() {
|
|||
trayIcon = nullptr;
|
||||
}
|
||||
}
|
||||
if (auto manager = Platform::Notifications::ManagerNative()) {
|
||||
if (auto manager = Platform::Notifications::GetNativeManager()) {
|
||||
manager->updateDelegate();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,10 +36,6 @@ inline bool SkipToast() {
|
|||
return false;
|
||||
}
|
||||
|
||||
inline Window::Notifications::Manager *GetManager() {
|
||||
return GetNativeManager();
|
||||
}
|
||||
|
||||
class Manager : public Window::Notifications::NativeManager {
|
||||
public:
|
||||
Manager();
|
||||
|
@ -59,5 +55,9 @@ private:
|
|||
|
||||
};
|
||||
|
||||
inline Window::Notifications::Manager *GetManager() {
|
||||
return GetNativeManager();
|
||||
}
|
||||
|
||||
} // namespace Notifications
|
||||
} // namespace Platform
|
||||
|
|
|
@ -20,20 +20,29 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
|
||||
namespace Platform {
|
||||
namespace File {
|
||||
|
||||
QString UrlToLocal(const QUrl &url);
|
||||
|
||||
// All these functions may enter a nested event loop. Use with caution.
|
||||
void UnsafeOpenEmailLink(const QString &email);
|
||||
bool UnsafeShowOpenWithDropdown(const QString &filepath, QPoint menuPosition);
|
||||
bool UnsafeShowOpenWith(const QString &filepath);
|
||||
void UnsafeLaunch(const QString &filepath);
|
||||
void UnsafeShowInFolder(const QString &filepath);
|
||||
|
||||
void PostprocessDownloaded(const QString &filepath);
|
||||
|
||||
} // namespace File
|
||||
|
||||
namespace FileDialog {
|
||||
|
||||
bool Supported();
|
||||
void InitLastPath();
|
||||
|
||||
bool Get(QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter, ::FileDialog::internal::Type type, QString startFile);
|
||||
bool Get(QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter, ::FileDialog::internal::Type type, QString startFile = QString());
|
||||
|
||||
} // namespace FileDialog
|
||||
} // namespace Platform
|
||||
|
|
|
@ -180,7 +180,6 @@ void DeInit() {
|
|||
if (WasCoInitialized) {
|
||||
CoUninitialize();
|
||||
}
|
||||
AUDCLNT_E_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
} // namespace Audio
|
||||
|
|
|
@ -21,3 +21,401 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "stdafx.h"
|
||||
#include "platform/win/file_utilities_win.h"
|
||||
|
||||
#include "mainwindow.h"
|
||||
#include "localstorage.h"
|
||||
#include "platform/win/windows_dlls.h"
|
||||
#include "lang.h"
|
||||
|
||||
#include <Shlwapi.h>
|
||||
#include <Windowsx.h>
|
||||
|
||||
HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &, int hbitmapFormat);
|
||||
|
||||
namespace Platform {
|
||||
namespace File {
|
||||
namespace {
|
||||
|
||||
class OpenWithApp {
|
||||
public:
|
||||
OpenWithApp(const QString &name, IAssocHandler *handler, HBITMAP icon = nullptr)
|
||||
: _name(name)
|
||||
, _handler(handler)
|
||||
, _icon(icon) {
|
||||
}
|
||||
OpenWithApp(OpenWithApp &&other)
|
||||
: _name(base::take(other._name))
|
||||
, _handler(base::take(other._handler))
|
||||
, _icon(base::take(other._icon)) {
|
||||
}
|
||||
OpenWithApp &operator=(OpenWithApp &&other) {
|
||||
_name = base::take(other._name);
|
||||
_icon = base::take(other._icon);
|
||||
_handler = base::take(other._handler);
|
||||
return (*this);
|
||||
}
|
||||
|
||||
OpenWithApp(const OpenWithApp &other) = delete;
|
||||
OpenWithApp &operator=(const OpenWithApp &other) = delete;
|
||||
|
||||
~OpenWithApp() {
|
||||
if (_icon) {
|
||||
DeleteBitmap(_icon);
|
||||
}
|
||||
if (_handler) {
|
||||
_handler->Release();
|
||||
}
|
||||
}
|
||||
|
||||
const QString &name() const {
|
||||
return _name;
|
||||
}
|
||||
HBITMAP icon() const {
|
||||
return _icon;
|
||||
}
|
||||
IAssocHandler *handler() const {
|
||||
return _handler;
|
||||
}
|
||||
|
||||
private:
|
||||
QString _name;
|
||||
IAssocHandler *_handler = nullptr;
|
||||
HBITMAP _icon = nullptr;
|
||||
|
||||
};
|
||||
|
||||
HBITMAP IconToBitmap(LPWSTR icon, int iconindex) {
|
||||
if (!icon) return 0;
|
||||
WCHAR tmpIcon[4096];
|
||||
if (icon[0] == L'@' && SUCCEEDED(SHLoadIndirectString(icon, tmpIcon, 4096, 0))) {
|
||||
icon = tmpIcon;
|
||||
}
|
||||
int32 w = GetSystemMetrics(SM_CXSMICON), h = GetSystemMetrics(SM_CYSMICON);
|
||||
|
||||
HICON ico = ExtractIcon(0, icon, iconindex);
|
||||
if (!ico) {
|
||||
if (!iconindex) { // try to read image
|
||||
QImage img(QString::fromWCharArray(icon));
|
||||
if (!img.isNull()) {
|
||||
return qt_pixmapToWinHBITMAP(App::pixmapFromImageInPlace(img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)), /* HBitmapAlpha */ 2);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
HDC screenDC = GetDC(0), hdc = CreateCompatibleDC(screenDC);
|
||||
HBITMAP result = CreateCompatibleBitmap(screenDC, w, h);
|
||||
HGDIOBJ was = SelectObject(hdc, result);
|
||||
DrawIconEx(hdc, 0, 0, ico, w, h, 0, NULL, DI_NORMAL);
|
||||
SelectObject(hdc, was);
|
||||
DeleteDC(hdc);
|
||||
ReleaseDC(0, screenDC);
|
||||
|
||||
DestroyIcon(ico);
|
||||
|
||||
return (HBITMAP)CopyImage(result, IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE | LR_CREATEDIBSECTION);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void UnsafeOpenEmailLink(const QString &email) {
|
||||
auto url = QUrl(qstr("mailto:") + email);
|
||||
if (!QDesktopServices::openUrl(url)) {
|
||||
auto wstringUrl = url.toString(QUrl::FullyEncoded).toStdWString();
|
||||
if (Dlls::SHOpenWithDialog) {
|
||||
OPENASINFO info;
|
||||
info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_REGISTER_EXT | OAIF_EXEC | OAIF_FILE_IS_URI | OAIF_URL_PROTOCOL;
|
||||
info.pcszClass = NULL;
|
||||
info.pcszFile = wstringUrl.c_str();
|
||||
Dlls::SHOpenWithDialog(0, &info);
|
||||
} else if (Dlls::OpenAs_RunDLL) {
|
||||
Dlls::OpenAs_RunDLL(0, 0, wstringUrl.c_str(), SW_SHOWNORMAL);
|
||||
} else {
|
||||
ShellExecute(0, L"open", wstringUrl.c_str(), 0, 0, SW_SHOWNORMAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool UnsafeShowOpenWithDropdown(const QString &filepath, QPoint menuPosition) {
|
||||
auto window = App::wnd();
|
||||
if (!window) return false;
|
||||
|
||||
auto parentHWND = window->psHwnd();
|
||||
auto wstringPath = QDir::toNativeSeparators(filepath).toStdWString();
|
||||
|
||||
auto result = false;
|
||||
std::vector<OpenWithApp> handlers;
|
||||
IShellItem* pItem = nullptr;
|
||||
if (SUCCEEDED(Dlls::SHCreateItemFromParsingName(wstringPath.c_str(), nullptr, IID_PPV_ARGS(&pItem)))) {
|
||||
IEnumAssocHandlers *assocHandlers = nullptr;
|
||||
if (SUCCEEDED(pItem->BindToHandler(nullptr, BHID_EnumAssocHandlers, IID_PPV_ARGS(&assocHandlers)))) {
|
||||
HRESULT hr = S_FALSE;
|
||||
do {
|
||||
IAssocHandler *handler = nullptr;
|
||||
ULONG ulFetched = 0;
|
||||
hr = assocHandlers->Next(1, &handler, &ulFetched);
|
||||
if (FAILED(hr) || hr == S_FALSE || !ulFetched) break;
|
||||
|
||||
LPWSTR name = 0;
|
||||
if (SUCCEEDED(handler->GetUIName(&name))) {
|
||||
LPWSTR icon = 0;
|
||||
int iconindex = 0;
|
||||
if (SUCCEEDED(handler->GetIconLocation(&icon, &iconindex)) && icon) {
|
||||
handlers.push_back(OpenWithApp(QString::fromWCharArray(name), handler, IconToBitmap(icon, iconindex)));
|
||||
CoTaskMemFree(icon);
|
||||
} else {
|
||||
handlers.push_back(OpenWithApp(QString::fromWCharArray(name), handler));
|
||||
}
|
||||
CoTaskMemFree(name);
|
||||
} else {
|
||||
handler->Release();
|
||||
}
|
||||
} while (hr != S_FALSE);
|
||||
assocHandlers->Release();
|
||||
}
|
||||
|
||||
if (!handlers.empty()) {
|
||||
HMENU menu = CreatePopupMenu();
|
||||
std::sort(handlers.begin(), handlers.end(), [](const OpenWithApp &a, const OpenWithApp &b) {
|
||||
return a.name() < b.name();
|
||||
});
|
||||
for (int32 i = 0, l = handlers.size(); i < l; ++i) {
|
||||
MENUITEMINFO menuInfo = { 0 };
|
||||
menuInfo.cbSize = sizeof(menuInfo);
|
||||
menuInfo.fMask = MIIM_STRING | MIIM_DATA | MIIM_ID;
|
||||
menuInfo.fType = MFT_STRING;
|
||||
menuInfo.wID = i + 1;
|
||||
if (auto icon = handlers[i].icon()) {
|
||||
menuInfo.fMask |= MIIM_BITMAP;
|
||||
menuInfo.hbmpItem = icon;
|
||||
}
|
||||
|
||||
auto name = handlers[i].name();
|
||||
if (name.size() > 512) name = name.mid(0, 512);
|
||||
WCHAR nameArr[1024];
|
||||
name.toWCharArray(nameArr);
|
||||
nameArr[name.size()] = 0;
|
||||
menuInfo.dwTypeData = nameArr;
|
||||
InsertMenuItem(menu, GetMenuItemCount(menu), TRUE, &menuInfo);
|
||||
}
|
||||
MENUITEMINFO sepInfo = { 0 };
|
||||
sepInfo.cbSize = sizeof(sepInfo);
|
||||
sepInfo.fMask = MIIM_STRING | MIIM_DATA;
|
||||
sepInfo.fType = MFT_SEPARATOR;
|
||||
InsertMenuItem(menu, GetMenuItemCount(menu), true, &sepInfo);
|
||||
|
||||
MENUITEMINFO menuInfo = { 0 };
|
||||
menuInfo.cbSize = sizeof(menuInfo);
|
||||
menuInfo.fMask = MIIM_STRING | MIIM_DATA | MIIM_ID;
|
||||
menuInfo.fType = MFT_STRING;
|
||||
menuInfo.wID = handlers.size() + 1;
|
||||
|
||||
QString name = lang(lng_wnd_choose_program_menu);
|
||||
if (name.size() > 512) name = name.mid(0, 512);
|
||||
WCHAR nameArr[1024];
|
||||
name.toWCharArray(nameArr);
|
||||
nameArr[name.size()] = 0;
|
||||
menuInfo.dwTypeData = nameArr;
|
||||
InsertMenuItem(menu, GetMenuItemCount(menu), TRUE, &menuInfo);
|
||||
|
||||
int sel = TrackPopupMenu(menu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON | TPM_RETURNCMD, menuPosition.x(), menuPosition.y(), 0, parentHWND, 0);
|
||||
DestroyMenu(menu);
|
||||
|
||||
if (sel > 0) {
|
||||
if (sel <= handlers.size()) {
|
||||
IDataObject *dataObj = 0;
|
||||
if (SUCCEEDED(pItem->BindToHandler(nullptr, BHID_DataObject, IID_PPV_ARGS(&dataObj))) && dataObj) {
|
||||
handlers[sel - 1].handler()->Invoke(dataObj);
|
||||
dataObj->Release();
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
pItem->Release();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool UnsafeShowOpenWith(const QString &filepath) {
|
||||
auto wstringPath = QDir::toNativeSeparators(filepath).toStdWString();
|
||||
if (Dlls::SHOpenWithDialog) {
|
||||
OPENASINFO info;
|
||||
info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_REGISTER_EXT | OAIF_EXEC;
|
||||
info.pcszClass = NULL;
|
||||
info.pcszFile = wstringPath.c_str();
|
||||
Dlls::SHOpenWithDialog(0, &info);
|
||||
return true;
|
||||
} else if (Dlls::OpenAs_RunDLL) {
|
||||
Dlls::OpenAs_RunDLL(0, 0, wstringPath.c_str(), SW_SHOWNORMAL);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void UnsafeLaunch(const QString &filepath) {
|
||||
auto wstringPath = QDir::toNativeSeparators(filepath).toStdWString();
|
||||
ShellExecute(0, L"open", wstringPath.c_str(), 0, 0, SW_SHOWNORMAL);
|
||||
}
|
||||
|
||||
void UnsafeShowInFolder(const QString &filepath) {
|
||||
auto pathEscaped = QDir::toNativeSeparators(filepath).replace('"', qsl("\"\""));
|
||||
auto wstringParam = (qstr("/select,") + pathEscaped).toStdWString();
|
||||
ShellExecute(0, 0, L"explorer", wstringParam.c_str(), 0, SW_SHOWNORMAL);
|
||||
}
|
||||
|
||||
void PostprocessDownloaded(const QString &filepath) {
|
||||
auto wstringZoneFile = QDir::toNativeSeparators(filepath).toStdWString() + L":Zone.Identifier";
|
||||
auto f = CreateFile(wstringZoneFile.c_str(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
if (f == INVALID_HANDLE_VALUE) { // :(
|
||||
return;
|
||||
}
|
||||
|
||||
const char data[] = "[ZoneTransfer]\r\nZoneId=3\r\n";
|
||||
|
||||
DWORD written = 0;
|
||||
BOOL result = WriteFile(f, data, sizeof(data), &written, NULL);
|
||||
CloseHandle(f);
|
||||
|
||||
if (!result || written != sizeof(data)) { // :(
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace File
|
||||
|
||||
namespace FileDialog {
|
||||
namespace {
|
||||
|
||||
using Type = ::FileDialog::internal::Type;
|
||||
|
||||
} // namespace
|
||||
|
||||
void InitLastPath() {
|
||||
// hack to restore previous dir without hurting performance
|
||||
QSettings settings(QSettings::UserScope, qstr("QtProject"));
|
||||
settings.beginGroup(qstr("Qt"));
|
||||
QByteArray sd = settings.value(qstr("filedialog")).toByteArray();
|
||||
QDataStream stream(&sd, QIODevice::ReadOnly);
|
||||
if (!stream.atEnd()) {
|
||||
int version = 3, _QFileDialogMagic = 190;
|
||||
QByteArray splitterState;
|
||||
QByteArray headerData;
|
||||
QList<QUrl> bookmarks;
|
||||
QStringList history;
|
||||
QString currentDirectory;
|
||||
qint32 marker;
|
||||
qint32 v;
|
||||
qint32 viewMode;
|
||||
stream >> marker;
|
||||
stream >> v;
|
||||
if (marker == _QFileDialogMagic && v == version) {
|
||||
stream >> splitterState
|
||||
>> bookmarks
|
||||
>> history
|
||||
>> currentDirectory
|
||||
>> headerData
|
||||
>> viewMode;
|
||||
cSetDialogLastPath(currentDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
if (cDialogHelperPath().isEmpty()) {
|
||||
QDir temppath(cWorkingDir() + "tdata/tdummy/");
|
||||
if (!temppath.exists()) {
|
||||
temppath.mkpath(temppath.absolutePath());
|
||||
}
|
||||
if (temppath.exists()) {
|
||||
cSetDialogHelperPath(temppath.absolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Get(QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter, ::FileDialog::internal::Type type, QString startFile) {
|
||||
if (cDialogLastPath().isEmpty()) {
|
||||
Platform::FileDialog::InitLastPath();
|
||||
}
|
||||
|
||||
// A hack for fast dialog create. There was some huge performance problem
|
||||
// if we open a file dialog in some folder with a large amount of files.
|
||||
// Some internal Qt watcher iterated over all of them, querying some information
|
||||
// that forced file icon and maybe other properties being resolved and this was
|
||||
// a blocking operation.
|
||||
auto helperPath = cDialogHelperPathFinal();
|
||||
QFileDialog dialog(App::wnd() ? App::wnd()->filedialogParent() : 0, caption, helperPath, filter);
|
||||
|
||||
dialog.setModal(true);
|
||||
if (type == Type::ReadFile || type == Type::ReadFiles) {
|
||||
dialog.setFileMode((type == Type::ReadFiles) ? QFileDialog::ExistingFiles : QFileDialog::ExistingFile);
|
||||
dialog.setAcceptMode(QFileDialog::AcceptOpen);
|
||||
} else if (type == Type::ReadFolder) { // save dir
|
||||
dialog.setAcceptMode(QFileDialog::AcceptOpen);
|
||||
|
||||
// We use "obsolete" value ::DirectoryOnly instead of ::Directory + ::ShowDirsOnly
|
||||
// because in Windows XP native dialog this one works, while the "preferred" one
|
||||
// shows a native file choose dialog where you can't choose a directory, only open one.
|
||||
dialog.setFileMode(QFileDialog::DirectoryOnly);
|
||||
dialog.setOption(QFileDialog::ShowDirsOnly);
|
||||
} else { // save file
|
||||
dialog.setFileMode(QFileDialog::AnyFile);
|
||||
dialog.setAcceptMode(QFileDialog::AcceptSave);
|
||||
}
|
||||
dialog.show();
|
||||
|
||||
auto realLastPath = ([startFile] {
|
||||
// If we're given some non empty path containing a folder - use it.
|
||||
if (!startFile.isEmpty() && (startFile.indexOf('/') >= 0 || startFile.indexOf('\\') >= 0)) {
|
||||
return QFileInfo(startFile).dir().absolutePath();
|
||||
}
|
||||
return cDialogLastPath();
|
||||
})();
|
||||
if (realLastPath.isEmpty() || realLastPath.endsWith(qstr("/tdummy"))) {
|
||||
realLastPath = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
|
||||
}
|
||||
dialog.setDirectory(realLastPath);
|
||||
|
||||
if (type == Type::WriteFile) {
|
||||
auto toSelect = startFile;
|
||||
auto lastSlash = toSelect.lastIndexOf('/');
|
||||
if (lastSlash >= 0) {
|
||||
toSelect = toSelect.mid(lastSlash + 1);
|
||||
}
|
||||
auto lastBackSlash = toSelect.lastIndexOf('\\');
|
||||
if (lastBackSlash >= 0) {
|
||||
toSelect = toSelect.mid(lastBackSlash + 1);
|
||||
}
|
||||
dialog.selectFile(toSelect);
|
||||
}
|
||||
|
||||
int res = dialog.exec();
|
||||
|
||||
if (type != Type::ReadFolder) {
|
||||
// Save last used directory for all queries except directory choosing.
|
||||
auto path = dialog.directory().absolutePath();
|
||||
if (path != cDialogLastPath()) {
|
||||
cSetDialogLastPath(path);
|
||||
Local::writeUserSettings();
|
||||
}
|
||||
}
|
||||
|
||||
if (res == QDialog::Accepted) {
|
||||
if (type == Type::ReadFiles) {
|
||||
files = dialog.selectedFiles();
|
||||
} else {
|
||||
files = dialog.selectedFiles().mid(0, 1);
|
||||
}
|
||||
if (type == Type::ReadFile || type == Type::ReadFiles) {
|
||||
remoteContent = dialog.selectedRemoteContent();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
files = QStringList();
|
||||
remoteContent = QByteArray();
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace FileDialog
|
||||
} // namespace Platform
|
|
@ -30,16 +30,4 @@ inline QString UrlToLocal(const QUrl &url) {
|
|||
}
|
||||
|
||||
} // namespace File
|
||||
|
||||
namespace FileDialog {
|
||||
|
||||
inline bool Supported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool Get(QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter, ::FileDialog::internal::Type type, QString startFile) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace FileDialog
|
||||
} // namespace Platform
|
||||
|
|
|
@ -26,7 +26,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "profile/profile_cover_drop_area.h"
|
||||
#include "profile/profile_userpic_button.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "observer_peer.h"
|
||||
#include "boxes/confirmbox.h"
|
||||
|
|
|
@ -21,7 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#pragma once
|
||||
|
||||
#include "core/observer.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
|
||||
namespace style {
|
||||
struct RoundButton;
|
||||
|
|
|
@ -23,7 +23,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "application.h"
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
#include "platform/linux/file_utilities_linux.h"
|
||||
#include "localstorage.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
@ -38,32 +38,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include <qpa/qplatformnativeinterface.h>
|
||||
|
||||
using namespace Platform;
|
||||
using Platform::File::internal::EscapeShell;
|
||||
|
||||
namespace {
|
||||
|
||||
QByteArray escapeShell(const QByteArray &str) {
|
||||
QByteArray result;
|
||||
const char *b = str.constData(), *e = str.constEnd();
|
||||
for (const char *ch = b; ch != e; ++ch) {
|
||||
if (*ch == ' ' || *ch == '"' || *ch == '\'' || *ch == '\\') {
|
||||
if (result.isEmpty()) {
|
||||
result.reserve(str.size() * 2);
|
||||
}
|
||||
if (ch > b) {
|
||||
result.append(b, ch - b);
|
||||
}
|
||||
result.append('\\');
|
||||
b = ch;
|
||||
}
|
||||
}
|
||||
if (result.isEmpty()) return str;
|
||||
|
||||
if (e > b) {
|
||||
result.append(b, e - b);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
class _PsEventFilter : public QAbstractNativeEventFilter {
|
||||
public:
|
||||
bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) {
|
||||
|
@ -129,7 +107,7 @@ QStringList addr2linestr(uint64 *addresses, int count) {
|
|||
if (!count) return result;
|
||||
|
||||
result.reserve(count);
|
||||
QByteArray cmd = "addr2line -e " + escapeShell(QFile::encodeName(cExeDir() + cExeName()));
|
||||
QByteArray cmd = "addr2line -e " + EscapeShell(QFile::encodeName(cExeDir() + cExeName()));
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (addresses[i]) {
|
||||
cmd += qsl(" 0x%1").arg(addresses[i], 0, 16).toUtf8();
|
||||
|
@ -387,18 +365,6 @@ int psFixPrevious() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void psPostprocessFile(const QString &name) {
|
||||
}
|
||||
|
||||
void psOpenFile(const QString &name, bool openWith) {
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(name));
|
||||
}
|
||||
|
||||
void psShowInFolder(const QString &name) {
|
||||
Ui::hideLayer(true);
|
||||
system(("xdg-open " + escapeShell(QFile::encodeName(QFileInfo(name).absoluteDir().absolutePath()))).constData());
|
||||
}
|
||||
|
||||
namespace Platform {
|
||||
|
||||
void start() {
|
||||
|
@ -503,7 +469,7 @@ void psRegisterCustomScheme() {
|
|||
s << "Version=1.0\n";
|
||||
s << "Name=Telegram Desktop\n";
|
||||
s << "Comment=Official desktop version of Telegram messaging app\n";
|
||||
s << "Exec=" << escapeShell(QFile::encodeName(cExeDir() + cExeName())) << " -- %u\n";
|
||||
s << "Exec=" << EscapeShell(QFile::encodeName(cExeDir() + cExeName())) << " -- %u\n";
|
||||
s << "Icon=telegram\n";
|
||||
s << "Terminal=false\n";
|
||||
s << "StartupWMClass=Telegram\n";
|
||||
|
@ -512,11 +478,11 @@ void psRegisterCustomScheme() {
|
|||
s << "MimeType=x-scheme-handler/tg;\n";
|
||||
f.close();
|
||||
|
||||
if (_psRunCommand("desktop-file-install --dir=" + escapeShell(QFile::encodeName(home + qsl(".local/share/applications"))) + " --delete-original " + escapeShell(QFile::encodeName(file)))) {
|
||||
if (_psRunCommand("desktop-file-install --dir=" + EscapeShell(QFile::encodeName(home + qsl(".local/share/applications"))) + " --delete-original " + EscapeShell(QFile::encodeName(file)))) {
|
||||
DEBUG_LOG(("App Info: removing old .desktop file"));
|
||||
QFile(qsl("%1.local/share/applications/telegram.desktop").arg(home)).remove();
|
||||
|
||||
_psRunCommand("update-desktop-database " + escapeShell(QFile::encodeName(home + qsl(".local/share/applications"))));
|
||||
_psRunCommand("update-desktop-database " + EscapeShell(QFile::encodeName(home + qsl(".local/share/applications"))));
|
||||
_psRunCommand("xdg-mime default telegramdesktop.desktop x-scheme-handler/tg");
|
||||
}
|
||||
} else {
|
||||
|
@ -526,7 +492,7 @@ void psRegisterCustomScheme() {
|
|||
#endif // !TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION
|
||||
|
||||
DEBUG_LOG(("App Info: registerting for Gnome"));
|
||||
if (_psRunCommand("gconftool-2 -t string -s /desktop/gnome/url-handlers/tg/command " + escapeShell(escapeShell(QFile::encodeName(cExeDir() + cExeName())) + " -- %s"))) {
|
||||
if (_psRunCommand("gconftool-2 -t string -s /desktop/gnome/url-handlers/tg/command " + EscapeShell(EscapeShell(QFile::encodeName(cExeDir() + cExeName())) + " -- %s"))) {
|
||||
_psRunCommand("gconftool-2 -t bool -s /desktop/gnome/url-handlers/tg/needs_terminal false");
|
||||
_psRunCommand("gconftool-2 -t bool -s /desktop/gnome/url-handlers/tg/enabled true");
|
||||
}
|
||||
|
@ -547,7 +513,7 @@ void psRegisterCustomScheme() {
|
|||
QTextStream s(&f);
|
||||
s.setCodec("UTF-8");
|
||||
s << "[Protocol]\n";
|
||||
s << "exec=" << QFile::decodeName(escapeShell(QFile::encodeName(cExeDir() + cExeName()))) << " -- %u\n";
|
||||
s << "exec=" << QFile::decodeName(EscapeShell(QFile::encodeName(cExeDir() + cExeName()))) << " -- %u\n";
|
||||
s << "protocol=tg\n";
|
||||
s << "input=none\n";
|
||||
s << "output=none\n";
|
||||
|
|
|
@ -63,12 +63,6 @@ int psFixPrevious();
|
|||
void psExecUpdater();
|
||||
void psExecTelegram(const QString &arg = QString());
|
||||
|
||||
bool psShowOpenWithMenu(int x, int y, const QString &file);
|
||||
|
||||
void psPostprocessFile(const QString &name);
|
||||
void psOpenFile(const QString &name, bool openWith = false);
|
||||
void psShowInFolder(const QString &name);
|
||||
|
||||
QAbstractNativeEventFilter *psNativeEventFilter();
|
||||
|
||||
void psNewVersion();
|
||||
|
|
|
@ -384,21 +384,6 @@ int psFixPrevious() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool psShowOpenWithMenu(int x, int y, const QString &file) {
|
||||
return objc_showOpenWithMenu(x, y, file);
|
||||
}
|
||||
|
||||
void psPostprocessFile(const QString &name) {
|
||||
}
|
||||
|
||||
void psOpenFile(const QString &name, bool openWith) {
|
||||
objc_openFile(name, openWith);
|
||||
}
|
||||
|
||||
void psShowInFolder(const QString &name) {
|
||||
objc_showInFinder(name, QFileInfo(name).absolutePath());
|
||||
}
|
||||
|
||||
namespace Platform {
|
||||
|
||||
void start() {
|
||||
|
@ -413,7 +398,7 @@ void finish() {
|
|||
}
|
||||
|
||||
bool TransparentWindowsSupported(QPoint globalPosition) {
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace ThirdParty {
|
||||
|
@ -468,35 +453,20 @@ bool psLaunchMaps(const LocationCoords &coords) {
|
|||
|
||||
QString strNotificationAboutThemeChange() {
|
||||
const uint32 letters[] = { 0xE9005541, 0x5600DC70, 0x88001570, 0xF500D86C, 0x8100E165, 0xEE005949, 0x2900526E, 0xAE00FB74, 0x96000865, 0x7000CD72, 0x3B001566, 0x5F007361, 0xAE00B663, 0x74009A65, 0x29003054, 0xC6002668, 0x98003865, 0xFA00336D, 0xA3007A65, 0x93001443, 0xBB007868, 0xE100E561, 0x3500366E, 0xC0007A67, 0x0200CA65, 0xBE00DF64, 0xE300BB4E, 0x2900D26F, 0xD500D374, 0xE900E269, 0x86008F66, 0xC4006669, 0x1C00A863, 0xE600A761, 0x8E00EE74, 0xB300B169, 0xCF00B36F, 0xE600D36E };
|
||||
return strMakeFromLetters(letters, sizeof(letters) / sizeof(letters[0]));
|
||||
return strMakeFromLetters(letters);
|
||||
}
|
||||
|
||||
QString strNotificationAboutScreenLocked() {
|
||||
const uint32 letters[] = { 0x22008263, 0x0800DB6F, 0x45004F6D, 0xCC00972E, 0x0E00A861, 0x9700D970, 0xA100D570, 0x8900686C, 0xB300B365, 0xFE00DE2E, 0x76009B73, 0xFA00BF63, 0xE000A772, 0x9C009F65, 0x4E006065, 0xD900426E, 0xB7007849, 0x64006473, 0x6700824C, 0xE300706F, 0x7C00A063, 0x8F00D76B, 0x04001C65, 0x1C00A664 };
|
||||
return strMakeFromLetters(letters, base::array_size(letters));
|
||||
return strMakeFromLetters(letters);
|
||||
}
|
||||
|
||||
QString strNotificationAboutScreenUnlocked() {
|
||||
const uint32 letters[] = { 0x9200D763, 0xC8003C6F, 0xD2003F6D, 0x6000012E, 0x36004061, 0x4400E570, 0xA500BF70, 0x2E00796C, 0x4A009E65, 0x2E00612E, 0xC8001D73, 0x57002263, 0xF0005872, 0x49000765, 0xE5008D65, 0xE600D76E, 0xE8007049, 0x19005C73, 0x34009455, 0xB800B36E, 0xF300CA6C, 0x4C00806F, 0x5300A763, 0xD1003B6B, 0x63003565, 0xF800F264 };
|
||||
return strMakeFromLetters(letters, base::array_size(letters));
|
||||
return strMakeFromLetters(letters);
|
||||
}
|
||||
|
||||
QString strStyleOfInterface() {
|
||||
const uint32 letters[] = { 0xEF004041, 0x4C007F70, 0x1F007A70, 0x9E00A76C, 0x8500D165, 0x2E003749, 0x7B00526E, 0x3400E774, 0x3C00FA65, 0x6200B172, 0xF7001D66, 0x0B002961, 0x71008C63, 0x86005465, 0xA3006F53, 0x11006174, 0xCD001779, 0x8200556C, 0x6C009B65 };
|
||||
return strMakeFromLetters(letters, sizeof(letters) / sizeof(letters[0]));
|
||||
}
|
||||
|
||||
QString strNeedToReload() {
|
||||
const uint32 letters[] = { 0x82007746, 0xBB00C649, 0x7E00235F, 0x9A00FE54, 0x4C004542, 0x91001772, 0x8A00D76F, 0xC700B977, 0x7F005F73, 0x34003665, 0x2300D572, 0x72002E54, 0x18001461, 0x14004A62, 0x5100CC6C, 0x83002365, 0x5A002C56, 0xA5004369, 0x26004265, 0x0D006577 };
|
||||
return strMakeFromLetters(letters, sizeof(letters) / sizeof(letters[0]));
|
||||
}
|
||||
|
||||
QString strNeedToRefresh1() {
|
||||
const uint32 letters[] = { 0xEF006746, 0xF500CE49, 0x1500715F, 0x95001254, 0x3A00CB4C, 0x17009469, 0xB400DA73, 0xDE00C574, 0x9200EC56, 0x3C00A669, 0xFD00D865, 0x59000977 };
|
||||
return strMakeFromLetters(letters, sizeof(letters) / sizeof(letters[0]));
|
||||
}
|
||||
|
||||
QString strNeedToRefresh2() {
|
||||
const uint32 letters[] = { 0x8F001546, 0xAF007A49, 0xB8002B5F, 0x1A000B54, 0x0D003E49, 0xE0003663, 0x4900796F, 0x0500836E, 0x9A00D156, 0x5E00FF69, 0x5900C765, 0x3D00D177 };
|
||||
return strMakeFromLetters(letters, sizeof(letters) / sizeof(letters[0]));
|
||||
return strMakeFromLetters(letters);
|
||||
}
|
||||
|
|
|
@ -68,10 +68,6 @@ void psExecTelegram(const QString &crashreport = QString());
|
|||
|
||||
bool psShowOpenWithMenu(int x, int y, const QString &file);
|
||||
|
||||
void psPostprocessFile(const QString &name);
|
||||
void psOpenFile(const QString &name, bool openWith = false);
|
||||
void psShowInFolder(const QString &name);
|
||||
|
||||
QAbstractNativeEventFilter *psNativeEventFilter();
|
||||
|
||||
void psNewVersion();
|
||||
|
@ -111,8 +107,5 @@ QString strNotificationAboutThemeChange();
|
|||
QString strNotificationAboutScreenLocked();
|
||||
QString strNotificationAboutScreenUnlocked();
|
||||
QString strStyleOfInterface();
|
||||
QString strNeedToReload();
|
||||
QString strNeedToRefresh1();
|
||||
QString strNeedToRefresh2();
|
||||
|
||||
bool psLaunchMaps(const LocationCoords &coords);
|
||||
|
|
|
@ -31,10 +31,6 @@ void objc_outputDebugString(const QString &str);
|
|||
bool objc_idleSupported();
|
||||
bool objc_idleTime(TimeMs &idleTime);
|
||||
|
||||
bool objc_showOpenWithMenu(int x, int y, const QString &file);
|
||||
|
||||
void objc_showInFinder(const QString &file, const QString &path);
|
||||
void objc_openFile(const QString &file, bool openwith);
|
||||
void objc_start();
|
||||
void objc_finish();
|
||||
bool objc_execUpdater();
|
||||
|
|
|
@ -325,516 +325,6 @@ bool objc_idleTime(TimeMs &idleTime) { // taken from https://github.com/trueinte
|
|||
return true;
|
||||
}
|
||||
|
||||
@interface OpenWithApp : NSObject {
|
||||
NSString *fullname;
|
||||
NSURL *app;
|
||||
NSImage *icon;
|
||||
}
|
||||
@property (nonatomic, retain) NSString *fullname;
|
||||
@property (nonatomic, retain) NSURL *app;
|
||||
@property (nonatomic, retain) NSImage *icon;
|
||||
@end
|
||||
|
||||
@implementation OpenWithApp
|
||||
@synthesize fullname, app, icon;
|
||||
|
||||
- (void) dealloc {
|
||||
[fullname release];
|
||||
[app release];
|
||||
[icon release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface OpenFileWithInterface : NSObject {
|
||||
}
|
||||
|
||||
- (id) init:(NSString *)file;
|
||||
- (BOOL) popupAtX:(int)x andY:(int)y;
|
||||
- (void) itemChosen:(id)sender;
|
||||
- (void) dealloc;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OpenFileWithInterface {
|
||||
NSString *toOpen;
|
||||
|
||||
NSURL *defUrl;
|
||||
NSString *defBundle, *defName, *defVersion;
|
||||
NSImage *defIcon;
|
||||
|
||||
NSMutableArray *apps;
|
||||
|
||||
NSMenu *menu;
|
||||
}
|
||||
|
||||
- (void) fillAppByUrl:(NSURL*)url bundle:(NSString**)bundle name:(NSString**)name version:(NSString**)version icon:(NSImage**)icon {
|
||||
NSBundle *b = [NSBundle bundleWithURL:url];
|
||||
if (b) {
|
||||
NSString *path = [url path];
|
||||
*name = [[NSFileManager defaultManager] displayNameAtPath: path];
|
||||
if (!*name) *name = (NSString*)[b objectForInfoDictionaryKey:@"CFBundleDisplayName"];
|
||||
if (!*name) *name = (NSString*)[b objectForInfoDictionaryKey:@"CFBundleName"];
|
||||
if (*name) {
|
||||
*bundle = [b bundleIdentifier];
|
||||
if (bundle) {
|
||||
*version = (NSString*)[b objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
|
||||
*icon = [[NSWorkspace sharedWorkspace] iconForFile: path];
|
||||
if (*icon && [*icon isValid]) [*icon setSize: CGSizeMake(16., 16.)];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
*bundle = *name = *version = nil;
|
||||
*icon = nil;
|
||||
}
|
||||
|
||||
- (id) init:(NSString*)file {
|
||||
toOpen = [file retain];
|
||||
if (self = [super init]) {
|
||||
NSURL *url = [NSURL fileURLWithPath:file];
|
||||
defUrl = [[NSWorkspace sharedWorkspace] URLForApplicationToOpenURL:url];
|
||||
if (defUrl) {
|
||||
[self fillAppByUrl:defUrl bundle:&defBundle name:&defName version:&defVersion icon:&defIcon];
|
||||
if (!defBundle || !defName) {
|
||||
defUrl = nil;
|
||||
}
|
||||
}
|
||||
NSArray *appsList = (NSArray*)LSCopyApplicationURLsForURL(CFURLRef(url), kLSRolesAll);
|
||||
NSMutableDictionary *data = [NSMutableDictionary dictionaryWithCapacity:16];
|
||||
int fullcount = 0;
|
||||
for (id app in appsList) {
|
||||
if (fullcount > 15) break;
|
||||
|
||||
NSString *bundle = nil, *name = nil, *version = nil;
|
||||
NSImage *icon = nil;
|
||||
[self fillAppByUrl:(NSURL*)app bundle:&bundle name:&name version:&version icon:&icon];
|
||||
if (bundle && name) {
|
||||
if ([bundle isEqualToString:defBundle] && [version isEqualToString:defVersion]) continue;
|
||||
NSString *key = [[NSArray arrayWithObjects:bundle, name, nil] componentsJoinedByString:@"|"];
|
||||
if (!version) version = @"";
|
||||
|
||||
NSMutableDictionary *versions = (NSMutableDictionary*)[data objectForKey:key];
|
||||
if (!versions) {
|
||||
versions = [NSMutableDictionary dictionaryWithCapacity:2];
|
||||
[data setValue:versions forKey:key];
|
||||
}
|
||||
if (![versions objectForKey:version]) {
|
||||
[versions setValue:[NSArray arrayWithObjects:name, icon, app, nil] forKey:version];
|
||||
++fullcount;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fullcount || defUrl) {
|
||||
apps = [NSMutableArray arrayWithCapacity:fullcount];
|
||||
for (id key in data) {
|
||||
NSMutableDictionary *val = (NSMutableDictionary*)[data objectForKey:key];
|
||||
for (id ver in val) {
|
||||
NSArray *app = (NSArray*)[val objectForKey:ver];
|
||||
OpenWithApp *a = [[OpenWithApp alloc] init];
|
||||
NSString *fullname = (NSString*)[app objectAtIndex:0], *version = (NSString*)ver;
|
||||
BOOL showVersion = ([val count] > 1);
|
||||
if (!showVersion) {
|
||||
NSError *error = NULL;
|
||||
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^\\d+\\.\\d+\\.\\d+(\\.\\d+)?$" options:NSRegularExpressionCaseInsensitive error:&error];
|
||||
showVersion = ![regex numberOfMatchesInString:version options:NSMatchingWithoutAnchoringBounds range:{0,[version length]}];
|
||||
}
|
||||
if (showVersion) fullname = [[NSArray arrayWithObjects:fullname, @" (", version, @")", nil] componentsJoinedByString:@""];
|
||||
[a setFullname:fullname];
|
||||
[a setIcon:(NSImage*)[app objectAtIndex:1]];
|
||||
[a setApp:(NSURL*)[app objectAtIndex:2]];
|
||||
[apps addObject:a];
|
||||
[a release];
|
||||
}
|
||||
}
|
||||
}
|
||||
[apps sortUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"fullname" ascending:YES]]];
|
||||
[appsList release];
|
||||
menu = nil;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL) popupAtX:(int)x andY:(int)y {
|
||||
if (![apps count] && !defName) return NO;
|
||||
menu = [[NSMenu alloc] initWithTitle:@"Open With"];
|
||||
|
||||
int index = 0;
|
||||
if (defName) {
|
||||
NSMenuItem *item = [menu insertItemWithTitle:[[NSArray arrayWithObjects:defName, @" (default)", nil] componentsJoinedByString:@""] action:@selector(itemChosen:) keyEquivalent:@"" atIndex:index++];
|
||||
if (defIcon) [item setImage:defIcon];
|
||||
[item setTarget:self];
|
||||
[menu insertItem:[NSMenuItem separatorItem] atIndex:index++];
|
||||
}
|
||||
if ([apps count]) {
|
||||
for (id a in apps) {
|
||||
OpenWithApp *app = (OpenWithApp*)a;
|
||||
NSMenuItem *item = [menu insertItemWithTitle:[a fullname] action:@selector(itemChosen:) keyEquivalent:@"" atIndex:index++];
|
||||
if ([app icon]) [item setImage:[app icon]];
|
||||
[item setTarget:self];
|
||||
}
|
||||
[menu insertItem:[NSMenuItem separatorItem] atIndex:index++];
|
||||
}
|
||||
NSMenuItem *item = [menu insertItemWithTitle:NSlang(lng_mac_choose_program_menu) action:@selector(itemChosen:) keyEquivalent:@"" atIndex:index++];
|
||||
[item setTarget:self];
|
||||
|
||||
[menu popUpMenuPositioningItem:nil atLocation:CGPointMake(x, y) inView:nil];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void) itemChosen:(id)sender {
|
||||
NSArray *items = [menu itemArray];
|
||||
NSURL *url = nil;
|
||||
for (int i = 0, l = [items count]; i < l; ++i) {
|
||||
if ([items objectAtIndex:i] == sender) {
|
||||
if (defName) i -= 2;
|
||||
if (i < 0) {
|
||||
url = defUrl;
|
||||
} else if (i < int([apps count])) {
|
||||
url = [(OpenWithApp*)[apps objectAtIndex:i] app];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (url) {
|
||||
[[NSWorkspace sharedWorkspace] openFile:toOpen withApplication:[url path]];
|
||||
} else {
|
||||
objc_openFile(NS2QString(toOpen), true);
|
||||
}
|
||||
}
|
||||
|
||||
- (void) dealloc {
|
||||
[toOpen release];
|
||||
if (menu) [menu release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
bool objc_showOpenWithMenu(int x, int y, const QString &f) {
|
||||
@autoreleasepool {
|
||||
|
||||
NSString *file = Q2NSString(f);
|
||||
@try {
|
||||
OpenFileWithInterface *menu = [[[OpenFileWithInterface alloc] init:file] autorelease];
|
||||
QRect r = QApplication::desktop()->screenGeometry(QPoint(x, y));
|
||||
y = r.y() + r.height() - y;
|
||||
return !![menu popupAtX:x andY:y];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
}
|
||||
@finally {
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void objc_showInFinder(const QString &file, const QString &path) {
|
||||
@autoreleasepool {
|
||||
|
||||
[[NSWorkspace sharedWorkspace] selectFile:Q2NSString(file) inFileViewerRootedAtPath:Q2NSString(path)];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@interface NSURL(CompareUrls)
|
||||
|
||||
- (BOOL) isEquivalent:(NSURL *)aURL;
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSURL(CompareUrls)
|
||||
|
||||
- (BOOL) isEquivalent:(NSURL *)aURL {
|
||||
if ([self isEqual:aURL]) return YES;
|
||||
if ([[self scheme] caseInsensitiveCompare:[aURL scheme]] != NSOrderedSame) return NO;
|
||||
if ([[self host] caseInsensitiveCompare:[aURL host]] != NSOrderedSame) return NO;
|
||||
if ([[self path] compare:[aURL path]] != NSOrderedSame) return NO;
|
||||
if ([[self port] compare:[aURL port]] != NSOrderedSame) return NO;
|
||||
if ([[self query] compare:[aURL query]] != NSOrderedSame) return NO;
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface ChooseApplicationDelegate : NSObject<NSOpenSavePanelDelegate> {
|
||||
}
|
||||
|
||||
- (id) init:(NSArray *)recommendedApps withPanel:(NSOpenPanel *)creator withSelector:(NSPopUpButton *)menu withGood:(NSTextField *)goodLabel withBad:(NSTextField *)badLabel withIcon:(NSImageView *)badIcon withAccessory:(NSView *)acc;
|
||||
- (BOOL) panel:(id)sender shouldEnableURL:(NSURL *)url;
|
||||
- (void) panelSelectionDidChange:(id)sender;
|
||||
- (void) menuDidClose;
|
||||
- (void) dealloc;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ChooseApplicationDelegate {
|
||||
BOOL onlyRecommended;
|
||||
NSArray *apps;
|
||||
NSOpenPanel *panel;
|
||||
NSPopUpButton *selector;
|
||||
NSTextField *good, *bad;
|
||||
NSImageView *icon;
|
||||
NSString *recom;
|
||||
NSView *accessory;
|
||||
}
|
||||
|
||||
- (id) init:(NSArray *)recommendedApps withPanel:(NSOpenPanel *)creator withSelector:(NSPopUpButton *)menu withGood:(NSTextField *)goodLabel withBad:(NSTextField *)badLabel withIcon:(NSImageView *)badIcon withAccessory:(NSView *)acc {
|
||||
if (self = [super init]) {
|
||||
onlyRecommended = YES;
|
||||
recom = [NSlang(lng_mac_recommended_apps) copy];
|
||||
apps = recommendedApps;
|
||||
panel = creator;
|
||||
selector = menu;
|
||||
good = goodLabel;
|
||||
bad = badLabel;
|
||||
icon = badIcon;
|
||||
accessory = acc;
|
||||
[selector setAction:@selector(menuDidClose)];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL) isRecommended:(NSURL *)url {
|
||||
if (apps) {
|
||||
for (id app in apps) {
|
||||
if ([(NSURL*)app isEquivalent:url]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL) panel:(id)sender shouldEnableURL:(NSURL *)url {
|
||||
NSNumber *isDirectory;
|
||||
if ([url getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil] && isDirectory != nil && [isDirectory boolValue]) {
|
||||
if (onlyRecommended) {
|
||||
CFStringRef ext = CFURLCopyPathExtension((CFURLRef)url);
|
||||
NSNumber *isPackage;
|
||||
if ([url getResourceValue:&isPackage forKey:NSURLIsPackageKey error:nil] && isPackage != nil && [isPackage boolValue]) {
|
||||
return [self isRecommended:url];
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void) panelSelectionDidChange:(id)sender {
|
||||
NSArray *urls = [panel URLs];
|
||||
if ([urls count]) {
|
||||
if ([self isRecommended:[urls firstObject]]) {
|
||||
[bad removeFromSuperview];
|
||||
[icon removeFromSuperview];
|
||||
[accessory addSubview:good];
|
||||
} else {
|
||||
[good removeFromSuperview];
|
||||
[accessory addSubview:bad];
|
||||
[accessory addSubview:icon];
|
||||
}
|
||||
} else {
|
||||
[good removeFromSuperview];
|
||||
[bad removeFromSuperview];
|
||||
[icon removeFromSuperview];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) menuDidClose {
|
||||
onlyRecommended = [[[selector selectedItem] title] isEqualToString:recom];
|
||||
[self refreshPanelTable];
|
||||
}
|
||||
|
||||
- (BOOL) refreshDataInViews: (NSArray*)subviews {
|
||||
for (id view in subviews) {
|
||||
NSString *cls = [view className];
|
||||
if ([cls isEqualToString:Q2NSString(strNeedToReload())]) {
|
||||
[view reloadData];
|
||||
} else if ([cls isEqualToString:Q2NSString(strNeedToRefresh1())] || [cls isEqualToString:Q2NSString(strNeedToRefresh2())]) {
|
||||
[view reloadData];
|
||||
return YES;
|
||||
} else {
|
||||
NSArray *next = [view subviews];
|
||||
if ([next count] && [self refreshDataInViews:next]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
- (void) refreshPanelTable {
|
||||
@autoreleasepool {
|
||||
|
||||
[self refreshDataInViews:[[panel contentView] subviews]];
|
||||
[panel validateVisibleColumns];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
- (void) dealloc {
|
||||
if (apps) {
|
||||
[apps release];
|
||||
[recom release];
|
||||
}
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
void objc_openFile(const QString &f, bool openwith) {
|
||||
@autoreleasepool {
|
||||
|
||||
NSString *file = Q2NSString(f);
|
||||
if (openwith || [[NSWorkspace sharedWorkspace] openFile:file] == NO) {
|
||||
@try {
|
||||
NSURL *url = [NSURL fileURLWithPath:file];
|
||||
NSString *ext = [url pathExtension];
|
||||
NSArray *names = [url pathComponents];
|
||||
NSString *name = [names count] ? [names lastObject] : @"";
|
||||
NSArray *apps = (NSArray*)LSCopyApplicationURLsForURL(CFURLRef(url), kLSRolesAll);
|
||||
|
||||
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
|
||||
|
||||
NSRect fullRect = { { 0., 0. }, { st::macAccessoryWidth, st::macAccessoryHeight } };
|
||||
NSView *accessory = [[NSView alloc] initWithFrame:fullRect];
|
||||
|
||||
[accessory setAutoresizesSubviews:YES];
|
||||
|
||||
NSPopUpButton *selector = [[NSPopUpButton alloc] init];
|
||||
[accessory addSubview:selector];
|
||||
[selector addItemWithTitle:NSlang(lng_mac_recommended_apps)];
|
||||
[selector addItemWithTitle:NSlang(lng_mac_all_apps)];
|
||||
[selector sizeToFit];
|
||||
|
||||
NSTextField *enableLabel = [[NSTextField alloc] init];
|
||||
[accessory addSubview:enableLabel];
|
||||
[enableLabel setStringValue:NSlang(lng_mac_enable_filter)];
|
||||
[enableLabel setFont:[selector font]];
|
||||
[enableLabel setBezeled:NO];
|
||||
[enableLabel setDrawsBackground:NO];
|
||||
[enableLabel setEditable:NO];
|
||||
[enableLabel setSelectable:NO];
|
||||
[enableLabel sizeToFit];
|
||||
|
||||
NSRect selectorFrame = [selector frame], enableFrame = [enableLabel frame];
|
||||
enableFrame.size.width += st::macEnableFilterAdd;
|
||||
enableFrame.origin.x = (fullRect.size.width - selectorFrame.size.width - enableFrame.size.width) / 2.;
|
||||
selectorFrame.origin.x = (fullRect.size.width - selectorFrame.size.width + enableFrame.size.width) / 2.;
|
||||
enableFrame.origin.y = fullRect.size.height - selectorFrame.size.height - st::macEnableFilterTop + (selectorFrame.size.height - enableFrame.size.height) / 2.;
|
||||
selectorFrame.origin.y = fullRect.size.height - selectorFrame.size.height - st::macSelectorTop;
|
||||
[enableLabel setFrame:enableFrame];
|
||||
[enableLabel setAutoresizingMask:NSViewMinXMargin|NSViewMaxXMargin];
|
||||
[selector setFrame:selectorFrame];
|
||||
[selector setAutoresizingMask:NSViewMinXMargin|NSViewMaxXMargin];
|
||||
|
||||
NSButton *button = [[NSButton alloc] init];
|
||||
[accessory addSubview:button];
|
||||
[button setButtonType:NSSwitchButton];
|
||||
[button setFont:[selector font]];
|
||||
[button setTitle:NSlang(lng_mac_always_open_with)];
|
||||
[button sizeToFit];
|
||||
NSRect alwaysRect = [button frame];
|
||||
alwaysRect.origin.x = (fullRect.size.width - alwaysRect.size.width) / 2;
|
||||
alwaysRect.origin.y = selectorFrame.origin.y - alwaysRect.size.height - st::macAlwaysThisAppTop;
|
||||
[button setFrame:alwaysRect];
|
||||
[button setAutoresizingMask:NSViewMinXMargin|NSViewMaxXMargin];
|
||||
#ifdef OS_MAC_STORE
|
||||
[button setHidden:YES];
|
||||
#endif // OS_MAC_STORE
|
||||
NSTextField *goodLabel = [[NSTextField alloc] init];
|
||||
[goodLabel setStringValue:Q2NSString(lng_mac_this_app_can_open(lt_file, NS2QString(name)))];
|
||||
[goodLabel setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
|
||||
[goodLabel setBezeled:NO];
|
||||
[goodLabel setDrawsBackground:NO];
|
||||
[goodLabel setEditable:NO];
|
||||
[goodLabel setSelectable:NO];
|
||||
[goodLabel sizeToFit];
|
||||
NSRect goodFrame = [goodLabel frame];
|
||||
goodFrame.origin.x = (fullRect.size.width - goodFrame.size.width) / 2.;
|
||||
goodFrame.origin.y = alwaysRect.origin.y - goodFrame.size.height - st::macAppHintTop;
|
||||
[goodLabel setFrame:goodFrame];
|
||||
|
||||
NSTextField *badLabel = [[NSTextField alloc] init];
|
||||
[badLabel setStringValue:Q2NSString(lng_mac_not_known_app(lt_file, NS2QString(name)))];
|
||||
[badLabel setFont:[goodLabel font]];
|
||||
[badLabel setBezeled:NO];
|
||||
[badLabel setDrawsBackground:NO];
|
||||
[badLabel setEditable:NO];
|
||||
[badLabel setSelectable:NO];
|
||||
[badLabel sizeToFit];
|
||||
NSImageView *badIcon = [[NSImageView alloc] init];
|
||||
NSImage *badImage = [NSImage imageNamed:NSImageNameCaution];
|
||||
[badIcon setImage:badImage];
|
||||
[badIcon setFrame:NSMakeRect(0, 0, st::macCautionIconSize, st::macCautionIconSize)];
|
||||
|
||||
NSRect badFrame = [badLabel frame], badIconFrame = [badIcon frame];
|
||||
badFrame.origin.x = (fullRect.size.width - badFrame.size.width + badIconFrame.size.width) / 2.;
|
||||
badIconFrame.origin.x = (fullRect.size.width - badFrame.size.width - badIconFrame.size.width) / 2.;
|
||||
badFrame.origin.y = alwaysRect.origin.y - badFrame.size.height - st::macAppHintTop;
|
||||
badIconFrame.origin.y = badFrame.origin.y;
|
||||
[badLabel setFrame:badFrame];
|
||||
[badIcon setFrame:badIconFrame];
|
||||
|
||||
[openPanel setAccessoryView:accessory];
|
||||
|
||||
ChooseApplicationDelegate *delegate = [[ChooseApplicationDelegate alloc] init:apps withPanel:openPanel withSelector:selector withGood:goodLabel withBad:badLabel withIcon:badIcon withAccessory:accessory];
|
||||
[openPanel setDelegate:delegate];
|
||||
|
||||
[openPanel setCanChooseDirectories:NO];
|
||||
[openPanel setCanChooseFiles:YES];
|
||||
[openPanel setAllowsMultipleSelection:NO];
|
||||
[openPanel setResolvesAliases:YES];
|
||||
[openPanel setTitle:NSlang(lng_mac_choose_app)];
|
||||
[openPanel setMessage:Q2NSString(lng_mac_choose_text(lt_file, NS2QString(name)))];
|
||||
|
||||
NSArray *appsPaths = [[NSFileManager defaultManager] URLsForDirectory:NSApplicationDirectory inDomains:NSLocalDomainMask];
|
||||
if ([appsPaths count]) [openPanel setDirectoryURL:[appsPaths firstObject]];
|
||||
[openPanel beginWithCompletionHandler:^(NSInteger result){
|
||||
if (result == NSFileHandlingPanelOKButton) {
|
||||
if ([[openPanel URLs] count] > 0) {
|
||||
NSURL *app = [[openPanel URLs] objectAtIndex:0];
|
||||
NSString *path = [app path];
|
||||
if ([button state] == NSOnState) {
|
||||
NSArray *UTIs = (NSArray *)UTTypeCreateAllIdentifiersForTag(kUTTagClassFilenameExtension,
|
||||
(CFStringRef)ext,
|
||||
nil);
|
||||
for (NSString *UTI in UTIs) {
|
||||
OSStatus result = LSSetDefaultRoleHandlerForContentType((CFStringRef)UTI,
|
||||
kLSRolesAll,
|
||||
(CFStringRef)[[NSBundle bundleWithPath:path] bundleIdentifier]);
|
||||
DEBUG_LOG(("App Info: set default handler for '%1' UTI result: %2").arg(NS2QString(UTI)).arg(result));
|
||||
}
|
||||
|
||||
[UTIs release];
|
||||
}
|
||||
[[NSWorkspace sharedWorkspace] openFile:file withApplication:[app path]];
|
||||
}
|
||||
}
|
||||
[selector release];
|
||||
[button release];
|
||||
[enableLabel release];
|
||||
[goodLabel release];
|
||||
[badLabel release];
|
||||
[badIcon release];
|
||||
[accessory release];
|
||||
[delegate release];
|
||||
}];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[[NSWorkspace sharedWorkspace] openFile:file];
|
||||
}
|
||||
@finally {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void objc_start() {
|
||||
_sharedDelegate = [[ApplicationDelegate alloc] init];
|
||||
[[NSApplication sharedApplication] setDelegate:_sharedDelegate];
|
||||
|
|
|
@ -525,211 +525,6 @@ int psFixPrevious() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void psPostprocessFile(const QString &name) {
|
||||
std::wstring zoneFile = QDir::toNativeSeparators(name).toStdWString() + L":Zone.Identifier";
|
||||
HANDLE f = CreateFile(zoneFile.c_str(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
if (f == INVALID_HANDLE_VALUE) { // :(
|
||||
return;
|
||||
}
|
||||
|
||||
const char data[] = "[ZoneTransfer]\r\nZoneId=3\r\n";
|
||||
|
||||
DWORD written = 0;
|
||||
BOOL result = WriteFile(f, data, sizeof(data), &written, NULL);
|
||||
CloseHandle(f);
|
||||
|
||||
if (!result || written != sizeof(data)) { // :(
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &, int hbitmapFormat);
|
||||
|
||||
namespace {
|
||||
struct OpenWithApp {
|
||||
OpenWithApp(const QString &name, HBITMAP icon, IAssocHandler *handler) : name(name), icon(icon), handler(handler) {
|
||||
}
|
||||
OpenWithApp(const QString &name, IAssocHandler *handler) : name(name), icon(0), handler(handler) {
|
||||
}
|
||||
void destroy() {
|
||||
if (icon) DeleteBitmap(icon);
|
||||
if (handler) handler->Release();
|
||||
}
|
||||
QString name;
|
||||
HBITMAP icon;
|
||||
IAssocHandler *handler;
|
||||
};
|
||||
|
||||
bool OpenWithAppLess(const OpenWithApp &a, const OpenWithApp &b) {
|
||||
return a.name < b.name;
|
||||
}
|
||||
|
||||
HBITMAP _iconToBitmap(LPWSTR icon, int iconindex) {
|
||||
if (!icon) return 0;
|
||||
WCHAR tmpIcon[4096];
|
||||
if (icon[0] == L'@' && SUCCEEDED(SHLoadIndirectString(icon, tmpIcon, 4096, 0))) {
|
||||
icon = tmpIcon;
|
||||
}
|
||||
int32 w = GetSystemMetrics(SM_CXSMICON), h = GetSystemMetrics(SM_CYSMICON);
|
||||
|
||||
HICON ico = ExtractIcon(0, icon, iconindex);
|
||||
if (!ico) {
|
||||
if (!iconindex) { // try to read image
|
||||
QImage img(QString::fromWCharArray(icon));
|
||||
if (!img.isNull()) {
|
||||
return qt_pixmapToWinHBITMAP(App::pixmapFromImageInPlace(img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)), /* HBitmapAlpha */ 2);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
HDC screenDC = GetDC(0), hdc = CreateCompatibleDC(screenDC);
|
||||
HBITMAP result = CreateCompatibleBitmap(screenDC, w, h);
|
||||
HGDIOBJ was = SelectObject(hdc, result);
|
||||
DrawIconEx(hdc, 0, 0, ico, w, h, 0, NULL, DI_NORMAL);
|
||||
SelectObject(hdc, was);
|
||||
DeleteDC(hdc);
|
||||
ReleaseDC(0, screenDC);
|
||||
|
||||
DestroyIcon(ico);
|
||||
|
||||
return (HBITMAP)CopyImage(result, IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE | LR_CREATEDIBSECTION);
|
||||
// return result;
|
||||
}
|
||||
}
|
||||
|
||||
bool psShowOpenWithMenu(int x, int y, const QString &file) {
|
||||
if (!useOpenWith || !App::wnd()) return false;
|
||||
|
||||
bool result = false;
|
||||
QList<OpenWithApp> handlers;
|
||||
IShellItem* pItem = nullptr;
|
||||
if (SUCCEEDED(Dlls::SHCreateItemFromParsingName(QDir::toNativeSeparators(file).toStdWString().c_str(), nullptr, IID_PPV_ARGS(&pItem)))) {
|
||||
IEnumAssocHandlers *assocHandlers = 0;
|
||||
if (SUCCEEDED(pItem->BindToHandler(nullptr, BHID_EnumAssocHandlers, IID_PPV_ARGS(&assocHandlers)))) {
|
||||
HRESULT hr = S_FALSE;
|
||||
do
|
||||
{
|
||||
IAssocHandler *handler = 0;
|
||||
ULONG ulFetched = 0;
|
||||
hr = assocHandlers->Next(1, &handler, &ulFetched);
|
||||
if (FAILED(hr) || hr == S_FALSE || !ulFetched) break;
|
||||
|
||||
LPWSTR name = 0;
|
||||
if (SUCCEEDED(handler->GetUIName(&name))) {
|
||||
LPWSTR icon = 0;
|
||||
int iconindex = 0;
|
||||
if (SUCCEEDED(handler->GetIconLocation(&icon, &iconindex)) && icon) {
|
||||
handlers.push_back(OpenWithApp(QString::fromWCharArray(name), _iconToBitmap(icon, iconindex), handler));
|
||||
CoTaskMemFree(icon);
|
||||
} else {
|
||||
handlers.push_back(OpenWithApp(QString::fromWCharArray(name), handler));
|
||||
}
|
||||
CoTaskMemFree(name);
|
||||
} else {
|
||||
handler->Release();
|
||||
}
|
||||
} while (hr != S_FALSE);
|
||||
assocHandlers->Release();
|
||||
}
|
||||
|
||||
if (!handlers.isEmpty()) {
|
||||
HMENU menu = CreatePopupMenu();
|
||||
std::sort(handlers.begin(), handlers.end(), OpenWithAppLess);
|
||||
for (int32 i = 0, l = handlers.size(); i < l; ++i) {
|
||||
MENUITEMINFO menuInfo = { 0 };
|
||||
menuInfo.cbSize = sizeof(menuInfo);
|
||||
menuInfo.fMask = MIIM_STRING | MIIM_DATA | MIIM_ID;
|
||||
menuInfo.fType = MFT_STRING;
|
||||
menuInfo.wID = i + 1;
|
||||
if (handlers.at(i).icon) {
|
||||
menuInfo.fMask |= MIIM_BITMAP;
|
||||
menuInfo.hbmpItem = handlers.at(i).icon;
|
||||
}
|
||||
|
||||
QString name = handlers.at(i).name;
|
||||
if (name.size() > 512) name = name.mid(0, 512);
|
||||
WCHAR nameArr[1024];
|
||||
name.toWCharArray(nameArr);
|
||||
nameArr[name.size()] = 0;
|
||||
menuInfo.dwTypeData = nameArr;
|
||||
InsertMenuItem(menu, GetMenuItemCount(menu), TRUE, &menuInfo);
|
||||
}
|
||||
MENUITEMINFO sepInfo = { 0 };
|
||||
sepInfo.cbSize = sizeof(sepInfo);
|
||||
sepInfo.fMask = MIIM_STRING | MIIM_DATA;
|
||||
sepInfo.fType = MFT_SEPARATOR;
|
||||
InsertMenuItem(menu, GetMenuItemCount(menu), true, &sepInfo);
|
||||
|
||||
MENUITEMINFO menuInfo = { 0 };
|
||||
menuInfo.cbSize = sizeof(menuInfo);
|
||||
menuInfo.fMask = MIIM_STRING | MIIM_DATA | MIIM_ID;
|
||||
menuInfo.fType = MFT_STRING;
|
||||
menuInfo.wID = handlers.size() + 1;
|
||||
|
||||
QString name = lang(lng_wnd_choose_program_menu);
|
||||
if (name.size() > 512) name = name.mid(0, 512);
|
||||
WCHAR nameArr[1024];
|
||||
name.toWCharArray(nameArr);
|
||||
nameArr[name.size()] = 0;
|
||||
menuInfo.dwTypeData = nameArr;
|
||||
InsertMenuItem(menu, GetMenuItemCount(menu), TRUE, &menuInfo);
|
||||
|
||||
int sel = TrackPopupMenu(menu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON | TPM_RETURNCMD, x, y, 0, App::wnd()->psHwnd(), 0);
|
||||
DestroyMenu(menu);
|
||||
|
||||
if (sel > 0) {
|
||||
if (sel <= handlers.size()) {
|
||||
IDataObject *dataObj = 0;
|
||||
if (SUCCEEDED(pItem->BindToHandler(nullptr, BHID_DataObject, IID_PPV_ARGS(&dataObj))) && dataObj) {
|
||||
handlers.at(sel - 1).handler->Invoke(dataObj);
|
||||
dataObj->Release();
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
for (int i = 0, l = handlers.size(); i < l; ++i) {
|
||||
handlers[i].destroy();
|
||||
}
|
||||
}
|
||||
|
||||
pItem->Release();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void psOpenFile(const QString &name, bool openWith) {
|
||||
base::TaskQueue::Main().Put([name, openWith] {
|
||||
bool mailtoScheme = name.startsWith(qstr("mailto:"));
|
||||
std::wstring wname = mailtoScheme ? name.toStdWString() : QDir::toNativeSeparators(name).toStdWString();
|
||||
|
||||
if (openWith && useOpenAs) {
|
||||
if (Dlls::SHOpenWithDialog) {
|
||||
OPENASINFO info;
|
||||
info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_REGISTER_EXT | OAIF_EXEC;
|
||||
if (mailtoScheme) info.oaifInFlags |= OAIF_FILE_IS_URI | OAIF_URL_PROTOCOL;
|
||||
info.pcszClass = NULL;
|
||||
info.pcszFile = wname.c_str();
|
||||
Dlls::SHOpenWithDialog(0, &info);
|
||||
} else {
|
||||
Dlls::OpenAs_RunDLL(0, 0, wname.c_str(), SW_SHOWNORMAL);
|
||||
}
|
||||
} else {
|
||||
ShellExecute(0, L"open", wname.c_str(), 0, 0, SW_SHOWNORMAL);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void psShowInFolder(const QString &name) {
|
||||
base::TaskQueue::Main().Put([name] {
|
||||
auto nameEscaped = QDir::toNativeSeparators(name).replace('"', qsl("\"\""));
|
||||
ShellExecute(0, 0, qsl("explorer").toStdWString().c_str(), (qsl("/select,") + nameEscaped).toStdWString().c_str(), 0, SW_SHOWNORMAL);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
namespace Platform {
|
||||
|
||||
void start() {
|
||||
|
|
|
@ -63,12 +63,6 @@ int psFixPrevious();
|
|||
void psExecUpdater();
|
||||
void psExecTelegram(const QString &arg = QString());
|
||||
|
||||
bool psShowOpenWithMenu(int x, int y, const QString &file);
|
||||
|
||||
void psPostprocessFile(const QString &name);
|
||||
void psOpenFile(const QString &name, bool openWith = false);
|
||||
void psShowInFolder(const QString &name);
|
||||
|
||||
QAbstractNativeEventFilter *psNativeEventFilter();
|
||||
|
||||
void psNewVersion();
|
||||
|
|
|
@ -22,7 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
|
||||
#include "settings/settings_block_widget.h"
|
||||
#include "ui/effects/radial_animation.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
|
||||
namespace Settings {
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#pragma once
|
||||
|
||||
#include "core/observer.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
|
||||
#include "settings/settings_block_widget.h"
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "boxes/languagebox.h"
|
||||
#include "boxes/confirmbox.h"
|
||||
#include "boxes/aboutbox.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "langloaderplain.h"
|
||||
#include "autoupdater.h"
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#pragma once
|
||||
|
||||
#include "settings/settings_block_widget.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
|
||||
namespace Settings {
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "localstorage.h"
|
||||
#include "boxes/confirmbox.h"
|
||||
#include "application.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
|
||||
namespace Settings {
|
||||
|
|
|
@ -30,6 +30,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "mainwindow.h"
|
||||
#include "window/notifications_manager.h"
|
||||
#include "boxes/notifications_box.h"
|
||||
#include "platform/platform_notifications_manager.h"
|
||||
|
||||
namespace Settings {
|
||||
|
||||
|
@ -72,16 +73,14 @@ void NotificationsWidget::createNotificationsControls() {
|
|||
style::margins margin(0, 0, 0, st::settingsSkip);
|
||||
style::margins slidedPadding(0, margin.bottom() / 2, 0, margin.bottom() - (margin.bottom() / 2));
|
||||
|
||||
QString nativeNotificationsLabel;
|
||||
auto nativeNotificationsLabel = QString();
|
||||
if (Platform::Notifications::Supported()) {
|
||||
#ifdef Q_OS_WIN
|
||||
if (App::wnd()->psHasNativeNotifications()) {
|
||||
nativeNotificationsLabel = lang(lng_settings_use_windows);
|
||||
}
|
||||
#elif defined Q_OS_LINUX64 || defined Q_OS_LINUX32
|
||||
if (App::wnd()->psHasNativeNotifications()) {
|
||||
#elif defined Q_OS_LINUX64 || defined Q_OS_LINUX32 // Q_OS_WIN
|
||||
nativeNotificationsLabel = lang(lng_settings_use_native_notifications);
|
||||
#endif // Q_OS_WIN || Q_OS_LINUX64 || Q_OS_LINUX32
|
||||
}
|
||||
#endif // Q_OS_WIN
|
||||
if (!nativeNotificationsLabel.isEmpty()) {
|
||||
addChildRow(_nativeNotifications, margin, nativeNotificationsLabel, SLOT(onNativeNotifications()), Global::NativeNotifications());
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "boxes/confirmbox.h"
|
||||
#include "lang.h"
|
||||
#include "messenger.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
#include "window/themes/window_theme_editor.h"
|
||||
|
||||
|
@ -93,7 +93,7 @@ void fillCodes() {
|
|||
}
|
||||
});
|
||||
Codes.insert(qsl("loadcolors"), []() {
|
||||
FileDialog::askOpenPath("Open palette file", "Palette (*.tdesktop-palette)", [](const FileDialog::OpenResult &result) {
|
||||
FileDialog::GetOpenPath("Open palette file", "Palette (*.tdesktop-palette)", [](const FileDialog::OpenResult &result) {
|
||||
if (!result.paths.isEmpty()) {
|
||||
Window::Theme::Apply(result.paths.front());
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "application.h"
|
||||
#include "fileuploader.h"
|
||||
#include "mainwindow.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "apiwrap.h"
|
||||
#include "boxes/confirmbox.h"
|
||||
#include "media/media_audio.h"
|
||||
|
@ -1191,16 +1191,14 @@ void DocumentOpenClickHandler::doOpen(DocumentData *data, HistoryItem *context,
|
|||
} else {
|
||||
auto filepath = location.name();
|
||||
if (documentIsValidMediaFile(filepath)) {
|
||||
psOpenFile(filepath);
|
||||
} else {
|
||||
psShowInFolder(filepath);
|
||||
File::Launch(filepath);
|
||||
}
|
||||
}
|
||||
if (App::main()) App::main()->mediaMarkRead(data);
|
||||
} else if (data->voice() || data->song() || data->isVideo()) {
|
||||
auto filepath = location.name();
|
||||
if (documentIsValidMediaFile(filepath)) {
|
||||
psOpenFile(filepath);
|
||||
File::Launch(filepath);
|
||||
}
|
||||
if (App::main()) App::main()->mediaMarkRead(data);
|
||||
} else if (data->size < App::kImageSizeLimit) {
|
||||
|
@ -1218,14 +1216,14 @@ void DocumentOpenClickHandler::doOpen(DocumentData *data, HistoryItem *context,
|
|||
App::wnd()->showDocument(data, context);
|
||||
}
|
||||
} else {
|
||||
psOpenFile(location.name());
|
||||
File::Launch(location.name());
|
||||
}
|
||||
location.accessDisable();
|
||||
} else {
|
||||
psOpenFile(location.name());
|
||||
File::Launch(location.name());
|
||||
}
|
||||
} else {
|
||||
psOpenFile(location.name());
|
||||
File::Launch(location.name());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -1256,10 +1254,7 @@ void DocumentSaveClickHandler::doSave(DocumentData *data, bool forceSavingAs) {
|
|||
|
||||
auto filepath = data->filepath(DocumentData::FilePathResolveSaveFromDataSilent, forceSavingAs);
|
||||
if (!filepath.isEmpty() && !forceSavingAs) {
|
||||
auto pos = QCursor::pos();
|
||||
if (!psShowOpenWithMenu(pos.x(), pos.y(), filepath)) {
|
||||
psOpenFile(filepath, true);
|
||||
}
|
||||
File::OpenWith(filepath, QCursor::pos());
|
||||
} else {
|
||||
auto fileinfo = QFileInfo(filepath);
|
||||
auto filedir = filepath.isEmpty() ? QDir() : fileinfo.dir();
|
||||
|
@ -1499,14 +1494,11 @@ void DocumentData::performActionOnLoad() {
|
|||
if (already.isEmpty()) return;
|
||||
|
||||
if (_actionOnLoad == ActionOnLoadOpenWith) {
|
||||
QPoint pos(QCursor::pos());
|
||||
if (!psShowOpenWithMenu(pos.x(), pos.y(), already)) {
|
||||
psOpenFile(already, true);
|
||||
}
|
||||
File::OpenWith(already, QCursor::pos());
|
||||
} else if (_actionOnLoad == ActionOnLoadOpen || _actionOnLoad == ActionOnLoadPlayInline) {
|
||||
if (voice() || song() || isVideo()) {
|
||||
if (documentIsValidMediaFile(already)) {
|
||||
psOpenFile(already);
|
||||
File::Launch(already);
|
||||
}
|
||||
if (App::main()) App::main()->mediaMarkRead(this);
|
||||
} else if (loc.accessEnable()) {
|
||||
|
@ -1517,11 +1509,11 @@ void DocumentData::performActionOnLoad() {
|
|||
App::wnd()->showDocument(this, item);
|
||||
}
|
||||
} else {
|
||||
psOpenFile(already);
|
||||
File::Launch(already);
|
||||
}
|
||||
loc.accessDisable();
|
||||
} else {
|
||||
psOpenFile(already);
|
||||
File::Launch(already);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "core/task_queue.h"
|
||||
#include "core/zlib_help.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "boxes/editcolorbox.h"
|
||||
#include "lang.h"
|
||||
|
||||
|
@ -635,7 +635,7 @@ void ThemeExportBox::updateThumbnail() {
|
|||
}
|
||||
|
||||
void ThemeExportBox::chooseBackgroundFromFile() {
|
||||
FileDialog::askOpenPath(lang(lng_theme_editor_choose_image), "Image files (*.jpeg *.jpg *.png)", base::lambda_guarded(this, [this](const FileDialog::OpenResult &result) {
|
||||
FileDialog::GetOpenPath(lang(lng_theme_editor_choose_image), "Image files (*.jpeg *.jpg *.png)", base::lambda_guarded(this, [this](const FileDialog::OpenResult &result) {
|
||||
auto content = result.remoteContent;
|
||||
if (!result.paths.isEmpty()) {
|
||||
QFile f(result.paths.front());
|
||||
|
@ -664,7 +664,7 @@ void ThemeExportBox::exportTheme() {
|
|||
auto caption = lang(lng_theme_editor_choose_name);
|
||||
auto filter = "Themes (*.tdesktop-theme)";
|
||||
auto name = "awesome.tdesktop-theme";
|
||||
FileDialog::askWritePath(caption, filter, name, base::lambda_guarded(this, [this](const QString &path) {
|
||||
FileDialog::GetWritePath(caption, filter, name, base::lambda_guarded(this, [this](const QString &path) {
|
||||
zlib::FileToWrite zip;
|
||||
|
||||
zip_fileinfo zfi = { { 0, 0, 0, 0, 0, 0 }, 0, 0, 0 };
|
||||
|
@ -798,7 +798,7 @@ void Editor::paintEvent(QPaintEvent *e) {
|
|||
void Editor::Start() {
|
||||
auto palettePath = Local::themePaletteAbsolutePath();
|
||||
if (palettePath.isEmpty()) {
|
||||
FileDialog::askWritePath(lang(lng_theme_editor_save_palette), "Palette (*.tdesktop-palette)", "colors.tdesktop-palette", [](const QString &path) {
|
||||
FileDialog::GetWritePath(lang(lng_theme_editor_save_palette), "Palette (*.tdesktop-palette)", "colors.tdesktop-palette", [](const QString &path) {
|
||||
if (!Local::copyThemeColorsToPalette(path)) {
|
||||
writeDefaultPalette(path);
|
||||
}
|
||||
|
|
|
@ -213,6 +213,8 @@
|
|||
'<(src_loc)/core/click_handler.h',
|
||||
'<(src_loc)/core/click_handler_types.cpp',
|
||||
'<(src_loc)/core/click_handler_types.h',
|
||||
'<(src_loc)/core/file_utilities.cpp',
|
||||
'<(src_loc)/core/file_utilities.h',
|
||||
'<(src_loc)/core/lambda.h',
|
||||
'<(src_loc)/core/observer.cpp',
|
||||
'<(src_loc)/core/observer.h',
|
||||
|
@ -563,8 +565,6 @@
|
|||
'<(src_loc)/ui/countryinput.h',
|
||||
'<(src_loc)/ui/emoji_config.cpp',
|
||||
'<(src_loc)/ui/emoji_config.h',
|
||||
'<(src_loc)/ui/filedialog.cpp',
|
||||
'<(src_loc)/ui/filedialog.h',
|
||||
'<(src_loc)/ui/images.cpp',
|
||||
'<(src_loc)/ui/images.h',
|
||||
'<(src_loc)/ui/twidget.cpp',
|
||||
|
|
Loading…
Add table
Reference in a new issue