Combine startUrls and sendPaths
This commit allows to handle multiple URLs of all types as positional arguments simultaneously: * tg:// links * tonsite:// links * interpret:// file paths * generic file paths (to share files) This allows to Drag'n'Drop files to the Telegram shortcut/binary.
This commit is contained in:
@@ -693,33 +693,26 @@ bool Application::eventFilter(QObject *object, QEvent *e) {
|
||||
|
||||
case QEvent::FileOpen: {
|
||||
if (object == QCoreApplication::instance()) {
|
||||
const auto event = static_cast<QFileOpenEvent*>(e);
|
||||
const auto flushQueued = [=] {
|
||||
if (_filesToOpen.isEmpty()) {
|
||||
InvokeQueued(this, [=] {
|
||||
cSetSendPaths(_filesToOpen);
|
||||
_filesToOpen.clear();
|
||||
checkSendPaths();
|
||||
});
|
||||
}
|
||||
};
|
||||
if (const auto file = event->file(); !file.isEmpty()) {
|
||||
flushQueued();
|
||||
_filesToOpen.append(file);
|
||||
} else if (event->url().scheme() == u"tg"_q
|
||||
|| event->url().scheme() == u"tonsite"_q) {
|
||||
const auto url = QString::fromUtf8(
|
||||
event->url().toEncoded().trimmed());
|
||||
cSetStartUrl(url.mid(0, 8192));
|
||||
checkStartUrl();
|
||||
if (_lastActivePrimaryWindow
|
||||
&& StartUrlRequiresActivate(url)) {
|
||||
_lastActivePrimaryWindow->activate();
|
||||
}
|
||||
} else if (event->url().scheme() == u"interpret"_q) {
|
||||
flushQueued();
|
||||
_filesToOpen.append(event->url().toString());
|
||||
if (_urlsToOpen.isEmpty()) {
|
||||
InvokeQueued(this, [=] {
|
||||
const auto activateRequired = ranges::any_of(
|
||||
ranges::views::all(
|
||||
_urlsToOpen
|
||||
) | ranges::views::transform([](const QUrl &url) {
|
||||
return url.toString();
|
||||
}),
|
||||
StartUrlRequiresActivate);
|
||||
cRefStartUrls() << base::take(_urlsToOpen);
|
||||
checkStartUrls();
|
||||
if (_lastActivePrimaryWindow && activateRequired) {
|
||||
_lastActivePrimaryWindow->activate();
|
||||
}
|
||||
});
|
||||
}
|
||||
const auto event = static_cast<QFileOpenEvent*>(e);
|
||||
_urlsToOpen << event->url().toString(QUrl::FullyEncoded).mid(
|
||||
0,
|
||||
8192);
|
||||
}
|
||||
} break;
|
||||
|
||||
@@ -1091,31 +1084,27 @@ bool Application::canApplyLangPackWithoutRestart() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Application::checkSendPaths() {
|
||||
if (!cSendPaths().isEmpty()
|
||||
void Application::checkStartUrls() {
|
||||
if (!Core::App().passcodeLocked()) {
|
||||
cRefStartUrls() = ranges::views::all(
|
||||
cRefStartUrls()
|
||||
) | ranges::views::filter([&](const QUrl &url) {
|
||||
if (url.scheme() == u"tonsite"_q) {
|
||||
iv().showTonSite(url.toString(), {});
|
||||
return false;
|
||||
} else if (_lastActivePrimaryWindow) {
|
||||
return !openLocalUrl(url.toString(), {});
|
||||
}
|
||||
return true;
|
||||
}) | ranges::to<QList<QUrl>>;
|
||||
}
|
||||
if (!cRefStartUrls().isEmpty()
|
||||
&& _lastActivePrimaryWindow
|
||||
&& !_lastActivePrimaryWindow->locked()) {
|
||||
_lastActivePrimaryWindow->widget()->sendPaths();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::checkStartUrl() {
|
||||
if (!cStartUrl().isEmpty()) {
|
||||
const auto url = cStartUrl();
|
||||
if (!Core::App().passcodeLocked()) {
|
||||
if (url.startsWith("tonsite://", Qt::CaseInsensitive)) {
|
||||
cSetStartUrl(QString());
|
||||
iv().showTonSite(url, {});
|
||||
} else if (_lastActivePrimaryWindow) {
|
||||
cSetStartUrl(QString());
|
||||
if (!openLocalUrl(url, {})) {
|
||||
cSetStartUrl(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Application::openLocalUrl(const QString &url, QVariant context) {
|
||||
return openCustomUrl("tg://", LocalUrlHandlers(), url, context);
|
||||
}
|
||||
|
||||
@@ -270,8 +270,7 @@ public:
|
||||
}
|
||||
|
||||
// Internal links.
|
||||
void checkStartUrl();
|
||||
void checkSendPaths();
|
||||
void checkStartUrls();
|
||||
bool openLocalUrl(const QString &url, QVariant context);
|
||||
bool openInternalUrl(const QString &url, QVariant context);
|
||||
[[nodiscard]] QString changelogLink() const;
|
||||
@@ -450,7 +449,7 @@ private:
|
||||
crl::time _shouldLockAt = 0;
|
||||
base::Timer _autoLockTimer;
|
||||
|
||||
QStringList _filesToOpen;
|
||||
QList<QUrl> _urlsToOpen;
|
||||
|
||||
std::optional<base::Timer> _saveSettingsTimer;
|
||||
|
||||
|
||||
@@ -536,9 +536,8 @@ void Launcher::processArguments() {
|
||||
{ "-tosettings" , KeyFormat::NoValues },
|
||||
{ "-startintray" , KeyFormat::NoValues },
|
||||
{ "-quit" , KeyFormat::NoValues },
|
||||
{ "-sendpath" , KeyFormat::AllLeftValues },
|
||||
{ "-workdir" , KeyFormat::OneValue },
|
||||
{ "--" , KeyFormat::OneValue },
|
||||
{ "--" , KeyFormat::AllLeftValues },
|
||||
{ "-scale" , KeyFormat::OneValue },
|
||||
};
|
||||
auto parseResult = QMap<QByteArray, QStringList>();
|
||||
@@ -585,19 +584,15 @@ void Launcher::processArguments() {
|
||||
gStartToSettings = parseResult.contains("-tosettings");
|
||||
gStartInTray = parseResult.contains("-startintray");
|
||||
gQuit = parseResult.contains("-quit");
|
||||
gSendPaths = parseResult.value("-sendpath", {});
|
||||
_customWorkingDir = parseResult.value("-workdir", {}).join(QString());
|
||||
if (!_customWorkingDir.isEmpty()) {
|
||||
_customWorkingDir = QDir(_customWorkingDir).absolutePath() + '/';
|
||||
}
|
||||
|
||||
auto startUrls = parseResult.value("--", {});
|
||||
startUrls = ranges::views::filter(startUrls, [](const QString &url) {
|
||||
return QUrl::fromUserInput(url).isValid();
|
||||
}) | ranges::to<QStringList>;
|
||||
if (!startUrls.isEmpty()) {
|
||||
gStartUrl = startUrls[0];
|
||||
}
|
||||
const auto startUrls = parseResult.value("--", {});
|
||||
gStartUrls = startUrls | ranges::views::transform([&](const QString &url) {
|
||||
return QUrl::fromUserInput(url, _initialWorkingDir);
|
||||
}) | ranges::views::filter(&QUrl::isValid) | ranges::to<QList<QUrl>>;
|
||||
|
||||
const auto scaleKey = parseResult.value("-scale", {});
|
||||
if (scaleKey.size() > 0) {
|
||||
|
||||
@@ -35,47 +35,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include <QtGui/qpa/qplatformscreen.h>
|
||||
|
||||
namespace Core {
|
||||
namespace {
|
||||
|
||||
QChar _toHex(ushort v) {
|
||||
v = v & 0x000F;
|
||||
return QChar::fromLatin1((v >= 10) ? ('a' + (v - 10)) : ('0' + v));
|
||||
}
|
||||
ushort _fromHex(QChar c) {
|
||||
return ((c.unicode() >= uchar('a')) ? (c.unicode() - uchar('a') + 10) : (c.unicode() - uchar('0'))) & 0x000F;
|
||||
}
|
||||
|
||||
QString _escapeTo7bit(const QString &str) {
|
||||
QString result;
|
||||
result.reserve(str.size() * 2);
|
||||
for (int i = 0, l = str.size(); i != l; ++i) {
|
||||
QChar ch(str.at(i));
|
||||
ushort uch(ch.unicode());
|
||||
if (uch < 32 || uch > 127 || uch == ushort(uchar('%'))) {
|
||||
result.append('%').append(_toHex(uch >> 12)).append(_toHex(uch >> 8)).append(_toHex(uch >> 4)).append(_toHex(uch));
|
||||
} else {
|
||||
result.append(ch);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString _escapeFrom7bit(const QString &str) {
|
||||
QString result;
|
||||
result.reserve(str.size());
|
||||
for (int i = 0, l = str.size(); i != l; ++i) {
|
||||
QChar ch(str.at(i));
|
||||
if (ch == QChar::fromLatin1('%') && i + 4 < l) {
|
||||
result.append(QChar(ushort((_fromHex(str.at(i + 1)) << 12) | (_fromHex(str.at(i + 2)) << 8) | (_fromHex(str.at(i + 3)) << 4) | _fromHex(str.at(i + 4)))));
|
||||
i += 4;
|
||||
} else {
|
||||
result.append(ch);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool Sandbox::QuitOnStartRequested = false;
|
||||
|
||||
@@ -277,18 +236,15 @@ void Sandbox::socketConnected() {
|
||||
_secondInstance = true;
|
||||
|
||||
QString commands;
|
||||
const QStringList &lst(cSendPaths());
|
||||
for (QStringList::const_iterator i = lst.cbegin(), e = lst.cend(); i != e; ++i) {
|
||||
commands += u"SEND:"_q + _escapeTo7bit(*i) + ';';
|
||||
}
|
||||
if (qEnvironmentVariableIsSet("XDG_ACTIVATION_TOKEN")) {
|
||||
commands += u"XDG_ACTIVATION_TOKEN:"_q + _escapeTo7bit(qEnvironmentVariable("XDG_ACTIVATION_TOKEN")) + ';';
|
||||
commands += u"XDG_ACTIVATION_TOKEN:"_q + qgetenv("XDG_ACTIVATION_TOKEN").toBase64() + ';';
|
||||
}
|
||||
if (!cStartUrl().isEmpty()) {
|
||||
commands += u"OPEN:"_q + _escapeTo7bit(cStartUrl()) + ';';
|
||||
} else if (cQuit()) {
|
||||
for (const auto &url : cRefStartUrls()) {
|
||||
commands += u"OPEN:"_q + url.toString(QUrl::FullyEncoded) + ';';
|
||||
}
|
||||
if (cQuit()) {
|
||||
commands += u"CMD:quit;"_q;
|
||||
} else {
|
||||
} else if (cRefStartUrls().isEmpty()) {
|
||||
commands += u"CMD:show;"_q;
|
||||
}
|
||||
|
||||
@@ -434,11 +390,11 @@ void Sandbox::newInstanceConnected() {
|
||||
|
||||
void Sandbox::readClients() {
|
||||
// This method can be called before Application is constructed.
|
||||
QString startUrl;
|
||||
QStringList toSend;
|
||||
QList<QUrl> startUrls;
|
||||
for (LocalClients::iterator i = _localClients.begin(), e = _localClients.end(); i != e; ++i) {
|
||||
i->second.append(i->first->readAll());
|
||||
if (i->second.size()) {
|
||||
bool activationRequired = false;
|
||||
QString cmds(QString::fromLatin1(i->second));
|
||||
int32 from = 0, l = cmds.length();
|
||||
for (int32 to = cmds.indexOf(QChar(';'), from); to >= from; to = (from < l) ? cmds.indexOf(QChar(';'), from) : -1) {
|
||||
@@ -448,21 +404,13 @@ void Sandbox::readClients() {
|
||||
const auto windowId = execExternal(cmds.mid(from + 4, to - from - 4));
|
||||
const auto response = u"RES:%1_%2;"_q.arg(processId).arg(windowId).toLatin1();
|
||||
i->first->write(response.data(), response.size());
|
||||
} else if (cmd.startsWith(u"SEND:"_q)) {
|
||||
if (cSendPaths().isEmpty()) {
|
||||
toSend.append(_escapeFrom7bit(cmds.mid(from + 5, to - from - 5)));
|
||||
}
|
||||
} else if (cmd.startsWith(u"XDG_ACTIVATION_TOKEN:"_q)) {
|
||||
qputenv("XDG_ACTIVATION_TOKEN", _escapeFrom7bit(cmds.mid(from + 21, to - from - 21)).toUtf8());
|
||||
qputenv("XDG_ACTIVATION_TOKEN", QByteArray::fromBase64(cmds.mid(from + 21, to - from - 21).toLatin1()));
|
||||
} else if (cmd.startsWith(u"OPEN:"_q)) {
|
||||
startUrl = _escapeFrom7bit(cmds.mid(from + 5, to - from - 5)).mid(0, 8192);
|
||||
const auto activationRequired = StartUrlRequiresActivate(startUrl);
|
||||
const auto processId = QApplication::applicationPid();
|
||||
const auto windowId = activationRequired
|
||||
? execExternal("show")
|
||||
: 0;
|
||||
const auto response = u"RES:%1_%2;"_q.arg(processId).arg(windowId).toLatin1();
|
||||
i->first->write(response.data(), response.size());
|
||||
startUrls.append(cmds.mid(from + 5, to - from - 5).mid(0, 8192));
|
||||
if (!activationRequired) {
|
||||
activationRequired = StartUrlRequiresActivate(startUrls.back().toString());
|
||||
}
|
||||
} else {
|
||||
LOG(("Sandbox Error: unknown command %1 passed in local socket").arg(cmd.toString()));
|
||||
}
|
||||
@@ -471,21 +419,17 @@ void Sandbox::readClients() {
|
||||
if (from > 0) {
|
||||
i->second = i->second.mid(from);
|
||||
}
|
||||
const auto processId = QApplication::applicationPid();
|
||||
const auto windowId = activationRequired
|
||||
? execExternal("show")
|
||||
: 0;
|
||||
const auto response = u"RES:%1_%2;"_q.arg(processId).arg(windowId).toLatin1();
|
||||
i->first->write(response.data(), response.size());
|
||||
}
|
||||
}
|
||||
if (!toSend.isEmpty()) {
|
||||
QStringList paths(cSendPaths());
|
||||
paths.append(toSend);
|
||||
cSetSendPaths(paths);
|
||||
}
|
||||
cRefStartUrls() << base::take(startUrls);
|
||||
if (_application) {
|
||||
_application->checkSendPaths();
|
||||
}
|
||||
if (!startUrl.isEmpty()) {
|
||||
cSetStartUrl(startUrl);
|
||||
}
|
||||
if (_application) {
|
||||
_application->checkStartUrl();
|
||||
_application->checkStartUrls();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2898,17 +2898,30 @@ bool MainWidget::contentOverlapped(const QRect &globalRect) {
|
||||
void MainWidget::activate() {
|
||||
if (_showAnimation) {
|
||||
return;
|
||||
} else if (const auto paths = cSendPaths(); !paths.isEmpty()) {
|
||||
const auto interpret = u"interpret://"_q;
|
||||
cSetSendPaths(QStringList());
|
||||
if (paths[0].startsWith(interpret)) {
|
||||
const auto error = Support::InterpretSendPath(
|
||||
_controller,
|
||||
paths[0].mid(interpret.size()));
|
||||
if (!error.isEmpty()) {
|
||||
_controller->show(Ui::MakeInformBox(error));
|
||||
}
|
||||
const auto urls = base::take(cRefStartUrls());
|
||||
const auto interprets = urls | ranges::views::filter([](const QUrl &url) {
|
||||
return url.scheme() == u"interpret"_q;
|
||||
}) | ranges::views::transform([](const QUrl &url) {
|
||||
return url.path();
|
||||
}) | ranges::to<QStringList>;
|
||||
const auto paths = urls | ranges::views::filter(
|
||||
&QUrl::isLocalFile
|
||||
) | ranges::views::transform(
|
||||
&QUrl::toLocalFile
|
||||
) | ranges::to<QStringList>;
|
||||
if (!interprets.isEmpty() || !paths.isEmpty()) {
|
||||
if (!interprets.isEmpty()) {
|
||||
for (const auto &interpret : interprets) {
|
||||
const auto error = Support::InterpretSendPath(
|
||||
_controller,
|
||||
interpret);
|
||||
if (!error.isEmpty()) {
|
||||
_controller->show(Ui::MakeInformBox(error));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
if (!paths.isEmpty()) {
|
||||
const auto chosen = [=](not_null<Data::Thread*> thread) {
|
||||
return sendPaths(thread, paths);
|
||||
};
|
||||
|
||||
@@ -215,7 +215,7 @@ void MainWindow::clearPasscodeLock() {
|
||||
_main->show();
|
||||
updateControlsGeometry();
|
||||
_main->showAnimated(std::move(oldContentCache), true);
|
||||
Core::App().checkStartUrl();
|
||||
Core::App().checkStartUrls();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,7 +285,7 @@ void MainWindow::setupMain(
|
||||
} else {
|
||||
_main->activate();
|
||||
}
|
||||
Core::App().checkStartUrl();
|
||||
Core::App().checkStartUrls();
|
||||
}
|
||||
fixOrder();
|
||||
if (const auto strong = weakAnimatedLayer.get()) {
|
||||
|
||||
@@ -707,7 +707,7 @@ void psSendToMenu(bool send, bool silent) {
|
||||
send,
|
||||
silent,
|
||||
FOLDERID_SendTo,
|
||||
L"-sendpath",
|
||||
L"--",
|
||||
L"Telegram send to link.\n"
|
||||
"You can disable send to menu item in Telegram settings.");
|
||||
}
|
||||
|
||||
@@ -20,8 +20,7 @@ bool gManyInstance = false;
|
||||
QString gKeyFile;
|
||||
QString gWorkingDir;
|
||||
|
||||
QStringList gSendPaths;
|
||||
QString gStartUrl;
|
||||
QList<QUrl> gStartUrls;
|
||||
|
||||
QString gDialogLastPath, gDialogHelperPath; // optimize QFileDialog
|
||||
|
||||
|
||||
@@ -108,8 +108,7 @@ DeclareSetting(bool, PasswordRecovered);
|
||||
DeclareSetting(int32, PasscodeBadTries);
|
||||
DeclareSetting(crl::time, PasscodeLastTry);
|
||||
|
||||
DeclareSetting(QStringList, SendPaths);
|
||||
DeclareSetting(QString, StartUrl);
|
||||
DeclareRefSetting(QList<QUrl>, StartUrls);
|
||||
|
||||
DeclareSetting(int, OtherOnline);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user