blob: 294588e0bfbc318f9577596a121a5d57e6d90a57 [file] [log] [blame]
Alexander Afanasyevfa2f6622016-12-25 12:28:00 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Alexander Afanasyev1cf5c432017-01-13 23:22:15 -08003 * Copyright (c) 2013-2017, Regents of the University of California.
Jared Lindblom06405c42013-01-17 20:48:39 -08004 *
Alexander Afanasyevfa2f6622016-12-25 12:28:00 -08005 * This file is part of ChronoShare, a decentralized file sharing application over NDN.
Jared Lindblom06405c42013-01-17 20:48:39 -08006 *
Alexander Afanasyevfa2f6622016-12-25 12:28:00 -08007 * ChronoShare is free software: you can redistribute it and/or modify it under the terms
8 * of the GNU General Public License as published by the Free Software Foundation, either
9 * version 3 of the License, or (at your option) any later version.
Jared Lindblom06405c42013-01-17 20:48:39 -080010 *
Alexander Afanasyevfa2f6622016-12-25 12:28:00 -080011 * ChronoShare is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 * PARTICULAR PURPOSE. See the GNU General Public License for more details.
Jared Lindblom06405c42013-01-17 20:48:39 -080014 *
Alexander Afanasyevfa2f6622016-12-25 12:28:00 -080015 * You should have received copies of the GNU General Public License along with
16 * ChronoShare, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * See AUTHORS.md for complete list of ChronoShare authors and contributors.
Jared Lindblom06405c42013-01-17 20:48:39 -080019 */
20
Alexander Afanasyevf4cde4e2016-12-25 13:42:57 -080021#include "chronosharegui.hpp"
Lijing Wang152449c2015-05-31 16:25:16 -070022#include "core/logging.hpp"
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -080023
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080024#include <QDesktopServices>
Zhenkai Zhuce04cb42013-02-25 15:29:31 -080025#include <QDir>
Zhenkai Zhu25e13582013-02-27 15:33:01 -080026#include <QFileInfo>
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080027#include <QValidator>
Alexander Afanasyev9ca444e2013-01-25 16:29:35 -080028
Yukai Tu73287f52017-02-06 15:46:40 -080029#include <boost/throw_exception.hpp>
Lijing Wang152449c2015-05-31 16:25:16 -070030#include <thread>
Alexander Afanasyev9ca444e2013-01-25 16:29:35 -080031
Lijing Wang152449c2015-05-31 16:25:16 -070032namespace ndn {
33namespace chronoshare {
Jared Lindblom06405c42013-01-17 20:48:39 -080034
Lijing Wang152449c2015-05-31 16:25:16 -070035namespace fs = boost::filesystem;
36
37static const std::string HTTP_SERVER_ADDRESS = "localhost";
38static const std::string HTTP_SERVER_PORT = "9001";
Zhenkai Zhu6b53f932013-03-02 20:46:02 -080039#ifdef _DEBUG
Lijing Wang152449c2015-05-31 16:25:16 -070040static const std::string DOC_ROOT = "gui/html";
Zhenkai Zhu6b53f932013-03-02 20:46:02 -080041#else
Lijing Wang152449c2015-05-31 16:25:16 -070042static const std::string DOC_ROOT = ":/html";
Zhenkai Zhu6b53f932013-03-02 20:46:02 -080043#endif
Alexander Afanasyevcf771142013-03-19 11:15:27 -070044static const QString ICON_BIG_FILE(":/images/chronoshare-big.png");
45static const QString ICON_TRAY_FILE(":/images/" TRAY_ICON);
Zhenkai Zhud9429222013-02-25 22:34:09 -080046
Alexander Afanasyev1cf5c432017-01-13 23:22:15 -080047_LOG_INIT(Gui);
Alexander Afanasyev83a53002013-01-24 11:12:01 -080048
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080049ChronoShareGui::ChronoShareGui(QWidget* parent)
Jared Lindblomc8ffd592013-01-25 00:00:30 -080050 : QDialog(parent)
Zhenkai Zhud9429222013-02-25 22:34:09 -080051 , m_httpServer(0)
Alexander Afanasyev1b51bb52017-02-10 19:52:32 -080052#ifdef AUTOUPDATE
53 , m_sparkle(CHRONOSHARE_APPCAST)
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -080054#endif
Jared Lindblom06405c42013-01-17 20:48:39 -080055{
Jared Lindblomf4b900e2013-02-07 18:08:47 -080056 setWindowTitle("Settings");
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080057 setMinimumWidth(600);
Jared Lindblomc8ffd592013-01-25 00:00:30 -080058
Lijing Wang152449c2015-05-31 16:25:16 -070059 labelUsername = new QLabel("Username(hint: /<username>)");
Jared Lindblomc8ffd592013-01-25 00:00:30 -080060 labelSharedFolder = new QLabel("Shared Folder Name");
61 labelSharedFolderPath = new QLabel("Shared Folder Path");
Zhenkai Zhuf3ea97a2013-01-30 15:20:52 -080062
Zhenkai Zhud5756312013-01-31 13:49:45 -080063 QRegExp regex("(/[^/]+)+$");
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080064 QValidator* prefixValidator = new QRegExpValidator(regex, this);
Zhenkai Zhuf3ea97a2013-01-30 15:20:52 -080065
Jared Lindblomc8ffd592013-01-25 00:00:30 -080066 editUsername = new QLineEdit();
Zhenkai Zhuf3ea97a2013-01-30 15:20:52 -080067 editUsername->setValidator(prefixValidator);
68
69 QRegExp noPureWhiteSpace("^\\S+.*$");
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080070 QValidator* wsValidator = new QRegExpValidator(noPureWhiteSpace, this);
Jared Lindblomc8ffd592013-01-25 00:00:30 -080071 editSharedFolder = new QLineEdit();
Zhenkai Zhuf3ea97a2013-01-30 15:20:52 -080072 editSharedFolder->setValidator(wsValidator);
73
Jared Lindblomc8ffd592013-01-25 00:00:30 -080074 editSharedFolderPath = new QLineEdit();
75 editSharedFolderPath->setReadOnly(true);
76 QPalette pal = editSharedFolderPath->palette();
77 pal.setColor(QPalette::Active, QPalette::Base, pal.color(QPalette::Disabled, QPalette::Base));
78 editSharedFolderPath->setPalette(pal);
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -080079 button = new QPushButton("Save and apply settings");
Zhenkai Zhu630d0f12013-02-28 17:21:54 -080080
81 QString versionString = QString("Version: ChronoShare v%1").arg(CHRONOSHARE_VERSION);
82 label = new QLabel(versionString, this);
Jared Lindblomc8ffd592013-01-25 00:00:30 -080083
84 connect(button, SIGNAL(clicked()), this, SLOT(changeSettings()));
85
Lijing Wang152449c2015-05-31 16:25:16 -070086 mainLayout = new QVBoxLayout; // vertically
Jared Lindblomc8ffd592013-01-25 00:00:30 -080087 mainLayout->addWidget(labelUsername);
88 mainLayout->addWidget(editUsername);
89 mainLayout->addWidget(labelSharedFolder);
90 mainLayout->addWidget(editSharedFolder);
91 mainLayout->addWidget(labelSharedFolderPath);
92 mainLayout->addWidget(editSharedFolderPath);
93 mainLayout->addWidget(button);
94 mainLayout->addWidget(label);
95 setLayout(mainLayout);
96
Alexander Afanasyev83a53002013-01-24 11:12:01 -080097 // create actions that result from clicking a menu option
Zhenkai Zhu25e13582013-02-27 15:33:01 -080098 createActionsAndMenu();
Jared Lindblomb4032e22013-01-17 23:50:51 -080099
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800100 // create tray icon
101 createTrayIcon();
Jared Lindblomb4032e22013-01-17 23:50:51 -0800102
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800103 // set icon image
104 setIcon();
Jared Lindblom06405c42013-01-17 20:48:39 -0800105
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800106 // show tray icon
107 m_trayIcon->show();
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -0800108
Zhenkai Zhud5756312013-01-31 13:49:45 -0800109 // load settings
Yukai Tu05126792016-10-11 15:03:30 -0700110 if (!loadSettings() || m_username.isNull() || m_username == "" || m_sharedFolderName.isNull() ||
111 m_sharedFolderName == "" || m_dirPath.isNull() || m_dirPath == "") {
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800112 // prompt user to choose folder
Yukai Tu73287f52017-02-06 15:46:40 -0800113 _LOG_DEBUG("First Time Setup");
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800114 openMessageBox("First Time Setup",
115 "Please enter a username, shared folder name and choose the shared folder path on your local filesystem.");
116 viewSettings();
117 openFileDialog();
118 viewSettings();
119 }
120 else {
Yukai Tu05126792016-10-11 15:03:30 -0700121 startBackend();
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800122 }
Zhenkai Zhud5756312013-01-31 13:49:45 -0800123}
124
Yukai Tu73287f52017-02-06 15:46:40 -0800125ChronoShareGui::ChronoShareGui(QString dirPath, QString username, QString sharedFolderName, QWidget* parent)
126 : QDialog(parent)
127 , m_dirPath(dirPath)
128 , m_username(username)
129 , m_sharedFolderName(sharedFolderName)
Alexander Afanasyev1b51bb52017-02-10 19:52:32 -0800130#ifdef AUTOUPDATE
131 , m_sparkle(CHRONOSHARE_APPCAST)
132#endif
Yukai Tu73287f52017-02-06 15:46:40 -0800133{
134 if (m_username.isNull() || m_username == "" || m_sharedFolderName.isNull() ||
135 m_sharedFolderName == "" || m_dirPath.isNull() || m_dirPath == "") {
136 // prompt user to choose folder
137 BOOST_THROW_EXCEPTION(Error("Some error with init info"));
138 }
139 else {
140 startBackend();
141 }
142}
143
Zhenkai Zhud5756312013-01-31 13:49:45 -0800144void
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800145ChronoShareGui::startBackend(bool restart /*=false*/)
Zhenkai Zhud5756312013-01-31 13:49:45 -0800146{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800147 if (m_username.isNull() || m_username == "" || m_sharedFolderName.isNull() ||
148 m_sharedFolderName == "" || m_dirPath.isNull() || m_dirPath == "") {
149 _LOG_DEBUG("Don't start backend, because settings are in progress or incomplete");
150 return;
151 }
152
153 if (m_watcher != 0 && m_dispatcher != 0) {
154 if (!restart) {
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800155 return;
156 }
157
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800158 _LOG_DEBUG("Restarting Dispatcher and FileWatcher for the new location or new username");
Lijing Wang152449c2015-05-31 16:25:16 -0700159 m_watcher.reset(); // stop filewatching ASAP
160 m_dispatcher.reset(); // stop dispatcher ASAP, but after watcher(to prevent triggering callbacks
161 // on deleted object)
Yukai Tu05126792016-10-11 15:03:30 -0700162 m_ioServiceWork.reset();
Yukai Tu73287f52017-02-06 15:46:40 -0800163 m_ioServiceManager->handle_stop();
Yukai Tu05126792016-10-11 15:03:30 -0700164 m_NetworkThread.join();
Yukai Tu73287f52017-02-06 15:46:40 -0800165 delete m_ioServiceManager;
Yukai Tuf6db5bf2017-04-14 12:13:03 -0700166
167 m_face.reset();
Zhenkai Zhud5756312013-01-31 13:49:45 -0800168 }
169
Lijing Wang152449c2015-05-31 16:25:16 -0700170 fs::path realPathToFolder(m_dirPath.toStdString());
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800171 realPathToFolder /= m_sharedFolderName.toStdString();
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800172
Yukai Tu729b2892017-02-09 21:15:48 -0800173 _LOG_DEBUG("m_dispatcher username:" << m_username.toStdString()
Lijing Wang152449c2015-05-31 16:25:16 -0700174 << " m_sharedFolderName:" << m_sharedFolderName.toStdString()
Yukai Tu729b2892017-02-09 21:15:48 -0800175 << " realPathToFolder: " << realPathToFolder);
Lijing Wang152449c2015-05-31 16:25:16 -0700176
177 m_ioService.reset(new boost::asio::io_service());
178 m_face.reset(new Face(*m_ioService));
179 m_dispatcher.reset(new Dispatcher(m_username.toStdString(), m_sharedFolderName.toStdString(),
180 realPathToFolder, *m_face));
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800181
182 // Alex: this **must** be here, otherwise m_dirPath will be uninitialized
Lijing Wang152449c2015-05-31 16:25:16 -0700183 m_watcher.reset(new FsWatcher(*m_ioService, realPathToFolder.string().c_str(),
184 bind(&Dispatcher::Did_LocalFile_AddOrModify, m_dispatcher.get(), _1),
185 bind(&Dispatcher::Did_LocalFile_Delete, m_dispatcher.get(), _1)));
Yukai Tu05126792016-10-11 15:03:30 -0700186 try {
Yukai Tu73287f52017-02-06 15:46:40 -0800187 m_ioServiceManager = new IoServiceManager(*m_ioService);
188 m_NetworkThread = std::thread(&IoServiceManager::run, m_ioServiceManager);
Yukai Tu05126792016-10-11 15:03:30 -0700189 }
190 catch (const std::exception& e) {
191 _LOG_ERROR("Start IO service or Face failed");
192 openWarningMessageBox("", "WARNING: Cannot allocate thread for face and io_service!",
193 QString("Starting chronoshare failed"
194 "Exception caused: %1")
195 .arg(e.what()));
196 // stop filewatching ASAP
197 m_watcher.reset();
198 m_dispatcher.reset();
199 return;
200 }
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800201
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800202 if (m_httpServer != 0) {
203 // no need to restart webserver if it already exists
204 return;
205 }
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800206
Zhenkai Zhud9429222013-02-25 22:34:09 -0800207 QFileInfo indexHtmlInfo(":/html/index.html");
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800208 if (indexHtmlInfo.exists()) {
209 try {
Zhenkai Zhu8da33652013-03-04 10:52:03 -0800210 m_httpServer = new http::server::server(HTTP_SERVER_ADDRESS, HTTP_SERVER_PORT, DOC_ROOT);
Lijing Wang152449c2015-05-31 16:25:16 -0700211 m_httpServerThread = std::thread(&http::server::server::run, m_httpServer);
Zhenkai Zhu8da33652013-03-04 10:52:03 -0800212 }
Yukai Tu05126792016-10-11 15:03:30 -0700213 catch (const std::exception& e) {
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800214 _LOG_ERROR("Start http server failed");
Zhenkai Zhu8da33652013-03-04 10:52:03 -0800215 m_httpServer = 0; // just to make sure
Yukai Tu05126792016-10-11 15:03:30 -0700216 openWarningMessageBox("WARNING", "WARNING: Cannot start http server!",
217 QString("Starting http server failed. You will "
218 "not be able to check history from web "
219 "brower. Exception caused: %1")
220 .arg(e.what()));
Zhenkai Zhu8da33652013-03-04 10:52:03 -0800221 }
Zhenkai Zhud9429222013-02-25 22:34:09 -0800222 }
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800223 else {
224 _LOG_ERROR("Http server doc root dir does not exist!");
Zhenkai Zhud9429222013-02-25 22:34:09 -0800225 }
Jared Lindblom06405c42013-01-17 20:48:39 -0800226}
227
228ChronoShareGui::~ChronoShareGui()
229{
Lijing Wang152449c2015-05-31 16:25:16 -0700230 // stop filewatching ASAP
231 m_watcher.reset();
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800232
Lijing Wang152449c2015-05-31 16:25:16 -0700233 // stop dispatcher ASAP, but after watcher to prevent triggering callbacks on the deleted object
234 m_dispatcher.reset();
235
Yukai Tu05126792016-10-11 15:03:30 -0700236 m_ioServiceWork.reset();
Yukai Tu73287f52017-02-06 15:46:40 -0800237 m_ioServiceManager->handle_stop();
Yukai Tu05126792016-10-11 15:03:30 -0700238 m_NetworkThread.join();
Yukai Tu73287f52017-02-06 15:46:40 -0800239 delete m_ioServiceManager;
Yukai Tu05126792016-10-11 15:03:30 -0700240
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800241 if (m_httpServer != 0) {
Zhenkai Zhu8da33652013-03-04 10:52:03 -0800242 m_httpServer->handle_stop();
243 m_httpServerThread.join();
244 delete m_httpServer;
245 }
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -0800246
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800247 // cleanup
248 delete m_trayIcon;
249 delete m_trayIconMenu;
Alexander Afanasyev1b51bb52017-02-10 19:52:32 -0800250
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800251 delete m_openFolder;
252 delete m_viewSettings;
253 delete m_changeFolder;
254 delete m_quitProgram;
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800255
256 delete labelUsername;
257 delete labelSharedFolder;
258 delete editUsername;
259 delete editSharedFolder;
260 delete button;
261 delete label;
262 delete mainLayout;
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800263}
264
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800265void
266ChronoShareGui::openMessageBox(QString title, QString text)
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800267{
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800268 QMessageBox messageBox(this);
269 messageBox.setWindowTitle(title);
270 messageBox.setText(text);
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800271
Alexander Afanasyevcf771142013-03-19 11:15:27 -0700272 messageBox.setIconPixmap(QPixmap(ICON_BIG_FILE));
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800273
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800274 messageBox.exec();
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800275}
276
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800277void
278ChronoShareGui::openMessageBox(QString title, QString text, QString infotext)
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800279{
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800280 QMessageBox messageBox(this);
281 messageBox.setWindowTitle(title);
282 messageBox.setText(text);
283 messageBox.setInformativeText(infotext);
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800284
Alexander Afanasyevcf771142013-03-19 11:15:27 -0700285 messageBox.setIconPixmap(QPixmap(ICON_BIG_FILE));
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800286
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800287 messageBox.exec();
Jared Lindblom06405c42013-01-17 20:48:39 -0800288}
289
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800290void
Yukai Tu05126792016-10-11 15:03:30 -0700291ChronoShareGui::openWarningMessageBox(QString title, QString text, QString infotext)
292{
293 QMessageBox messageBox(this);
294 messageBox.setWindowTitle(title);
295 messageBox.setText(text);
296 messageBox.setInformativeText(infotext);
297
298 messageBox.setIcon(QMessageBox::Warning);
299 messageBox.setStandardButtons(QMessageBox::Ok);
300
301 messageBox.exec();
302}
303
304void
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800305ChronoShareGui::createActionsAndMenu()
Jared Lindblom06405c42013-01-17 20:48:39 -0800306{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800307 _LOG_DEBUG("Create actions");
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800308
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800309 // create the "open folder" action
310 m_openFolder = new QAction(tr("&Open Folder"), this);
311 connect(m_openFolder, SIGNAL(triggered()), this, SLOT(openSharedFolder()));
Jared Lindblom06405c42013-01-17 20:48:39 -0800312
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800313 m_openWeb = new QAction(tr("Open in &Browser"), this);
314 connect(m_openWeb, SIGNAL(triggered()), this, SLOT(openInWebBrowser()));
315
316 m_recentFilesMenu = new QMenu(tr("Recently Changed Files"), this);
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800317 for (int i = 0; i < 5; i++) {
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800318 m_fileActions[i] = new QAction(this);
319 m_fileActions[i]->setVisible(false);
320 connect(m_fileActions[i], SIGNAL(triggered()), this, SLOT(openFile()));
321 m_recentFilesMenu->addAction(m_fileActions[i]);
322 }
323 connect(m_recentFilesMenu, SIGNAL(aboutToShow()), this, SLOT(updateRecentFilesMenu()));
324
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800325 // create the "view settings" action
326 m_viewSettings = new QAction(tr("&View Settings"), this);
327 connect(m_viewSettings, SIGNAL(triggered()), this, SLOT(viewSettings()));
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800328
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800329 // create the "change folder" action
330 m_changeFolder = new QAction(tr("&Change Folder"), this);
331 connect(m_changeFolder, SIGNAL(triggered()), this, SLOT(openFileDialog()));
Jared Lindblomb4032e22013-01-17 23:50:51 -0800332
Alexander Afanasyev1b51bb52017-02-10 19:52:32 -0800333#ifdef AUTOUPDATE
334 m_checkForUpdates = new QAction("Check for updates", this);
335 connect(m_checkForUpdates, SIGNAL(triggered()), this, SLOT(checkForUpdates()));
336#endif // AUTOUPDATE
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -0800337
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800338 // create the "quit program" action
339 m_quitProgram = new QAction(tr("&Quit"), this);
340 connect(m_quitProgram, SIGNAL(triggered()), qApp, SLOT(quit()));
Jared Lindblom06405c42013-01-17 20:48:39 -0800341}
342
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800343void
344ChronoShareGui::createTrayIcon()
Jared Lindblom06405c42013-01-17 20:48:39 -0800345{
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800346 // create a new icon menu
347 m_trayIconMenu = new QMenu(this);
Jared Lindblom06405c42013-01-17 20:48:39 -0800348
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800349 // add actions to the menu
350 m_trayIconMenu->addAction(m_openFolder);
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800351 m_trayIconMenu->addAction(m_openWeb);
352 m_trayIconMenu->addMenu(m_recentFilesMenu);
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800353 m_trayIconMenu->addSeparator();
354 m_trayIconMenu->addAction(m_viewSettings);
355 m_trayIconMenu->addAction(m_changeFolder);
Alexander Afanasyeve8496a32013-03-03 16:10:43 -0800356
Alexander Afanasyev1b51bb52017-02-10 19:52:32 -0800357#ifdef AUTOUPDATE
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800358 m_trayIconMenu->addSeparator();
Zhenkai Zhu3857d712013-02-27 22:51:25 -0800359 m_trayIconMenu->addAction(m_checkForUpdates);
Alexander Afanasyev1b51bb52017-02-10 19:52:32 -0800360#endif // AUTOUPDATE
Alexander Afanasyeve8496a32013-03-03 16:10:43 -0800361
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800362 m_trayIconMenu->addSeparator();
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800363 m_trayIconMenu->addAction(m_quitProgram);
Jared Lindblom06405c42013-01-17 20:48:39 -0800364
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800365 // create new tray icon
366 m_trayIcon = new QSystemTrayIcon(this);
Jared Lindblomb4032e22013-01-17 23:50:51 -0800367
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800368 // associate the menu with the tray icon
369 m_trayIcon->setContextMenu(m_trayIconMenu);
Jared Lindblomb4032e22013-01-17 23:50:51 -0800370
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800371 // handle left click of icon
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800372 connect(m_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this,
373 SLOT(trayIconClicked(QSystemTrayIcon::ActivationReason)));
Jared Lindblom06405c42013-01-17 20:48:39 -0800374}
375
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800376void
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800377ChronoShareGui::setIcon()
Jared Lindblom06405c42013-01-17 20:48:39 -0800378{
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800379 // set the icon image
Alexander Afanasyevcf771142013-03-19 11:15:27 -0700380 m_trayIcon->setIcon(QIcon(ICON_TRAY_FILE));
Jared Lindblom06405c42013-01-17 20:48:39 -0800381}
382
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800383void
384ChronoShareGui::openSharedFolder()
Jared Lindblom06405c42013-01-17 20:48:39 -0800385{
Lijing Wang152449c2015-05-31 16:25:16 -0700386 fs::path realPathToFolder(m_dirPath.toStdString());
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800387 realPathToFolder /= m_sharedFolderName.toStdString();
Zhenkai Zhu872cc592013-03-04 14:20:46 -0800388 QString path = QDir::toNativeSeparators(realPathToFolder.string().c_str());
Zhenkai Zhuce04cb42013-02-25 15:29:31 -0800389 QDesktopServices::openUrl(QUrl("file:///" + path));
Jared Lindblom06405c42013-01-17 20:48:39 -0800390}
Jared Lindblomb4032e22013-01-17 23:50:51 -0800391
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800392void
393ChronoShareGui::openInWebBrowser()
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800394{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800395 QUrl url("http://localhost:9001/");
396 url.setFragment("folderHistory&"
397 "user=" +
398 QUrl::toPercentEncoding(m_username) + "&"
399 "folder=" +
400 QUrl::toPercentEncoding(m_sharedFolderName));
Alexander Afanasyevf63a5142013-02-28 02:21:42 -0800401
402 // i give up. there is double encoding and I have no idea how to fight it...
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800403 QDesktopServices::openUrl(url);
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800404}
405
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800406void
407ChronoShareGui::openFile()
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800408{
409 // figure out who sent the signal
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800410 QAction* pAction = qobject_cast<QAction*>(sender());
411 if (pAction->isEnabled()) {
412// we stored full path of the file in this toolTip field
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800413#ifdef Q_WS_MAC
414 // we do some hack so we could show the file in Finder highlighted
415 QStringList args;
416 args << "-e";
417 args << "tell application \"Finder\"";
418 args << "-e";
419 args << "activate";
420 args << "-e";
421 args << "select POSIX file \"" + pAction->toolTip() + "/" + pAction->text() + "\"";
422 args << "-e";
423 args << "end tell";
424 QProcess::startDetached("osascript", args);
425#else
Lijing Wang152449c2015-05-31 16:25:16 -0700426 // too bad qt couldn't do highlighting for Linux(or Mac)
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800427 QDesktopServices::openUrl(QUrl("file:///" + pAction->toolTip()));
428#endif
429 }
430}
431
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800432void
433ChronoShareGui::updateRecentFilesMenu()
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800434{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800435 for (int i = 0; i < 5; i++) {
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800436 m_fileActions[i]->setVisible(false);
437 }
Lijing Wang152449c2015-05-31 16:25:16 -0700438 m_dispatcher->LookupRecentFileActions(std::bind(&ChronoShareGui::checkFileAction, this, _1, _2, _3),
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800439 5);
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800440}
441
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800442void
443ChronoShareGui::checkFileAction(const std::string& filename, int action, int index)
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800444{
Lijing Wang152449c2015-05-31 16:25:16 -0700445 fs::path realPathToFolder(m_dirPath.toStdString());
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800446 realPathToFolder /= m_sharedFolderName.toStdString();
Zhenkai Zhu872cc592013-03-04 14:20:46 -0800447 realPathToFolder /= filename;
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800448 QString fullPath = realPathToFolder.string().c_str();
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800449 QFileInfo fileInfo(fullPath);
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800450 if (fileInfo.exists()) {
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800451 // This is a hack, we just use some field to store the path
452 m_fileActions[index]->setToolTip(fileInfo.absolutePath());
453 m_fileActions[index]->setEnabled(true);
454 }
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800455 else {
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800456 // after half an hour frustrating test and search around,
457 // I think it's the problem of Qt.
458 // According to the Qt doc, the action cannot be clicked
459 // and the area would be grey, but it didn't happen
460 // User can still trigger the action, and not greyed
461 // added check in SLOT to see if the action is "enalbed"
462 // as a remedy
463 // Give up at least for now
464 m_fileActions[index]->setEnabled(false);
465 // UPDATE, file not fetched yet
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800466 if (action == 0) {
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800467 QFont font;
468 // supposed by change the font, didn't happen
469 font.setWeight(QFont::Light);
470 m_fileActions[index]->setFont(font);
471 m_fileActions[index]->setToolTip(tr("Fetching..."));
472 }
473 // DELETE
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800474 else {
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800475 QFont font;
476 // supposed by change the font, didn't happen
477 font.setStrikeOut(true);
478 m_fileActions[index]->setFont(font);
479 m_fileActions[index]->setToolTip(tr("Deleted..."));
480 }
481 }
482 m_fileActions[index]->setText(fileInfo.fileName());
483 m_fileActions[index]->setVisible(true);
484}
485
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800486void
487ChronoShareGui::changeSettings()
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800488{
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800489 QString oldUsername = m_username;
490 QString oldSharedFolderName = m_sharedFolderName;
491
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800492 if (!editUsername->text().isEmpty())
Zhenkai Zhuf3ea97a2013-01-30 15:20:52 -0800493 m_username = editUsername->text().trimmed();
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800494 else
495 editUsername->setText(m_username);
496
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800497 if (!editSharedFolder->text().isEmpty())
Zhenkai Zhuf3ea97a2013-01-30 15:20:52 -0800498 m_sharedFolderName = editSharedFolder->text().trimmed();
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800499 else
500 editSharedFolder->setText(m_sharedFolderName);
501
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800502 if (m_username.isNull() || m_username == "" || m_sharedFolderName.isNull() ||
503 m_sharedFolderName == "") {
504 openMessageBox("Error", "Username and shared folder name cannot be empty");
505 }
506 else {
507 saveSettings();
508 this->hide();
Zhenkai Zhud5756312013-01-31 13:49:45 -0800509
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800510 if (m_username != oldUsername || oldSharedFolderName != m_sharedFolderName) {
511 startBackend(true); // restart dispatcher/fswatcher
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800512 }
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800513 }
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800514}
515
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800516void
517ChronoShareGui::openFileDialog()
Jared Lindblomb4032e22013-01-17 23:50:51 -0800518{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800519 while (true) {
520 // prompt user for new directory
521 QString tempPath =
522 QFileDialog::getExistingDirectory(this, tr("Choose ChronoShare folder"), m_dirPath,
523 QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
524 if (tempPath.isNull()) {
525 if (m_dirPath.isNull()) {
526 openMessageBox("Notice",
527 "ChronoShare will use [" + QDir::homePath() +
528 "/ChronoShare]. \n\nYou can change it later selecting \"Change Folder\" menu.");
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800529
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800530 m_dirPath = QDir::homePath() + "/ChronoShare";
531 editSharedFolderPath->setText(m_dirPath);
532 break;
533 }
534 else {
535 // user just cancelled, no need to do anything else
536 return;
537 }
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800538 }
Jared Lindblomb4032e22013-01-17 23:50:51 -0800539
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800540 QFileInfo qFileInfo(tempPath);
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800541
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800542 if (!qFileInfo.isDir()) {
543 openMessageBox("Error", "Please select an existing folder or create a new one.");
544 continue;
545 }
546
547 if (m_dirPath == tempPath) {
548 // user selected the same directory, no need to do anything
549 return;
550 }
551
552 m_dirPath = tempPath;
553 editSharedFolderPath->setText(m_dirPath);
554 break;
555 }
556
557 _LOG_DEBUG("Selected path: " << m_dirPath.toStdString());
558 // save settings
559 saveSettings();
560
561 startBackend(true); // restart dispatcher/fswatcher
Jared Lindblomb4032e22013-01-17 23:50:51 -0800562}
563
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800564void
565ChronoShareGui::trayIconClicked(QSystemTrayIcon::ActivationReason reason)
Jared Lindblomb4032e22013-01-17 23:50:51 -0800566{
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800567 // if double clicked, open shared folder
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800568 if (reason == QSystemTrayIcon::DoubleClick) {
569 openSharedFolder();
570 }
Jared Lindblomb4032e22013-01-17 23:50:51 -0800571}
572
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800573void
574ChronoShareGui::viewSettings()
Jared Lindblomb4032e22013-01-17 23:50:51 -0800575{
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800576 //simple for now
577 this->show();
Zhenkai Zhud5756312013-01-31 13:49:45 -0800578 this->raise();
579 this->activateWindow();
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800580}
581
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800582bool
583ChronoShareGui::loadSettings()
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800584{
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800585 bool successful = true;
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800586
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800587 // Load Settings
588 // QSettings settings(m_settingsFilePath, QSettings::NativeFormat);
Alexander Afanasyev1b51bb52017-02-10 19:52:32 -0800589 QSettings settings;
590
591 _LOG_DEBUG("load settings");
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800592
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800593 if (settings.contains("username")) {
594 m_username = settings.value("username", "admin").toString();
595 }
596 else {
597 successful = false;
598 }
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800599
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800600 editUsername->setText(m_username);
601
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800602 if (settings.contains("sharedfoldername")) {
603 m_sharedFolderName = settings.value("sharedfoldername", "shared").toString();
604 }
605 else {
606 successful = false;
607 }
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800608
609 editSharedFolder->setText(m_sharedFolderName);
610
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800611 if (settings.contains("dirPath")) {
612 m_dirPath = settings.value("dirPath", QDir::homePath()).toString();
613 }
614 else {
615 successful = false;
616 }
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800617
618 editSharedFolderPath->setText(m_dirPath);
619
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800620 _LOG_DEBUG("Found configured path: " << (successful ? m_dirPath.toStdString() : std::string("no")));
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800621
622 return successful;
Jared Lindblomb4032e22013-01-17 23:50:51 -0800623}
624
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800625void
626ChronoShareGui::saveSettings()
Jared Lindblomb4032e22013-01-17 23:50:51 -0800627{
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800628 // Save Settings
629 // QSettings settings(m_settingsFilePath, QSettings::NativeFormat);
Alexander Afanasyev1b51bb52017-02-10 19:52:32 -0800630 QSettings settings;
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800631
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800632 settings.setValue("dirPath", m_dirPath);
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800633 settings.setValue("username", m_username);
634 settings.setValue("sharedfoldername", m_sharedFolderName);
Jared Lindblomb4032e22013-01-17 23:50:51 -0800635}
636
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800637void
638ChronoShareGui::closeEvent(QCloseEvent* event)
Jared Lindblomb4032e22013-01-17 23:50:51 -0800639{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800640 _LOG_DEBUG("Close Event");
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800641
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800642 if (m_username.isNull() || m_username == "" || m_sharedFolderName.isNull() ||
643 m_sharedFolderName == "") {
644 openMessageBox("ChronoShare is not active",
Lijing Wang152449c2015-05-31 16:25:16 -0700645 "Username and/or shared folder name are not set.\n\n To activate ChronoShare, "
646 "open Settings menu and configure your username and shared folder name");
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800647 }
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800648
649 this->hide();
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800650 event->ignore(); // don't let the event propagate to the base class
Jared Lindblomb4032e22013-01-17 23:50:51 -0800651}
Jared Lindblomdc845f02013-01-18 17:29:40 -0800652
Alexander Afanasyev1b51bb52017-02-10 19:52:32 -0800653#ifdef AUTOUPDATE
654void
655ChronoShareGui::checkForUpdates()
656{
657 m_sparkle.checkForUpdates();
658}
659#endif // AUTOUPDATE
660
Lijing Wang152449c2015-05-31 16:25:16 -0700661} // namespace chronoshare
662} // namespace ndn
663
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800664#include "chronosharegui.moc"