Multiple interface and functionality improvements
- Enable start of the control center after login
- Enable start of the bundled NFD when the center starts
- Slightly refactored implementation of how ndn-autoconfig starting is
handled
- Add ability to stop NFD when the center stops
Change-Id: Ieb19b8c0a3e75a5a91961e87c8ccffad364d5170
diff --git a/src/tray-menu.cpp b/src/tray-menu.cpp
index 2ec3cb4..9bcdaa4 100644
--- a/src/tray-menu.cpp
+++ b/src/tray-menu.cpp
@@ -20,8 +20,12 @@
#include "tray-menu.hpp"
#include "tray-menu.moc"
+#include <chrono>
+#include <functional>
+
#include <ndn-cxx/face.hpp>
#include <ndn-cxx/interest.hpp>
+#include <boost/algorithm/string/replace.hpp>
#ifdef OSX_BUILD
#define CONNECT_ICON ":/res/icon-connected-black.png"
@@ -37,13 +41,22 @@
namespace ndn {
namespace ncc {
+/**
+ * @brief Maximum number of lines that can show up in auto-config status tab view
+ */
+const int MAX_LINES_IN_AUTOCONF_STATUS = 100;
+
+#ifdef OSX_BUILD
+const QString AUTO_START_SUFFIX = "Library/LaunchAgents/net.named-data.control-center.plist";
+#endif // OSX_BUILD
+
TrayMenu::TrayMenu(QQmlContext* context, Face& face)
: m_context(context)
, m_isNfdRunning(false)
, m_menu(new QMenu(this))
, m_entryPref(new QAction("Preferences...", m_menu))
, m_entrySec(new QAction("Security...", m_menu))
- , m_acProc(new QProcess())
+ , m_acProc(nullptr)
, m_settings(new QSettings())
#ifdef OSX_BUILD
, m_entryEnableCli(new QAction("Enable Command Terminal Usage...", m_menu))
@@ -55,18 +68,15 @@
, m_face(face)
{
connect(m_entryPref, SIGNAL(triggered()), this, SIGNAL(showApp()));
- connect(this, SIGNAL(showApp()), this, SLOT(showPref()));
connect(m_entrySec, SIGNAL(triggered()), m_keyViewerDialog, SLOT(present()));
connect(m_entryQuit, SIGNAL(triggered()), this, SLOT(quitApp()));
connect(this, SIGNAL(nfdActivityUpdate(bool)), this, SLOT(updateNfdActivityIcon(bool)),
Qt::QueuedConnection);
- m_context->setContextProperty("startStopButtonText", QVariant::fromValue(QString("Start NFD")));
- m_context->setContextProperty("acText", QVariant::fromValue(QString("")));
-
m_menu->addAction(m_entryPref);
m_menu->addAction(m_entrySec);
+
#ifdef OSX_BUILD
connect(m_entryEnableCli, SIGNAL(triggered()), this, SLOT(enableCli()));
m_menu->addAction(m_entryEnableCli);
@@ -74,156 +84,85 @@
connect(m_checkForUpdates, SIGNAL(triggered()), this, SLOT(checkForUpdates()));
m_menu->addAction(m_checkForUpdates);
#endif
+
m_menu->addAction(m_entryQuit);
m_tray = new QSystemTrayIcon(this);
m_tray->setContextMenu(m_menu);
- connect(m_tray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
- this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
m_tray->setIcon(QIcon(DISCONNECT_ICON));
m_tray->show();
- if (isAutoConfigEnabled()) {
- stopAutoConfig(); // Make sure no more than one auto-config process exist
- QTimer* mTimer = new QTimer(this);
- mTimer->setSingleShot(true);
- connect(mTimer, SIGNAL(timeout()), SLOT(startAutoConfig()));
- mTimer->start(2000);
+
+ // delaying for the case when NFD is already started (to avoid starting / to avoid starting autoconfig
+ if (isNfdAutoStartEnabled()) {
+ scheduleDelayedTask(std::chrono::seconds(5), [this] {
+ if (!m_isNfdRunning) {
+ startNfd();
+ }
+ });
}
}
TrayMenu::~TrayMenu()
{
+ // delete for all new's
}
-void
-TrayMenu::appendMsg(QString &target, QString source)
-{
- int maxLine = 100; // Max line that will show in auto-config status tab view
- if (target.count(QString("\n")) > maxLine) { // Only when target QString has more line than maxLine, it needs remove line
- int end = target.indexOf(QString("\n"));
- target.remove(0, end + 1); // Remove the first line of target
- }
- target.append(source);
-}
-
-void
-TrayMenu::showPref()
-{
- m_context->setContextProperty("acText", QVariant::fromValue(m_autoConfigMsg)); // Update auto-config status tab view
-}
-
-void
-TrayMenu::processOutput()
-{
- std::string msg = m_acProc->readAllStandardOutput().toStdString();
- if (msg != "") {
- appendMsg(m_autoConfigMsg, QString::fromStdString(msg));
- }
- msg = m_acProc->readAllStandardError().toStdString();
- if (msg != "") {
- appendMsg(m_autoConfigMsg, QString::fromStdString(msg));
- }
- m_context->setContextProperty("acText", QVariant::fromValue(m_autoConfigMsg));
-}
+////////////////////////////////////////////////////////////////////////////////
+// NFD Control Center start on login
Q_INVOKABLE bool
-TrayMenu::isAutoConfigEnabled()
+TrayMenu::isNccAutoStartEnabled() const
{
- bool sAutoConfig = m_settings.value("main/auto-config", QVariant(false)).toBool();
- return sAutoConfig;
-}
-
-void
-TrayMenu::startAutoConfig()
-{
- if (m_isNfdRunning) {
- appendMsg(m_autoConfigMsg, QString("NDN auto configuration will start...\n"));
- connect(m_acProc, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutput()));
- connect(m_acProc, SIGNAL(readyReadStandardError()), this, SLOT(processOutput()));
- m_acProc->start((QCoreApplication::applicationDirPath().toStdString() + "/../Platform/ndn-autoconfig").c_str(),
- QStringList()
- << "--daemon");
- }
-}
-
-void
-TrayMenu::stopAutoConfig()
-{
-#ifdef OSX_BUILD
- QProcess* proc = new QProcess();
- connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater()));
- proc->startDetached("killall", QStringList() << "ndn-autoconfig");
-#endif
+ QFileInfo file(QDir::home().path() + "/" + AUTO_START_SUFFIX);
+ return file.exists() && file.isFile();
}
Q_INVOKABLE void
-TrayMenu::startStopAutoConfig(bool autoConfig)
+TrayMenu::enableDisableNccAutoStart(bool isEnabled)
{
- if (m_isNfdRunning) {
- if (autoConfig) {
- stopAutoConfig(); // Make sure no more than one auto-config process exist
- QTimer* mTimer = new QTimer(this);
- mTimer->setSingleShot(true);
- connect(mTimer, SIGNAL(timeout()), SLOT(startAutoConfig()));
- mTimer->start(2000);
- m_context->setContextProperty("acText", QVariant::fromValue(m_autoConfigMsg));
- m_settings.setValue("main/auto-config", true);
- }
- else {
- stopAutoConfig();
- appendMsg(m_autoConfigMsg, QString("NDN auto configuration will be stopped!\n"));
- m_context->setContextProperty("acText", QVariant::fromValue(m_autoConfigMsg));
- m_settings.setValue("main/auto-config", false);
- }
- }
- else { // No need to start or stop auto-config when NFD is not running, but it needs to update settings
- if (autoConfig) {
- m_settings.setValue("main/auto-config", true);
- }
- else {
- m_settings.setValue("main/auto-config", false);
- }
+ if (isEnabled) {
+ QFile file(QDir::home().path() + "/" + AUTO_START_SUFFIX);
+ file.open(QIODevice::WriteOnly);
+
+ std::string plist = R"PLIST(<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<?xml version="1.0" encoding="UTF-8"?>
+<plist version="1.0">
+ <dict>
+ <key>Label</key>
+ <string>net.named-data.control-center</string>
+ <key>ProgramArguments</key>
+ <array>
+ <string>%%PATH%%</string>
+ </array>
+ <key>RunAtLoad</key>
+ <true/>
+ <key>KeepAlive</key>
+ <false/>
+ </dict>
+</plist>
+)PLIST"; //"
+ boost::replace_all(plist, "%%PATH%%", QCoreApplication::applicationFilePath().toStdString());
+ file.write(plist.data(), plist.size());
+ file.close();
+ } else {
+ QFile::remove(QDir::home().path() + "/" + AUTO_START_SUFFIX);
}
}
-void
-TrayMenu::quitApp()
-{
- QCoreApplication::exit(0);
-}
+////////////////////////////////////////////////////////////////////////////////
+// NFD start on launch
-void
-TrayMenu::iconActivated(QSystemTrayIcon::ActivationReason reason)
+Q_INVOKABLE bool
+TrayMenu::isNfdAutoStartEnabled() const
{
- switch (reason) {
- // case QSystemTrayIcon::Trigger:
- // emit showApp();
- // break;
- case QSystemTrayIcon::Context:
- break;
- default:
- break;
- }
+ return m_settings->value("ENABLE_AUTO_START", false).toBool();
}
Q_INVOKABLE void
-TrayMenu::startStopNfd(bool autoConfig)
+TrayMenu::enableDisableNfdAutoStart(bool isEnabled)
{
+ m_settings->setValue("ENABLE_AUTO_START", isEnabled);
if (!m_isNfdRunning) {
startNfd();
- if (autoConfig) {
- stopAutoConfig(); // Make sure no more than one auto-config process exist
- QTimer* mTimer = new QTimer(this);
- mTimer->setSingleShot(true);
- connect(mTimer, SIGNAL(timeout()), SLOT(startAutoConfig()));
- mTimer->start(2000);
- m_context->setContextProperty("acText", QVariant::fromValue(m_autoConfigMsg));
- }
- }
- else {
- stopNfd();
- stopAutoConfig();
- appendMsg(m_autoConfigMsg, QString("NDN auto configuration will be stopped!\n"));
- m_context->setContextProperty("acText", QVariant::fromValue(m_autoConfigMsg));
}
}
@@ -233,21 +172,10 @@
#ifdef OSX_BUILD
QProcess* proc = new QProcess();
connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater()));
- proc->startDetached((QCoreApplication::applicationDirPath().toStdString() + "/../Platform/nfd").c_str(),
+ proc->startDetached((QCoreApplication::applicationDirPath() + "/../Platform/nfd"),
QStringList()
<< "--config"
- << (QCoreApplication::applicationDirPath().toStdString() + "/../etc/ndn/nfd.conf").c_str());
-
-
- // #endif
- // QProcess * proc = new QProcess();
- // connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater()));
- // #ifdef __linux__
- // proc->start("gksudo", QStringList() << NFD_START_COMMAND);
- // #else
- // proc->start("osascript", QStringList()
- // << "-e"
- // << "do shell script \"" NFD_START_COMMAND "\" with administrator privileges");
+ << (QCoreApplication::applicationDirPath() + "/../etc/ndn/nfd.conf"));
#endif
}
@@ -261,6 +189,117 @@
#endif
}
+////////////////////////////////////////////////////////////////////////////////
+// NDN Autoconfig
+
+Q_INVOKABLE bool
+TrayMenu::isNdnAutoConfigEnabled() const
+{
+ return m_settings->value("ENABLE_NDN_AUTO_CONFIG", false).toBool();
+}
+
+Q_INVOKABLE void
+TrayMenu::enableDisableNdnAutoConfig(bool isEnabled)
+{
+ m_settings->setValue("ENABLE_NDN_AUTO_CONFIG", isEnabled);
+
+ if (isEnabled) {
+ startNdnAutoConfig();
+ }
+ else {
+ stopNdnAutoConfig();
+ }
+}
+
+void
+TrayMenu::startNdnAutoConfig()
+{
+ if (m_acProc != nullptr) {
+ return;
+ }
+
+ stopNdnAutoConfig(false); // Make sure no more than one auto-config process exist
+
+ if (!m_isNfdRunning) {
+ return;
+ }
+
+ m_acProc = new QProcess();
+
+ scheduleDelayedTask(std::chrono::seconds(2), [this] {
+ appendNdnAutoConfigStatus("NDN auto configuration starting...\n");
+ m_acProc->start(QCoreApplication::applicationDirPath() + "/../Platform/ndn-autoconfig",
+ QStringList() << "--daemon");
+ connect(m_acProc, SIGNAL(readyReadStandardOutput()), this, SLOT(processOutput()));
+ connect(m_acProc, SIGNAL(readyReadStandardError()), this, SLOT(processOutput()));
+ });
+}
+
+void
+TrayMenu::stopNdnAutoConfig(bool appendStatus)
+{
+ m_acProc = nullptr;
+ QProcess* proc = new QProcess();
+ connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater()));
+ proc->startDetached("killall", QStringList() << "ndn-autoconfig");
+ if (appendStatus) {
+ appendNdnAutoConfigStatus("NDN auto configuration stopped\n");
+ }
+}
+
+void
+TrayMenu::appendNdnAutoConfigStatus(const QString& message)
+{
+ if (message == "") {
+ return;
+ }
+ if (m_ndnAutoConfigMsg.count("\n") > MAX_LINES_IN_AUTOCONF_STATUS) {
+ int end = m_ndnAutoConfigMsg.indexOf("\n");
+ m_ndnAutoConfigMsg.remove(0, end + 1);
+ }
+ m_ndnAutoConfigMsg.append(message);
+
+ m_context->setContextProperty("ndnAutoConfigText", m_ndnAutoConfigMsg);
+}
+
+void
+TrayMenu::processOutput()
+{
+ if (m_acProc == nullptr) {
+ return;
+ }
+ appendNdnAutoConfigStatus(m_acProc->readAllStandardOutput());
+ appendNdnAutoConfigStatus(m_acProc->readAllStandardError());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NFD stop on shutdown
+
+Q_INVOKABLE bool
+TrayMenu::isNfdStopOnExitEnabled() const
+{
+ return m_settings->value("ENABLE_SHUTDOWN", false).toBool();
+}
+
+Q_INVOKABLE void
+TrayMenu::enableDisableNfdStopOnExit(bool isEnabled)
+{
+ m_settings->setValue("ENABLE_SHUTDOWN", isEnabled);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Misc
+
+void
+TrayMenu::quitApp()
+{
+ if (isNfdStopOnExitEnabled()) {
+ stopNdnAutoConfig();
+ stopNfd();
+ }
+ QCoreApplication::exit(0);
+}
+
Q_INVOKABLE void
TrayMenu::addDeleteRoute()
{
@@ -296,11 +335,12 @@
if (isActive) {
m_tray->setIcon(QIcon(CONNECT_ICON));
- m_context->setContextProperty("startStopButtonText", QVariant::fromValue(QString("Stop NFD")));
+ if (isNdnAutoConfigEnabled()) {
+ startNdnAutoConfig();
+ }
}
else {
m_tray->setIcon(QIcon(DISCONNECT_ICON));
- m_context->setContextProperty("startStopButtonText", QVariant::fromValue(QString("Start NFD")));
}
}
@@ -308,44 +348,16 @@
TrayMenu::enableCli()
{
#ifdef OSX_BUILD
- AuthorizationRef authorizationRef;
- OSStatus status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,
- kAuthorizationFlagDefaults, &authorizationRef);
- if (status != errAuthorizationSuccess)
- return;
+ QProcess* proc = new QProcess();
+ connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater()));
- AuthorizationItem item = { kAuthorizationRightExecute, 0, 0, 0 };
- AuthorizationRights rights = { 1, &item };
- const AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed
- | kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights;
-
- status = AuthorizationCopyRights(authorizationRef, &rights, kAuthorizationEmptyEnvironment,
- flags, 0);
- if (status != errAuthorizationSuccess)
- return;
-
- char const* mkdir_arg[] = { "-p", "/usr/local/bin", nullptr };
- char const* mkdir = "/bin/mkdir";
- AuthorizationExecuteWithPrivileges(authorizationRef,
- mkdir,
- kAuthorizationFlagDefaults, (char**)mkdir_arg, nullptr);
-
- std::vector<std::string> arguments = { "-f", "-s",
- QCoreApplication::applicationDirPath().toStdString() + "/../Resources/ndn",
- "/usr/local/bin/ndn" };
- std::vector<const char*> args;
- for (const auto& i : arguments) {
- args.push_back(i.c_str());
- }
- args.push_back(nullptr);
-
- char const* helperTool = "/bin/ln";
- AuthorizationExecuteWithPrivileges(authorizationRef,
- helperTool,
- kAuthorizationFlagDefaults,
- (char**)args.data(), NULL);
-
- AuthorizationFree(authorizationRef, kAuthorizationFlagDestroyRights);
+ proc->start("osascript", QStringList()
+ << "-e"
+ << "do shell script \""
+ "/bin/mkdir -vp /usr/local/bin; "
+ "/bin/ln -s -f '" + QCoreApplication::applicationDirPath() +
+ "/../Resources/ndn" + "' /usr/local/bin/ndn;"
+ "\" with administrator privileges");
#endif
}
@@ -357,5 +369,19 @@
}
#endif // OSX_BUILD
+void
+TrayMenu::scheduleDelayedTask(std::chrono::milliseconds delays, const std::function<void()>& task)
+{
+ auto timer = new QTimer();
+ timer->setSingleShot(true);
+ timer->setInterval(delays);
+ connect(timer, &QTimer::timeout,
+ [task, timer] {
+ task();
+ timer->deleteLater();
+ });
+ timer->start();
+}
+
} // namespace ncc
} // namespace ndn