blob: 687d14ca2337ae484ceb94c131a4486da827cf18 [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)
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -080052#ifdef SPARKLE_SUPPORTED
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080053 , m_autoUpdate(
54 new SparkleAutoUpdate(tr("http://irl.cs.ucla.edu/~zhenkai/chronoshare_dist/chronoshare.xml")))
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -080055#endif
Jared Lindblom06405c42013-01-17 20:48:39 -080056{
Jared Lindblomf4b900e2013-02-07 18:08:47 -080057 setWindowTitle("Settings");
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080058 setMinimumWidth(600);
Jared Lindblomc8ffd592013-01-25 00:00:30 -080059
Lijing Wang152449c2015-05-31 16:25:16 -070060 labelUsername = new QLabel("Username(hint: /<username>)");
Jared Lindblomc8ffd592013-01-25 00:00:30 -080061 labelSharedFolder = new QLabel("Shared Folder Name");
62 labelSharedFolderPath = new QLabel("Shared Folder Path");
Zhenkai Zhuf3ea97a2013-01-30 15:20:52 -080063
Zhenkai Zhud5756312013-01-31 13:49:45 -080064 QRegExp regex("(/[^/]+)+$");
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080065 QValidator* prefixValidator = new QRegExpValidator(regex, this);
Zhenkai Zhuf3ea97a2013-01-30 15:20:52 -080066
Jared Lindblomc8ffd592013-01-25 00:00:30 -080067 editUsername = new QLineEdit();
Zhenkai Zhuf3ea97a2013-01-30 15:20:52 -080068 editUsername->setValidator(prefixValidator);
69
70 QRegExp noPureWhiteSpace("^\\S+.*$");
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080071 QValidator* wsValidator = new QRegExpValidator(noPureWhiteSpace, this);
Jared Lindblomc8ffd592013-01-25 00:00:30 -080072 editSharedFolder = new QLineEdit();
Zhenkai Zhuf3ea97a2013-01-30 15:20:52 -080073 editSharedFolder->setValidator(wsValidator);
74
Jared Lindblomc8ffd592013-01-25 00:00:30 -080075 editSharedFolderPath = new QLineEdit();
76 editSharedFolderPath->setReadOnly(true);
77 QPalette pal = editSharedFolderPath->palette();
78 pal.setColor(QPalette::Active, QPalette::Base, pal.color(QPalette::Disabled, QPalette::Base));
79 editSharedFolderPath->setPalette(pal);
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -080080 button = new QPushButton("Save and apply settings");
Zhenkai Zhu630d0f12013-02-28 17:21:54 -080081
82 QString versionString = QString("Version: ChronoShare v%1").arg(CHRONOSHARE_VERSION);
83 label = new QLabel(versionString, this);
Jared Lindblomc8ffd592013-01-25 00:00:30 -080084
85 connect(button, SIGNAL(clicked()), this, SLOT(changeSettings()));
86
Lijing Wang152449c2015-05-31 16:25:16 -070087 mainLayout = new QVBoxLayout; // vertically
Jared Lindblomc8ffd592013-01-25 00:00:30 -080088 mainLayout->addWidget(labelUsername);
89 mainLayout->addWidget(editUsername);
90 mainLayout->addWidget(labelSharedFolder);
91 mainLayout->addWidget(editSharedFolder);
92 mainLayout->addWidget(labelSharedFolderPath);
93 mainLayout->addWidget(editSharedFolderPath);
94 mainLayout->addWidget(button);
95 mainLayout->addWidget(label);
96 setLayout(mainLayout);
97
Alexander Afanasyev83a53002013-01-24 11:12:01 -080098 // create actions that result from clicking a menu option
Zhenkai Zhu25e13582013-02-27 15:33:01 -080099 createActionsAndMenu();
Jared Lindblomb4032e22013-01-17 23:50:51 -0800100
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800101 // create tray icon
102 createTrayIcon();
Jared Lindblomb4032e22013-01-17 23:50:51 -0800103
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800104 // set icon image
105 setIcon();
Jared Lindblom06405c42013-01-17 20:48:39 -0800106
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800107 // show tray icon
108 m_trayIcon->show();
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -0800109
Zhenkai Zhud5756312013-01-31 13:49:45 -0800110 // load settings
Yukai Tu05126792016-10-11 15:03:30 -0700111 if (!loadSettings() || m_username.isNull() || m_username == "" || m_sharedFolderName.isNull() ||
112 m_sharedFolderName == "" || m_dirPath.isNull() || m_dirPath == "") {
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800113 // prompt user to choose folder
Yukai Tu73287f52017-02-06 15:46:40 -0800114 _LOG_DEBUG("First Time Setup");
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800115 openMessageBox("First Time Setup",
116 "Please enter a username, shared folder name and choose the shared folder path on your local filesystem.");
117 viewSettings();
118 openFileDialog();
119 viewSettings();
120 }
121 else {
Yukai Tu05126792016-10-11 15:03:30 -0700122 startBackend();
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800123 }
Zhenkai Zhud5756312013-01-31 13:49:45 -0800124}
125
Yukai Tu73287f52017-02-06 15:46:40 -0800126ChronoShareGui::ChronoShareGui(QString dirPath, QString username, QString sharedFolderName, QWidget* parent)
127 : QDialog(parent)
128 , m_dirPath(dirPath)
129 , m_username(username)
130 , m_sharedFolderName(sharedFolderName)
131{
132 if (m_username.isNull() || m_username == "" || m_sharedFolderName.isNull() ||
133 m_sharedFolderName == "" || m_dirPath.isNull() || m_dirPath == "") {
134 // prompt user to choose folder
135 BOOST_THROW_EXCEPTION(Error("Some error with init info"));
136 }
137 else {
138 startBackend();
139 }
140}
141
Zhenkai Zhud5756312013-01-31 13:49:45 -0800142void
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800143ChronoShareGui::startBackend(bool restart /*=false*/)
Zhenkai Zhud5756312013-01-31 13:49:45 -0800144{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800145 if (m_username.isNull() || m_username == "" || m_sharedFolderName.isNull() ||
146 m_sharedFolderName == "" || m_dirPath.isNull() || m_dirPath == "") {
147 _LOG_DEBUG("Don't start backend, because settings are in progress or incomplete");
148 return;
149 }
150
151 if (m_watcher != 0 && m_dispatcher != 0) {
152 if (!restart) {
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800153 return;
154 }
155
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800156 _LOG_DEBUG("Restarting Dispatcher and FileWatcher for the new location or new username");
Lijing Wang152449c2015-05-31 16:25:16 -0700157 m_watcher.reset(); // stop filewatching ASAP
158 m_dispatcher.reset(); // stop dispatcher ASAP, but after watcher(to prevent triggering callbacks
159 // on deleted object)
Yukai Tu05126792016-10-11 15:03:30 -0700160 m_ioServiceWork.reset();
Yukai Tu73287f52017-02-06 15:46:40 -0800161 m_ioServiceManager->handle_stop();
Yukai Tu05126792016-10-11 15:03:30 -0700162 m_NetworkThread.join();
Yukai Tu73287f52017-02-06 15:46:40 -0800163 delete m_ioServiceManager;
Zhenkai Zhud5756312013-01-31 13:49:45 -0800164 }
165
Lijing Wang152449c2015-05-31 16:25:16 -0700166 fs::path realPathToFolder(m_dirPath.toStdString());
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800167 realPathToFolder /= m_sharedFolderName.toStdString();
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800168
Yukai Tu729b2892017-02-09 21:15:48 -0800169 _LOG_DEBUG("m_dispatcher username:" << m_username.toStdString()
Lijing Wang152449c2015-05-31 16:25:16 -0700170 << " m_sharedFolderName:" << m_sharedFolderName.toStdString()
Yukai Tu729b2892017-02-09 21:15:48 -0800171 << " realPathToFolder: " << realPathToFolder);
Lijing Wang152449c2015-05-31 16:25:16 -0700172
173 m_ioService.reset(new boost::asio::io_service());
174 m_face.reset(new Face(*m_ioService));
175 m_dispatcher.reset(new Dispatcher(m_username.toStdString(), m_sharedFolderName.toStdString(),
176 realPathToFolder, *m_face));
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800177
178 // Alex: this **must** be here, otherwise m_dirPath will be uninitialized
Lijing Wang152449c2015-05-31 16:25:16 -0700179 m_watcher.reset(new FsWatcher(*m_ioService, realPathToFolder.string().c_str(),
180 bind(&Dispatcher::Did_LocalFile_AddOrModify, m_dispatcher.get(), _1),
181 bind(&Dispatcher::Did_LocalFile_Delete, m_dispatcher.get(), _1)));
Yukai Tu05126792016-10-11 15:03:30 -0700182 try {
Yukai Tu73287f52017-02-06 15:46:40 -0800183 m_ioServiceManager = new IoServiceManager(*m_ioService);
184 m_NetworkThread = std::thread(&IoServiceManager::run, m_ioServiceManager);
Yukai Tu05126792016-10-11 15:03:30 -0700185 }
186 catch (const std::exception& e) {
187 _LOG_ERROR("Start IO service or Face failed");
188 openWarningMessageBox("", "WARNING: Cannot allocate thread for face and io_service!",
189 QString("Starting chronoshare failed"
190 "Exception caused: %1")
191 .arg(e.what()));
192 // stop filewatching ASAP
193 m_watcher.reset();
194 m_dispatcher.reset();
195 return;
196 }
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800197
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800198 if (m_httpServer != 0) {
199 // no need to restart webserver if it already exists
200 return;
201 }
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800202
Zhenkai Zhud9429222013-02-25 22:34:09 -0800203 QFileInfo indexHtmlInfo(":/html/index.html");
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800204 if (indexHtmlInfo.exists()) {
205 try {
Zhenkai Zhu8da33652013-03-04 10:52:03 -0800206 m_httpServer = new http::server::server(HTTP_SERVER_ADDRESS, HTTP_SERVER_PORT, DOC_ROOT);
Lijing Wang152449c2015-05-31 16:25:16 -0700207 m_httpServerThread = std::thread(&http::server::server::run, m_httpServer);
Zhenkai Zhu8da33652013-03-04 10:52:03 -0800208 }
Yukai Tu05126792016-10-11 15:03:30 -0700209 catch (const std::exception& e) {
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800210 _LOG_ERROR("Start http server failed");
Zhenkai Zhu8da33652013-03-04 10:52:03 -0800211 m_httpServer = 0; // just to make sure
Yukai Tu05126792016-10-11 15:03:30 -0700212 openWarningMessageBox("WARNING", "WARNING: Cannot start http server!",
213 QString("Starting http server failed. You will "
214 "not be able to check history from web "
215 "brower. Exception caused: %1")
216 .arg(e.what()));
Zhenkai Zhu8da33652013-03-04 10:52:03 -0800217 }
Zhenkai Zhud9429222013-02-25 22:34:09 -0800218 }
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800219 else {
220 _LOG_ERROR("Http server doc root dir does not exist!");
Zhenkai Zhud9429222013-02-25 22:34:09 -0800221 }
Jared Lindblom06405c42013-01-17 20:48:39 -0800222}
223
224ChronoShareGui::~ChronoShareGui()
225{
Lijing Wang152449c2015-05-31 16:25:16 -0700226 // stop filewatching ASAP
227 m_watcher.reset();
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800228
Lijing Wang152449c2015-05-31 16:25:16 -0700229 // stop dispatcher ASAP, but after watcher to prevent triggering callbacks on the deleted object
230 m_dispatcher.reset();
231
Yukai Tu05126792016-10-11 15:03:30 -0700232 m_ioServiceWork.reset();
Yukai Tu73287f52017-02-06 15:46:40 -0800233 m_ioServiceManager->handle_stop();
Yukai Tu05126792016-10-11 15:03:30 -0700234 m_NetworkThread.join();
Yukai Tu73287f52017-02-06 15:46:40 -0800235 delete m_ioServiceManager;
Yukai Tu05126792016-10-11 15:03:30 -0700236
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800237 if (m_httpServer != 0) {
Zhenkai Zhu8da33652013-03-04 10:52:03 -0800238 m_httpServer->handle_stop();
239 m_httpServerThread.join();
240 delete m_httpServer;
241 }
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -0800242
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800243 // cleanup
244 delete m_trayIcon;
245 delete m_trayIconMenu;
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -0800246#ifdef SPARKLE_SUPPORTED
247 delete m_autoUpdate;
Zhenkai Zhubb170d42013-02-25 13:48:59 -0800248 delete m_checkForUpdates;
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -0800249#endif
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800250 delete m_openFolder;
251 delete m_viewSettings;
252 delete m_changeFolder;
253 delete m_quitProgram;
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800254
255 delete labelUsername;
256 delete labelSharedFolder;
257 delete editUsername;
258 delete editSharedFolder;
259 delete button;
260 delete label;
261 delete mainLayout;
Lijing Wang152449c2015-05-31 16:25:16 -0700262
263 // to avoid `private field 'm_checkForUpdates' is not used` warning/error
264 (void)(m_checkForUpdates);
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800265}
266
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800267void
268ChronoShareGui::openMessageBox(QString title, QString text)
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800269{
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800270 QMessageBox messageBox(this);
271 messageBox.setWindowTitle(title);
272 messageBox.setText(text);
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800273
Alexander Afanasyevcf771142013-03-19 11:15:27 -0700274 messageBox.setIconPixmap(QPixmap(ICON_BIG_FILE));
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800275
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800276 messageBox.exec();
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800277}
278
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800279void
280ChronoShareGui::openMessageBox(QString title, QString text, QString infotext)
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800281{
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800282 QMessageBox messageBox(this);
283 messageBox.setWindowTitle(title);
284 messageBox.setText(text);
285 messageBox.setInformativeText(infotext);
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800286
Alexander Afanasyevcf771142013-03-19 11:15:27 -0700287 messageBox.setIconPixmap(QPixmap(ICON_BIG_FILE));
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800288
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800289 messageBox.exec();
Jared Lindblom06405c42013-01-17 20:48:39 -0800290}
291
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800292void
Yukai Tu05126792016-10-11 15:03:30 -0700293ChronoShareGui::openWarningMessageBox(QString title, QString text, QString infotext)
294{
295 QMessageBox messageBox(this);
296 messageBox.setWindowTitle(title);
297 messageBox.setText(text);
298 messageBox.setInformativeText(infotext);
299
300 messageBox.setIcon(QMessageBox::Warning);
301 messageBox.setStandardButtons(QMessageBox::Ok);
302
303 messageBox.exec();
304}
305
306void
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800307ChronoShareGui::createActionsAndMenu()
Jared Lindblom06405c42013-01-17 20:48:39 -0800308{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800309 _LOG_DEBUG("Create actions");
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800310
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800311 // create the "open folder" action
312 m_openFolder = new QAction(tr("&Open Folder"), this);
313 connect(m_openFolder, SIGNAL(triggered()), this, SLOT(openSharedFolder()));
Jared Lindblom06405c42013-01-17 20:48:39 -0800314
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800315 m_openWeb = new QAction(tr("Open in &Browser"), this);
316 connect(m_openWeb, SIGNAL(triggered()), this, SLOT(openInWebBrowser()));
317
318 m_recentFilesMenu = new QMenu(tr("Recently Changed Files"), this);
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800319 for (int i = 0; i < 5; i++) {
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800320 m_fileActions[i] = new QAction(this);
321 m_fileActions[i]->setVisible(false);
322 connect(m_fileActions[i], SIGNAL(triggered()), this, SLOT(openFile()));
323 m_recentFilesMenu->addAction(m_fileActions[i]);
324 }
325 connect(m_recentFilesMenu, SIGNAL(aboutToShow()), this, SLOT(updateRecentFilesMenu()));
326
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800327 // create the "view settings" action
328 m_viewSettings = new QAction(tr("&View Settings"), this);
329 connect(m_viewSettings, SIGNAL(triggered()), this, SLOT(viewSettings()));
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800330
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800331 // create the "change folder" action
332 m_changeFolder = new QAction(tr("&Change Folder"), this);
333 connect(m_changeFolder, SIGNAL(triggered()), this, SLOT(openFileDialog()));
Jared Lindblomb4032e22013-01-17 23:50:51 -0800334
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -0800335#ifdef SPARKLE_SUPPORTED
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800336 m_checkForUpdates = new QAction(tr("Check For Updates"), this);
337 connect(m_checkForUpdates, SIGNAL(triggered()), this, SLOT(onCheckForUpdates()));
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -0800338#endif
339
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800340 // create the "quit program" action
341 m_quitProgram = new QAction(tr("&Quit"), this);
342 connect(m_quitProgram, SIGNAL(triggered()), qApp, SLOT(quit()));
Jared Lindblom06405c42013-01-17 20:48:39 -0800343}
344
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800345void
346ChronoShareGui::createTrayIcon()
Jared Lindblom06405c42013-01-17 20:48:39 -0800347{
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800348 // create a new icon menu
349 m_trayIconMenu = new QMenu(this);
Jared Lindblom06405c42013-01-17 20:48:39 -0800350
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800351 // add actions to the menu
352 m_trayIconMenu->addAction(m_openFolder);
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800353 m_trayIconMenu->addAction(m_openWeb);
354 m_trayIconMenu->addMenu(m_recentFilesMenu);
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800355 m_trayIconMenu->addSeparator();
356 m_trayIconMenu->addAction(m_viewSettings);
357 m_trayIconMenu->addAction(m_changeFolder);
Alexander Afanasyeve8496a32013-03-03 16:10:43 -0800358
359#ifdef SPARKLE_SUPPORTED
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800360 m_trayIconMenu->addSeparator();
Zhenkai Zhu3857d712013-02-27 22:51:25 -0800361 m_trayIconMenu->addAction(m_checkForUpdates);
Alexander Afanasyeve8496a32013-03-03 16:10:43 -0800362#endif
363
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800364 m_trayIconMenu->addSeparator();
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800365 m_trayIconMenu->addAction(m_quitProgram);
Jared Lindblom06405c42013-01-17 20:48:39 -0800366
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800367 // create new tray icon
368 m_trayIcon = new QSystemTrayIcon(this);
Jared Lindblomb4032e22013-01-17 23:50:51 -0800369
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800370 // associate the menu with the tray icon
371 m_trayIcon->setContextMenu(m_trayIconMenu);
Jared Lindblomb4032e22013-01-17 23:50:51 -0800372
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800373 // handle left click of icon
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800374 connect(m_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this,
375 SLOT(trayIconClicked(QSystemTrayIcon::ActivationReason)));
Jared Lindblom06405c42013-01-17 20:48:39 -0800376}
377
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800378void
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -0800379ChronoShareGui::onCheckForUpdates()
380{
Zhenkai Zhu249fbc72013-02-27 21:50:25 -0800381#ifdef SPARKLE_SUPPORTED
Zhenkai Zhu3857d712013-02-27 22:51:25 -0800382 cout << "+++++++++++ trying to update +++++++ " << endl;
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -0800383 m_autoUpdate->checkForUpdates();
Zhenkai Zhu3857d712013-02-27 22:51:25 -0800384 cout << "+++++++++++ end trying to update +++++++ " << endl;
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -0800385#endif
Zhenkai Zhu249fbc72013-02-27 21:50:25 -0800386}
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -0800387
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800388void
389ChronoShareGui::setIcon()
Jared Lindblom06405c42013-01-17 20:48:39 -0800390{
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800391 // set the icon image
Alexander Afanasyevcf771142013-03-19 11:15:27 -0700392 m_trayIcon->setIcon(QIcon(ICON_TRAY_FILE));
Jared Lindblom06405c42013-01-17 20:48:39 -0800393}
394
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800395void
396ChronoShareGui::openSharedFolder()
Jared Lindblom06405c42013-01-17 20:48:39 -0800397{
Lijing Wang152449c2015-05-31 16:25:16 -0700398 fs::path realPathToFolder(m_dirPath.toStdString());
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800399 realPathToFolder /= m_sharedFolderName.toStdString();
Zhenkai Zhu872cc592013-03-04 14:20:46 -0800400 QString path = QDir::toNativeSeparators(realPathToFolder.string().c_str());
Zhenkai Zhuce04cb42013-02-25 15:29:31 -0800401 QDesktopServices::openUrl(QUrl("file:///" + path));
Jared Lindblom06405c42013-01-17 20:48:39 -0800402}
Jared Lindblomb4032e22013-01-17 23:50:51 -0800403
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800404void
405ChronoShareGui::openInWebBrowser()
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800406{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800407 QUrl url("http://localhost:9001/");
408 url.setFragment("folderHistory&"
409 "user=" +
410 QUrl::toPercentEncoding(m_username) + "&"
411 "folder=" +
412 QUrl::toPercentEncoding(m_sharedFolderName));
Alexander Afanasyevf63a5142013-02-28 02:21:42 -0800413
414 // i give up. there is double encoding and I have no idea how to fight it...
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800415 QDesktopServices::openUrl(url);
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800416}
417
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800418void
419ChronoShareGui::openFile()
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800420{
421 // figure out who sent the signal
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800422 QAction* pAction = qobject_cast<QAction*>(sender());
423 if (pAction->isEnabled()) {
424// we stored full path of the file in this toolTip field
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800425#ifdef Q_WS_MAC
426 // we do some hack so we could show the file in Finder highlighted
427 QStringList args;
428 args << "-e";
429 args << "tell application \"Finder\"";
430 args << "-e";
431 args << "activate";
432 args << "-e";
433 args << "select POSIX file \"" + pAction->toolTip() + "/" + pAction->text() + "\"";
434 args << "-e";
435 args << "end tell";
436 QProcess::startDetached("osascript", args);
437#else
Lijing Wang152449c2015-05-31 16:25:16 -0700438 // too bad qt couldn't do highlighting for Linux(or Mac)
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800439 QDesktopServices::openUrl(QUrl("file:///" + pAction->toolTip()));
440#endif
441 }
442}
443
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800444void
445ChronoShareGui::updateRecentFilesMenu()
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800446{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800447 for (int i = 0; i < 5; i++) {
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800448 m_fileActions[i]->setVisible(false);
449 }
Lijing Wang152449c2015-05-31 16:25:16 -0700450 m_dispatcher->LookupRecentFileActions(std::bind(&ChronoShareGui::checkFileAction, this, _1, _2, _3),
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800451 5);
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800452}
453
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800454void
455ChronoShareGui::checkFileAction(const std::string& filename, int action, int index)
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800456{
Lijing Wang152449c2015-05-31 16:25:16 -0700457 fs::path realPathToFolder(m_dirPath.toStdString());
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800458 realPathToFolder /= m_sharedFolderName.toStdString();
Zhenkai Zhu872cc592013-03-04 14:20:46 -0800459 realPathToFolder /= filename;
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800460 QString fullPath = realPathToFolder.string().c_str();
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800461 QFileInfo fileInfo(fullPath);
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800462 if (fileInfo.exists()) {
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800463 // This is a hack, we just use some field to store the path
464 m_fileActions[index]->setToolTip(fileInfo.absolutePath());
465 m_fileActions[index]->setEnabled(true);
466 }
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800467 else {
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800468 // after half an hour frustrating test and search around,
469 // I think it's the problem of Qt.
470 // According to the Qt doc, the action cannot be clicked
471 // and the area would be grey, but it didn't happen
472 // User can still trigger the action, and not greyed
473 // added check in SLOT to see if the action is "enalbed"
474 // as a remedy
475 // Give up at least for now
476 m_fileActions[index]->setEnabled(false);
477 // UPDATE, file not fetched yet
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800478 if (action == 0) {
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800479 QFont font;
480 // supposed by change the font, didn't happen
481 font.setWeight(QFont::Light);
482 m_fileActions[index]->setFont(font);
483 m_fileActions[index]->setToolTip(tr("Fetching..."));
484 }
485 // DELETE
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800486 else {
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800487 QFont font;
488 // supposed by change the font, didn't happen
489 font.setStrikeOut(true);
490 m_fileActions[index]->setFont(font);
491 m_fileActions[index]->setToolTip(tr("Deleted..."));
492 }
493 }
494 m_fileActions[index]->setText(fileInfo.fileName());
495 m_fileActions[index]->setVisible(true);
496}
497
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800498void
499ChronoShareGui::changeSettings()
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800500{
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800501 QString oldUsername = m_username;
502 QString oldSharedFolderName = m_sharedFolderName;
503
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800504 if (!editUsername->text().isEmpty())
Zhenkai Zhuf3ea97a2013-01-30 15:20:52 -0800505 m_username = editUsername->text().trimmed();
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800506 else
507 editUsername->setText(m_username);
508
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800509 if (!editSharedFolder->text().isEmpty())
Zhenkai Zhuf3ea97a2013-01-30 15:20:52 -0800510 m_sharedFolderName = editSharedFolder->text().trimmed();
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800511 else
512 editSharedFolder->setText(m_sharedFolderName);
513
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800514 if (m_username.isNull() || m_username == "" || m_sharedFolderName.isNull() ||
515 m_sharedFolderName == "") {
516 openMessageBox("Error", "Username and shared folder name cannot be empty");
517 }
518 else {
519 saveSettings();
520 this->hide();
Zhenkai Zhud5756312013-01-31 13:49:45 -0800521
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800522 if (m_username != oldUsername || oldSharedFolderName != m_sharedFolderName) {
523 startBackend(true); // restart dispatcher/fswatcher
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800524 }
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800525 }
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800526}
527
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800528void
529ChronoShareGui::openFileDialog()
Jared Lindblomb4032e22013-01-17 23:50:51 -0800530{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800531 while (true) {
532 // prompt user for new directory
533 QString tempPath =
534 QFileDialog::getExistingDirectory(this, tr("Choose ChronoShare folder"), m_dirPath,
535 QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
536 if (tempPath.isNull()) {
537 if (m_dirPath.isNull()) {
538 openMessageBox("Notice",
539 "ChronoShare will use [" + QDir::homePath() +
540 "/ChronoShare]. \n\nYou can change it later selecting \"Change Folder\" menu.");
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800541
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800542 m_dirPath = QDir::homePath() + "/ChronoShare";
543 editSharedFolderPath->setText(m_dirPath);
544 break;
545 }
546 else {
547 // user just cancelled, no need to do anything else
548 return;
549 }
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800550 }
Jared Lindblomb4032e22013-01-17 23:50:51 -0800551
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800552 QFileInfo qFileInfo(tempPath);
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800553
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800554 if (!qFileInfo.isDir()) {
555 openMessageBox("Error", "Please select an existing folder or create a new one.");
556 continue;
557 }
558
559 if (m_dirPath == tempPath) {
560 // user selected the same directory, no need to do anything
561 return;
562 }
563
564 m_dirPath = tempPath;
565 editSharedFolderPath->setText(m_dirPath);
566 break;
567 }
568
569 _LOG_DEBUG("Selected path: " << m_dirPath.toStdString());
570 // save settings
571 saveSettings();
572
573 startBackend(true); // restart dispatcher/fswatcher
Jared Lindblomb4032e22013-01-17 23:50:51 -0800574}
575
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800576void
577ChronoShareGui::trayIconClicked(QSystemTrayIcon::ActivationReason reason)
Jared Lindblomb4032e22013-01-17 23:50:51 -0800578{
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800579 // if double clicked, open shared folder
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800580 if (reason == QSystemTrayIcon::DoubleClick) {
581 openSharedFolder();
582 }
Jared Lindblomb4032e22013-01-17 23:50:51 -0800583}
584
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800585void
586ChronoShareGui::viewSettings()
Jared Lindblomb4032e22013-01-17 23:50:51 -0800587{
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800588 //simple for now
589 this->show();
Zhenkai Zhud5756312013-01-31 13:49:45 -0800590 this->raise();
591 this->activateWindow();
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800592}
593
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800594bool
595ChronoShareGui::loadSettings()
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800596{
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800597 bool successful = true;
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800598
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800599 // Load Settings
600 // QSettings settings(m_settingsFilePath, QSettings::NativeFormat);
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800601 QSettings settings(QSettings::NativeFormat, QSettings::UserScope, "irl.cs.ucla.edu", "ChronoShare");
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800602
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800603 if (settings.contains("username")) {
604 m_username = settings.value("username", "admin").toString();
605 }
606 else {
607 successful = false;
608 }
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800609
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800610 editUsername->setText(m_username);
611
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800612 if (settings.contains("sharedfoldername")) {
613 m_sharedFolderName = settings.value("sharedfoldername", "shared").toString();
614 }
615 else {
616 successful = false;
617 }
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800618
619 editSharedFolder->setText(m_sharedFolderName);
620
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800621 if (settings.contains("dirPath")) {
622 m_dirPath = settings.value("dirPath", QDir::homePath()).toString();
623 }
624 else {
625 successful = false;
626 }
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800627
628 editSharedFolderPath->setText(m_dirPath);
629
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800630 _LOG_DEBUG("Found configured path: " << (successful ? m_dirPath.toStdString() : std::string("no")));
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800631
632 return successful;
Jared Lindblomb4032e22013-01-17 23:50:51 -0800633}
634
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800635void
636ChronoShareGui::saveSettings()
Jared Lindblomb4032e22013-01-17 23:50:51 -0800637{
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800638 // Save Settings
639 // QSettings settings(m_settingsFilePath, QSettings::NativeFormat);
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800640 QSettings settings(QSettings::NativeFormat, QSettings::UserScope, "irl.cs.ucla.edu", "ChronoShare");
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800641
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800642 settings.setValue("dirPath", m_dirPath);
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800643 settings.setValue("username", m_username);
644 settings.setValue("sharedfoldername", m_sharedFolderName);
Jared Lindblomb4032e22013-01-17 23:50:51 -0800645}
646
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800647void
648ChronoShareGui::closeEvent(QCloseEvent* event)
Jared Lindblomb4032e22013-01-17 23:50:51 -0800649{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800650 _LOG_DEBUG("Close Event");
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800651
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800652 if (m_username.isNull() || m_username == "" || m_sharedFolderName.isNull() ||
653 m_sharedFolderName == "") {
654 openMessageBox("ChronoShare is not active",
Lijing Wang152449c2015-05-31 16:25:16 -0700655 "Username and/or shared folder name are not set.\n\n To activate ChronoShare, "
656 "open Settings menu and configure your username and shared folder name");
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800657 }
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800658
659 this->hide();
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800660 event->ignore(); // don't let the event propagate to the base class
Jared Lindblomb4032e22013-01-17 23:50:51 -0800661}
Jared Lindblomdc845f02013-01-18 17:29:40 -0800662
Lijing Wang152449c2015-05-31 16:25:16 -0700663} // namespace chronoshare
664} // namespace ndn
665
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800666#include "chronosharegui.moc"