Commit 646f0fe3 authored by Nicolas Follet's avatar Nicolas Follet Committed by Wescoeur

feat(App): supports commands and conferences with id

parent 628665c6
......@@ -421,6 +421,14 @@ Server url not configured.</translation>
<source>showFunctionCall</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>joinConferenceFunctionDescription</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>initiateConferenceFunctionDescription</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>CodecsViewer</name>
......
......@@ -421,6 +421,14 @@ Url du serveur non configurée.</translation>
<source>showFunctionCall</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>joinConferenceFunctionDescription</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>initiateConferenceFunctionDescription</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>CodecsViewer</name>
......
......@@ -7,6 +7,7 @@ Exec=linphone %u
Icon=linphone
Terminal=false
Categories=Network;Telephony;
MimeType=x-scheme-handler/sip-linphone;x-scheme-handler/sip;
# Translations
......
......@@ -310,7 +310,8 @@ if(WIN32)
string(REPLACE "\\" "\\\\" ESCAPED_DOS_STYLE_SOURCE_DIR "${DOS_STYLE_SOURCE_DIR}")
file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}" DOS_STYLE_BINARY_DIR)
string(REPLACE "\\" "\\\\" ESCAPED_DOS_STYLE_BINARY_DIR "${DOS_STYLE_BINARY_DIR}")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/uri-handler-install.nsi.in" "${CMAKE_CURRENT_BINARY_DIR}/uri-handler-install.nsi" @ONLY)
set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "!include \\\"${ESCAPED_DOS_STYLE_BINARY_DIR}\\\\uri-handler-install.nsi\\\"")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/uninstall.nsi" "${CMAKE_CURRENT_BINARY_DIR}/uninstall.nsi" COPYONLY)
set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "!include \\\"${ESCAPED_DOS_STYLE_BINARY_DIR}\\\\uninstall.nsi\\\"")
if(ENABLE_OPENH264)
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
......@@ -32,6 +32,17 @@
<string>@CMAKE_OSX_DEPLOYMENT_TARGET@</string>
<key>NSAppSleepDisabled</key>
<string>YES</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>com.belldonnecommunications.linphone</string>
<key>CFBundleURLSchemes</key>
<array>
<string>sip</string>
<string>sip-linphone</string>
</array>
</dict>
</array>
</dict>
</plist>
......@@ -5,3 +5,5 @@ Abort
notRunningInUninstall:
DeleteRegKey HKCR "sip"
DeleteRegKey HKCR "sip-linphone"
WriteRegStr HKCR "sip" "" "URL:sip Protocol"
WriteRegStr HKCR "sip" "URL Protocol" ""
WriteRegExpandStr HKCR "sip\DefaultIcon" "" "@LINPHONE_DESKTOP_DIR@/assets/linphone.ico,1"
WriteRegStr HKCR "sip\shell" "" "open"
WriteRegStr HKCR "sip\shell\open" "" "command"
WriteRegStr HKCR "sip\shell\open\command" "" "$INSTDIR\bin\linphone.exe $\"%1$\""
WriteRegStr HKCR "sip-linphone" "" "URL:sip-linphone Protocol"
WriteRegStr HKCR "sip-linphone" "URL Protocol" ""
WriteRegExpandStr HKCR "sip-linphone\DefaultIcon" "" "@LINPHONE_DESKTOP_DIR@/assets/linphone.ico,1"
WriteRegStr HKCR "sip-linphone\shell" "" "open"
WriteRegStr HKCR "sip-linphone\shell\open" "" "command"
WriteRegStr HKCR "sip-linphone\shell\open\command" "" "$INSTDIR\bin\linphone.exe $\"%1$\""
\ No newline at end of file
......@@ -215,6 +215,13 @@ void App::initContentApp () {
if (mEngine->rootObjects().isEmpty())
qFatal("Unable to open main window.");
// Execute command argument if needed.
{
const QString commandArgument = getCommandArgument();
if (!commandArgument.isEmpty())
mCli->executeCommand(commandArgument);
}
QObject::connect(
CoreManager::getInstance()->getHandlers().get(),
&CoreHandlers::coreStarted,
......@@ -225,13 +232,39 @@ void App::initContentApp () {
// -----------------------------------------------------------------------------
QString App::getCommandArgument () {
// TODO: Remove me when cmd option will be available.
return QString("");
// return mParser->value("cmd");
const QStringList &arguments = mParser->positionalArguments();
return arguments.empty() ? QString("") : arguments[0];
}
// -----------------------------------------------------------------------------
void App::executeCommand (const QString &command) {
Q_CHECK_PTR(mCli);
mCli->executeCommand(command);
}
// -----------------------------------------------------------------------------
#ifdef Q_OS_MACOS
bool App::event (QEvent *event) {
if (event->type() == QEvent::FileOpen) {
const QString url = static_cast<QFileOpenEvent *>(event)->url().toString();
if (isSecondary()) {
sendMessage(url.toLocal8Bit(), -1);
::exit(EXIT_SUCCESS);
}
executeCommand(url);
}
return SingleApplication::event(event);
}
#endif // ifdef Q_OS_MACOS
// -----------------------------------------------------------------------------
QQuickWindow *App::getCallsWindow () {
if (!mCallsWindow)
mCallsWindow = ::createSubWindow(mEngine, QML_VIEW_CALLS_WINDOW);
......@@ -281,30 +314,19 @@ bool App::hasFocus () const {
// -----------------------------------------------------------------------------
void App::createParser () {
// TODO: Remove me in the future.
static const char *disabledOptions[] = {
QT_TR_NOOP("commandLineOptionCmd"),
QT_TR_NOOP("commandLineOptionCmdArg")
};
(void)disabledOptions;
if (mParser)
delete mParser;
mParser = new QCommandLineParser();
mParser->setApplicationDescription(tr("applicationDescription"));
mParser->addOptions({
{ { "h", "help" }, tr("commandLineOptionHelp") },
{ { "v", "version" }, tr("commandLineOptionVersion") },
{ "config", tr("commandLineOptionConfig"), tr("commandLineOptionConfigArg") },
{ { "c", "cmd" }, tr("commandLineOptionCmd"), tr("commandLineOptionCmdArg") },
#ifndef Q_OS_MACOS
{ "iconified", tr("commandLineOptionIconified") },
#endif // ifndef Q_OS_MACOS
{ { "V", "verbose" }, tr("commandLineOptionVerbose") }
// TODO: Enable me in future version!
// ,
// { { "c", "cmd" }, tr("commandLineOptionCmd"), tr("commandLineOptionCmdArg") }
});
}
......
......@@ -54,6 +54,11 @@ public:
void initContentApp ();
QString getCommandArgument ();
void executeCommand (const QString &command);
#ifdef Q_OS_MACOS
bool event (QEvent *event) override;
#endif // ifdef Q_OS_MACOS
QQmlEngine *getEngine () {
return mEngine;
......@@ -117,11 +122,11 @@ private:
return qVersion();
}
QCommandLineParser *mParser = nullptr;
QVariantList mAvailableLocales;
QString mLocale;
QCommandLineParser *mParser = nullptr;
QQmlApplicationEngine *mEngine = nullptr;
DefaultTranslator *mTranslator = nullptr;
......
......@@ -84,8 +84,13 @@ AppController::AppController (int &argc, char *argv[]) {
mApp = new App(argc, argv);
if (mApp->isSecondary()) {
#ifdef Q_OS_MACOS
mApp->processEvents();
#endif // ifdef Q_OS_MACOS
QString command = mApp->getCommandArgument();
mApp->sendMessage(command.isEmpty() ? "show" : command.toLocal8Bit(), -1);
return;
}
......
......@@ -20,9 +20,8 @@
* Author: Nicolas Follet
*/
#include <stdexcept>
#include "../../components/core/CoreManager.hpp"
#include "../../utils/Utils.hpp"
#include "../App.hpp"
#include "Cli.hpp"
......@@ -33,13 +32,64 @@ using namespace std;
// API.
// =============================================================================
static void cliShow (const QHash<QString, QString> &) {
static void cliShow (QHash<QString, QString> &) {
App *app = App::getInstance();
app->smartShowWindow(app->getMainWindow());
}
static void cliCall (const QHash<QString, QString> &args) {
CoreManager::getInstance()->getCallsListModel()->launchAudioCall(args["sipAddress"]);
static void cliCall (QHash<QString, QString> &args) {
CoreManager::getInstance()->getCallsListModel()->launchAudioCall(args["sip-address"]);
}
static void cliJoinConference (QHash<QString, QString> &args) {
const QString sipAddress = args.take("sip-address");
args["method"] = QStringLiteral("join-conference");
CoreManager::getInstance()->getCallsListModel()->launchAudioCall(sipAddress, args);
}
static void cliInitiateConference (QHash<QString, QString> &args) {
shared_ptr<linphone::Core> core = CoreManager::getInstance()->getCore();
// Check identity.
{
shared_ptr<linphone::Address> address = core->interpretUrl(::Utils::appStringToCoreString(args["sip-address"]));
address->clean();
const string sipAddress = address->asString();
const string identity = core->getIdentity();
if (sipAddress != identity) {
qWarning() << QStringLiteral("Received different sip address from identity : `%1 != %2`.")
.arg(::Utils::coreStringToAppString(identity))
.arg(::Utils::coreStringToAppString(sipAddress));
return;
}
}
shared_ptr<linphone::Conference> conference = core->getConference();
const QString id = args["conference-id"];
if (conference) {
if (conference->getId() == ::Utils::appStringToCoreString(id)) {
qInfo() << QStringLiteral("Conference `%1` already exists.").arg(id);
// TODO: Set the view to the "waiting call view".
return;
}
qInfo() << QStringLiteral("Remove existing conference with id: `%1`.")
.arg(::Utils::coreStringToAppString(conference->getId()));
core->terminateConference();
}
qInfo() << QStringLiteral("Create conference with id: `%1`.").arg(id);
conference = core->createConferenceWithParams(core->createConferenceParams());
conference->setId(::Utils::appStringToCoreString(id));
if (core->enterConference() == -1) {
qWarning() << QStringLiteral("Unable to join created conference: `%1`.").arg(id);
return;
}
// TODO: Set the view to the "waiting call view".
}
// =============================================================================
......@@ -55,7 +105,18 @@ Cli::Command::Command (
mFunction(function),
mArgsScheme(argsScheme) {}
void Cli::Command::execute (const QHash<QString, QString> &args) {
void Cli::Command::execute (QHash<QString, QString> &args) const {
// Check arguments validity.
for (const auto &argName : args.keys()) {
if (!mArgsScheme.contains(argName)) {
qWarning() << QStringLiteral("Command with invalid argument: `%1 (%2)`.")
.arg(mFunctionName).arg(argName);
return;
}
}
// Check missing arguments.
for (const auto &argName : mArgsScheme.keys()) {
if (!args.contains(argName) && !mArgsScheme[argName].isOptional) {
qWarning() << QStringLiteral("Missing argument for command: `%1 (%2)`.")
......@@ -64,14 +125,34 @@ void Cli::Command::execute (const QHash<QString, QString> &args) {
}
}
// Execute!
CoreManager *coreManager = CoreManager::getInstance();
if (coreManager->started())
(*mFunction)(args);
else {
Function f = mFunction;
::Utils::connectOnce(coreManager->getHandlers().get(), &CoreHandlers::coreStarted, coreManager, [f, args] {
QHash<QString, QString> fuckConst = args;
(*f)(fuckConst);
});
}
}
void Cli::Command::executeUri (const shared_ptr<linphone::Address> &address) const {
QHash<QString, QString> args;
for (const auto &argName : mArgsScheme.keys())
args[argName] = ::Utils::coreStringToAppString(address->getHeader(::Utils::appStringToCoreString(argName)));
args["sip-address"] = ::Utils::coreStringToAppString(address->asString());
execute(args);
}
// =============================================================================
// FIXME: Do not accept args without value like: cmd toto.
// In the future `toto` could be a boolean argument.
QRegExp Cli::mRegExpArgs("(?:(?:(\\w+)\\s*)=\\s*(?:\"([^\"\\\\]*(?:\\\\.[^\"\\\\]*)*)\"|([^\\s]+)\\s*))");
QRegExp Cli::mRegExpArgs("(?:(?:([\\w-]+)\\s*)=\\s*(?:\"([^\"\\\\]*(?:\\\\.[^\"\\\\]*)*)\"|([^\\s]+)\\s*))");
QRegExp Cli::mRegExpFunctionName("^\\s*([a-z-]+)\\s*");
Cli::Cli (QObject *parent) : QObject(parent) {
......@@ -79,6 +160,12 @@ Cli::Cli (QObject *parent) : QObject(parent) {
addCommand("call", tr("showFunctionCall"), ::cliCall, {
{ "sip-address", {} }
});
addCommand("initiate-conference", tr("initiateConferenceFunctionDescription"), ::cliInitiateConference, {
{ "sip-address", {} }, { "conference-id", {} }
});
addCommand("join-conference", tr("joinConferenceFunctionDescription"), ::cliJoinConference, {
{ "sip-address", {} }, { "conference-id", {} }
});
}
// -----------------------------------------------------------------------------
......@@ -88,7 +175,7 @@ void Cli::addCommand (
const QString &description,
Function function,
const QHash<QString, Argument> &argsScheme
) noexcept {
) {
if (mCommands.contains(functionName))
qWarning() << QStringLiteral("Command already exists: `%1`.").arg(functionName);
else
......@@ -97,22 +184,44 @@ void Cli::addCommand (
// -----------------------------------------------------------------------------
void Cli::executeCommand (const QString &command) noexcept {
const QString functionName = parseFunctionName(command);
if (functionName.isEmpty())
void Cli::executeCommand (const QString &command) const {
shared_ptr<linphone::Address> address = linphone::Factory::get()->createAddress(
::Utils::appStringToCoreString(command)
);
// Execute cli command.
if (!address) {
const QString &functionName = parseFunctionName(command);
if (!functionName.isEmpty()) {
QHash<QString, QString> args = parseArgs(command);
mCommands[functionName].execute(args);
}
return;
}
bool soFarSoGood;
const QHash<QString, QString> &args = parseArgs(command, functionName, soFarSoGood);
if (!soFarSoGood)
// Execute uri command.
string scheme = address->getScheme();
if (address->getUsername().empty() || (scheme != "sip" && scheme != "sip-linphone")) {
qWarning() << QStringLiteral("Not a valid uri: `%1`.").arg(command);
return;
}
mCommands[functionName].execute(args);
const QString functionName = ::Utils::coreStringToAppString(address->getHeader("method")).isEmpty()
? QStringLiteral("call")
: ::Utils::coreStringToAppString(address->getHeader("method"));
if (!functionName.isEmpty() && !mCommands.contains(functionName)) {
qWarning() << QStringLiteral("This command doesn't exist: `%1`.").arg(functionName);
return;
}
mCommands[functionName].executeUri(address);
}
// -----------------------------------------------------------------------------
const QString Cli::parseFunctionName (const QString &command) noexcept {
QString Cli::parseFunctionName (const QString &command) const {
mRegExpFunctionName.indexIn(command);
if (mRegExpFunctionName.pos(1) == -1) {
qWarning() << QStringLiteral("Unable to parse function name of command: `%1`.").arg(command);
......@@ -130,26 +239,12 @@ const QString Cli::parseFunctionName (const QString &command) noexcept {
return functionName;
}
const QHash<QString, QString> Cli::parseArgs (
const QString &command,
const QString functionName,
bool &soFarSoGood
) noexcept {
QHash<QString, QString> Cli::parseArgs (const QString &command) const {
QHash<QString, QString> args;
int pos = 0;
soFarSoGood = true;
while ((pos = mRegExpArgs.indexIn(command, pos)) != -1) {
pos += mRegExpArgs.matchedLength();
if (!mCommands[functionName].argNameExists(mRegExpArgs.cap(1))) {
qWarning() << QStringLiteral("Command with invalid argument(s): `%1 (%2)`.")
.arg(functionName).arg(mRegExpArgs.cap(1));
soFarSoGood = false;
return args;
}
args[mRegExpArgs.cap(1)] = (mRegExpArgs.cap(2).isEmpty() ? mRegExpArgs.cap(3) : mRegExpArgs.cap(2));
}
......
......@@ -23,15 +23,21 @@
#ifndef CLI_H_
#define CLI_H_
#include <memory>
#include <QHash>
#include <QObject>
// =============================================================================
namespace linphone {
class Address;
}
class Cli : public QObject {
Q_OBJECT;
typedef void (*Function)(const QHash<QString, QString> &);
typedef void (*Function)(QHash<QString, QString> &);
enum ArgumentType {
STRING
......@@ -50,13 +56,15 @@ class Cli : public QObject {
class Command {
public:
Command () = default;
Command (const QString &functionName, const QString &description, Function function, const QHash<QString, Argument> &argsScheme);
Command (
const QString &functionName,
const QString &description,
Function function,
const QHash<QString, Argument> &argsScheme
);
void execute (const QHash<QString, QString> &args);
bool argNameExists (const QString &argName) {
return mArgsScheme.contains(argName);
}
void execute (QHash<QString, QString> &args) const;
void executeUri (const std::shared_ptr<linphone::Address> &address) const;
private:
QString mFunctionName;
......@@ -69,13 +77,18 @@ public:
Cli (QObject *parent = Q_NULLPTR);
~Cli () = default;
void executeCommand (const QString &command) noexcept;
void executeCommand (const QString &command) const;
private:
void addCommand (const QString &functionName, const QString &description, Function function, const QHash<QString, Argument> &argsScheme = {}) noexcept;
const QString parseFunctionName (const QString &command) noexcept;
const QHash<QString, QString> parseArgs (const QString &command, const QString functionName, bool &soFarSoGood) noexcept;
void addCommand (
const QString &functionName,
const QString &description,
Function function,
const QHash<QString, Argument> &argsScheme = QHash<QString, Argument>()
);
QString parseFunctionName (const QString &command) const;
QHash<QString, QString> parseArgs (const QString &command) const;
QHash<QString, Command> mCommands;
......
......@@ -24,6 +24,8 @@
#include "../../app/App.hpp"
#include "../../utils/Utils.hpp"
#include "../conference/ConferenceAddModel.hpp"
#include "../conference/ConferenceHelperModel.hpp"
#include "../core/CoreManager.hpp"
#include "CallsListModel.hpp"
......@@ -89,7 +91,7 @@ void CallsListModel::askForTransfer (CallModel *callModel) {
// -----------------------------------------------------------------------------
void CallsListModel::launchAudioCall (const QString &sipUri) const {
void CallsListModel::launchAudioCall (const QString &sipUri, const QHash<QString, QString> &headers) const {
shared_ptr<linphone::Core> core = CoreManager::getInstance()->getCore();
shared_ptr<linphone::Address> address = core->interpretUrl(::Utils::appStringToCoreString(sipUri));
......@@ -100,6 +102,12 @@ void CallsListModel::launchAudioCall (const QString &sipUri) const {
params->enableVideo(false);
CallModel::setRecordFile(params);
QHashIterator<QString, QString> iterator(headers);
while (iterator.hasNext()) {
iterator.next();
params->addCustomHeader(::Utils::appStringToCoreString(iterator.key()), ::Utils::appStringToCoreString(iterator.value()));
}
core->inviteAddressWithParams(address, params);
}
......@@ -137,8 +145,50 @@ void CallsListModel::terminateAllCalls () const {
void CallsListModel::handleCallStateChanged (const shared_ptr<linphone::Call> &call, linphone::CallState state) {
switch (state) {
case linphone::CallStateIncomingReceived:
case linphone::CallStateIncomingReceived: {
// _______________________________________________________________________________________________________________________________________
// _______________________________________________________________________________________________________________________________________
addCall(call);
if (!call->getToHeader("method").empty()) {
shared_ptr<linphone::Core> core = CoreManager::getInstance()->getCore();
qInfo() << QStringLiteral("----Header method----") << Utils::coreStringToAppString(call->getToHeader("method"));
if (call->getToHeader("method") == "join-conference") {
if (core->getConference() == NULL) { // TODO change this condition, use isInConference() (need to be initiate)
qWarning() << QStringLiteral("Not in a conference,responding to join-conference as a call.");
break;
}
shared_ptr<linphone::Conference> currentConference = core->getConference();
QString conferenceIdHeader = Utils::coreStringToAppString(call->getToHeader("conference-id"));
qInfo() << QStringLiteral("conference id asked: '%1'").arg(conferenceIdHeader);
qWarning() << QStringLiteral("current conference id: '%1'").arg(Utils::coreStringToAppString(currentConference->getId()));
if (currentConference->getId().empty() || currentConference->getId() != Utils::appStringToCoreString(conferenceIdHeader)) {
qWarning() << QStringLiteral("trying to join conference with invalid conference-id, `%1`").arg(conferenceIdHeader);
qWarning() << QStringLiteral("join-conference managed as a call ");
break;
}
qInfo() << QStringLiteral("join conference: `%1`").arg(Utils::coreStringToAppString(currentConference->getId()));
ConferenceHelperModel helperModel;
ConferenceHelperModel::ConferenceAddModel *addModel = helperModel.getAddModel();
CallModel *callModel = &call->getData<CallModel>("call-model");
callModel->accept();
addModel->addToConference(call->getRemoteAddress());
addModel->update();
break;
}
}
break;
}
// _______________________________________________________________________________________________________________________________________
// _______________________________________________________________________________________________________________________________________
case linphone::CallStateOutgoingInit:
if (!call->getToAddress()->getHeader("method").empty())
qInfo() << QStringLiteral("----Header method----") << Utils::coreStringToAppString(call->getToAddress()->getHeader("method"));
addCall(call);
break;
......
......@@ -45,7 +45,7 @@ public:
void askForTransfer (CallModel *callModel);
Q_INVOKABLE void launchAudioCall (const QString &sipUri) const;
Q_INVOKABLE void launchAudioCall (const QString &sipUri, const QHash<QString, QString> &headers = {}) const;
Q_INVOKABLE void launchVideoCall (const QString &sipUri) const;
Q_INVOKABLE int getRunningCallsNumber () const;
......
......@@ -24,7 +24,6 @@
#include "../../utils/Utils.hpp"
#include "../core/CoreManager.hpp"
#include "../sip-addresses/SipAddressesProxyModel.hpp"
#include "ConferenceAddModel.hpp"
#include "ConferenceHelperModel.hpp"
......
......@@ -27,17 +27,18 @@
#include <QSortFilterProxyModel>
#include "ConferenceHelperModel.hpp"
// =============================================================================
// Sip addresses not in conference.
// Can filter the sip addresses with a pattern.
// =============================================================================
class CallModel;
class ConferenceAddModel;
namespace linphone {
class Conference;
class Core;
class Conference;
class Core;
}
class ConferenceHelperModel : public QSortFilterProxyModel {
......@@ -53,6 +54,10 @@ public:
QHash<int, QByteArray> roleNames () const override;
ConferenceAddModel *getConferenceAddModel () const {
return mConferenceAddModel;
}
Q_INVOKABLE void setFilter (const QString &pattern);
protected:
......@@ -60,9 +65,6 @@ protected:
bool lessThan (const QModelIndex &left, const QModelIndex &right) const override;
private:
ConferenceAddModel *getConferenceAddModel () const {
return mConferenceAddModel;
}
ConferenceAddModel *mConferenceAddModel = nullptr;
......
......@@ -23,6 +23,7 @@
#ifndef UTILS_H_
#define UTILS_H_
#include <QObject>
#include <QString>
// =============================================================================
......@@ -54,6 +55,46 @@ namespace Utils {
// Returns the same path given in parameter if `filePath` exists.
// Otherwise returns a safe path with a unique number before the extension.
QString getSafeFilePath (const QString &filePath, bool *soFarSoGood = nullptr);
// Connect once to a member function.
template<typename Func1, typename Func2>
static inline QMetaObject::Connection connectOnce (
typename QtPrivate::FunctionPointer<Func1>::Object *sender,
Func1 signal,
typename QtPrivate::FunctionPointer<Func2>::Object *receiver,
Func2 slot
) {
QMetaObject::Connection connection = QObject::connect(sender, signal, receiver, slot);
QMetaObject::Connection *deleter = new QMetaObject::Connection();
*deleter = QObject::connect(sender, signal, [connection, deleter] {
QObject::disconnect(connection);
QObject::disconnect(*deleter);
delete deleter;
});
return connection;
}
// Connect once to a function.
template<typename Func1, typename Func2>
static inline QMetaObject::Connection connectOnce (
typename QtPrivate::FunctionPointer<Func1>::Object *sender,
Func1 signal,
const QObject *receiver,
Func2 slot
) {
QMetaObject::Connection connection = QObject::connect(sender, signal, receiver, slot);
QMetaObject::Connection *deleter = new QMetaObject::Connection();
*deleter = QObject::connect(sender, signal, [connection, deleter] {
QObject::disconnect(connection);
QObject::disconnect(*deleter);
delete deleter;
});
return connection;
}
}
#endif // UTILS_H_
bcmatroska2 @ ac155e02
Subproject commit 4c7acea973ac94e559d52e45312e90cf4d0f6247
Subproject commit ac155e025efbcb13778cb436babb80e0f4fd6e72
belcard @ a2e36ce3
Subproject commit 38eba4c264b5e45c8650c8ccf05f359541d8c4ae
Subproject commit a2e36ce337b7cba0bd6fddec5912b7134dc627ba
belle-sip @ a19b7e3e
Subproject commit 647393a5e4b49551f34e213c91d80b675c60a7f6
Subproject commit a19b7e3e833dc121f91452e40658dd80f8f0c145
belr @ a6380ecc
Subproject commit df0f6640647925b1225eba011a40ce988ede5c57
Subproject commit a6380eccb56bb070712971189912f41eb7f047a3
linphone @ d88e17cd
Subproject commit b9b283dcdb0b19a976df8d5cccba43c373727ee8
Subproject commit d88e17cd3ab831f2a9ce5ae04f796caff2a93cc2
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment