blob: ccc0c6ecd9f5100512470fbd74be8f2a35fa3135 [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
Lijing Wang152449c2015-05-31 16:25:16 -070029#include <thread>
Alexander Afanasyev9ca444e2013-01-25 16:29:35 -080030
Lijing Wang152449c2015-05-31 16:25:16 -070031namespace ndn {
32namespace chronoshare {
Jared Lindblom06405c42013-01-17 20:48:39 -080033
Lijing Wang152449c2015-05-31 16:25:16 -070034namespace fs = boost::filesystem;
35
36static const std::string HTTP_SERVER_ADDRESS = "localhost";
37static const std::string HTTP_SERVER_PORT = "9001";
Zhenkai Zhu6b53f932013-03-02 20:46:02 -080038#ifdef _DEBUG
Lijing Wang152449c2015-05-31 16:25:16 -070039static const std::string DOC_ROOT = "gui/html";
Zhenkai Zhu6b53f932013-03-02 20:46:02 -080040#else
Lijing Wang152449c2015-05-31 16:25:16 -070041static const std::string DOC_ROOT = ":/html";
Zhenkai Zhu6b53f932013-03-02 20:46:02 -080042#endif
Alexander Afanasyevcf771142013-03-19 11:15:27 -070043static const QString ICON_BIG_FILE(":/images/chronoshare-big.png");
44static const QString ICON_TRAY_FILE(":/images/" TRAY_ICON);
Zhenkai Zhud9429222013-02-25 22:34:09 -080045
Alexander Afanasyev1cf5c432017-01-13 23:22:15 -080046_LOG_INIT(Gui);
Alexander Afanasyev83a53002013-01-24 11:12:01 -080047
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080048ChronoShareGui::ChronoShareGui(QWidget* parent)
Jared Lindblomc8ffd592013-01-25 00:00:30 -080049 : QDialog(parent)
Zhenkai Zhud9429222013-02-25 22:34:09 -080050 , m_httpServer(0)
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -080051#ifdef SPARKLE_SUPPORTED
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080052 , m_autoUpdate(
53 new SparkleAutoUpdate(tr("http://irl.cs.ucla.edu/~zhenkai/chronoshare_dist/chronoshare.xml")))
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
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800110 if (!loadSettings()) {
111 // prompt user to choose folder
112 openMessageBox("First Time Setup",
113 "Please enter a username, shared folder name and choose the shared folder path on your local filesystem.");
114 viewSettings();
115 openFileDialog();
116 viewSettings();
117 }
118 else {
119 if (m_username.isNull() || m_username == "" || m_sharedFolderName.isNull() ||
120 m_sharedFolderName == "") {
121 openMessageBox("First Time Setup",
122 "To activate ChronoShare, please configure your username and shared folder name.");
Zhenkai Zhud5756312013-01-31 13:49:45 -0800123 viewSettings();
124 }
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800125 else {
126 startBackend();
Zhenkai Zhud5756312013-01-31 13:49:45 -0800127 }
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800128 }
Zhenkai Zhud5756312013-01-31 13:49:45 -0800129}
130
131void
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800132ChronoShareGui::startBackend(bool restart /*=false*/)
Zhenkai Zhud5756312013-01-31 13:49:45 -0800133{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800134 if (m_username.isNull() || m_username == "" || m_sharedFolderName.isNull() ||
135 m_sharedFolderName == "" || m_dirPath.isNull() || m_dirPath == "") {
136 _LOG_DEBUG("Don't start backend, because settings are in progress or incomplete");
137 return;
138 }
139
140 if (m_watcher != 0 && m_dispatcher != 0) {
141 if (!restart) {
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800142 return;
143 }
144
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800145 _LOG_DEBUG("Restarting Dispatcher and FileWatcher for the new location or new username");
Lijing Wang152449c2015-05-31 16:25:16 -0700146 m_watcher.reset(); // stop filewatching ASAP
147 m_dispatcher.reset(); // stop dispatcher ASAP, but after watcher(to prevent triggering callbacks
148 // on deleted object)
Zhenkai Zhud5756312013-01-31 13:49:45 -0800149 }
150
Lijing Wang152449c2015-05-31 16:25:16 -0700151 fs::path realPathToFolder(m_dirPath.toStdString());
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800152 realPathToFolder /= m_sharedFolderName.toStdString();
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800153
Lijing Wang152449c2015-05-31 16:25:16 -0700154 std::cout << "m_dispatcher username:" << m_username.toStdString()
155 << " m_sharedFolderName:" << m_sharedFolderName.toStdString()
156 << " realPathToFolder: " << realPathToFolder << std::endl;
157
158 m_ioService.reset(new boost::asio::io_service());
159 m_face.reset(new Face(*m_ioService));
160 m_dispatcher.reset(new Dispatcher(m_username.toStdString(), m_sharedFolderName.toStdString(),
161 realPathToFolder, *m_face));
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800162
163 // Alex: this **must** be here, otherwise m_dirPath will be uninitialized
Lijing Wang152449c2015-05-31 16:25:16 -0700164 m_watcher.reset(new FsWatcher(*m_ioService, realPathToFolder.string().c_str(),
165 bind(&Dispatcher::Did_LocalFile_AddOrModify, m_dispatcher.get(), _1),
166 bind(&Dispatcher::Did_LocalFile_Delete, m_dispatcher.get(), _1)));
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800167
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800168 if (m_httpServer != 0) {
169 // no need to restart webserver if it already exists
170 return;
171 }
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800172
Zhenkai Zhud9429222013-02-25 22:34:09 -0800173 QFileInfo indexHtmlInfo(":/html/index.html");
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800174 if (indexHtmlInfo.exists()) {
175 try {
Zhenkai Zhu8da33652013-03-04 10:52:03 -0800176 m_httpServer = new http::server::server(HTTP_SERVER_ADDRESS, HTTP_SERVER_PORT, DOC_ROOT);
Lijing Wang152449c2015-05-31 16:25:16 -0700177 m_httpServerThread = std::thread(&http::server::server::run, m_httpServer);
Zhenkai Zhu8da33652013-03-04 10:52:03 -0800178 }
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800179 catch (std::exception& e) {
180 _LOG_ERROR("Start http server failed");
Zhenkai Zhu8da33652013-03-04 10:52:03 -0800181 m_httpServer = 0; // just to make sure
182 QMessageBox msgBox;
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800183 msgBox.setText("WARNING: Cannot start http server!");
184 msgBox.setIcon(QMessageBox::Warning);
185 msgBox.setInformativeText(
Lijing Wang152449c2015-05-31 16:25:16 -0700186 QString("Starting http server failed. You will not be able to check history from web "
187 "brower. Exception caused: %1")
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800188 .arg(e.what()));
Zhenkai Zhu8da33652013-03-04 10:52:03 -0800189 msgBox.setStandardButtons(QMessageBox::Ok);
190 msgBox.exec();
191 }
Zhenkai Zhud9429222013-02-25 22:34:09 -0800192 }
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800193 else {
194 _LOG_ERROR("Http server doc root dir does not exist!");
Zhenkai Zhud9429222013-02-25 22:34:09 -0800195 }
Jared Lindblom06405c42013-01-17 20:48:39 -0800196}
197
198ChronoShareGui::~ChronoShareGui()
199{
Lijing Wang152449c2015-05-31 16:25:16 -0700200 // stop filewatching ASAP
201 m_watcher.reset();
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800202
Lijing Wang152449c2015-05-31 16:25:16 -0700203 // stop dispatcher ASAP, but after watcher to prevent triggering callbacks on the deleted object
204 m_dispatcher.reset();
205
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800206 if (m_httpServer != 0) {
Zhenkai Zhu8da33652013-03-04 10:52:03 -0800207 m_httpServer->handle_stop();
208 m_httpServerThread.join();
209 delete m_httpServer;
210 }
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -0800211
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800212 // cleanup
213 delete m_trayIcon;
214 delete m_trayIconMenu;
Lijing Wang152449c2015-05-31 16:25:16 -0700215#ifdef D_ADHOC_SUPPORTED
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800216 delete m_wifiAction;
217#endif
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -0800218#ifdef SPARKLE_SUPPORTED
219 delete m_autoUpdate;
Zhenkai Zhubb170d42013-02-25 13:48:59 -0800220 delete m_checkForUpdates;
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -0800221#endif
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800222 delete m_openFolder;
223 delete m_viewSettings;
224 delete m_changeFolder;
225 delete m_quitProgram;
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800226
227 delete labelUsername;
228 delete labelSharedFolder;
229 delete editUsername;
230 delete editSharedFolder;
231 delete button;
232 delete label;
233 delete mainLayout;
Lijing Wang152449c2015-05-31 16:25:16 -0700234
235 // to avoid `private field 'm_checkForUpdates' is not used` warning/error
236 (void)(m_checkForUpdates);
237 (void)(m_wifiAction);
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800238}
239
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800240void
241ChronoShareGui::openMessageBox(QString title, QString text)
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800242{
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800243 QMessageBox messageBox(this);
244 messageBox.setWindowTitle(title);
245 messageBox.setText(text);
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800246
Alexander Afanasyevcf771142013-03-19 11:15:27 -0700247 messageBox.setIconPixmap(QPixmap(ICON_BIG_FILE));
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800248
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800249 messageBox.exec();
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800250}
251
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800252void
253ChronoShareGui::openMessageBox(QString title, QString text, QString infotext)
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800254{
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800255 QMessageBox messageBox(this);
256 messageBox.setWindowTitle(title);
257 messageBox.setText(text);
258 messageBox.setInformativeText(infotext);
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800259
Alexander Afanasyevcf771142013-03-19 11:15:27 -0700260 messageBox.setIconPixmap(QPixmap(ICON_BIG_FILE));
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800261
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800262 messageBox.exec();
Jared Lindblom06405c42013-01-17 20:48:39 -0800263}
264
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800265void
266ChronoShareGui::createActionsAndMenu()
Jared Lindblom06405c42013-01-17 20:48:39 -0800267{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800268 _LOG_DEBUG("Create actions");
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800269
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800270 // create the "open folder" action
271 m_openFolder = new QAction(tr("&Open Folder"), this);
272 connect(m_openFolder, SIGNAL(triggered()), this, SLOT(openSharedFolder()));
Jared Lindblom06405c42013-01-17 20:48:39 -0800273
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800274 m_openWeb = new QAction(tr("Open in &Browser"), this);
275 connect(m_openWeb, SIGNAL(triggered()), this, SLOT(openInWebBrowser()));
276
277 m_recentFilesMenu = new QMenu(tr("Recently Changed Files"), this);
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800278 for (int i = 0; i < 5; i++) {
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800279 m_fileActions[i] = new QAction(this);
280 m_fileActions[i]->setVisible(false);
281 connect(m_fileActions[i], SIGNAL(triggered()), this, SLOT(openFile()));
282 m_recentFilesMenu->addAction(m_fileActions[i]);
283 }
284 connect(m_recentFilesMenu, SIGNAL(aboutToShow()), this, SLOT(updateRecentFilesMenu()));
285
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800286 // create the "view settings" action
287 m_viewSettings = new QAction(tr("&View Settings"), this);
288 connect(m_viewSettings, SIGNAL(triggered()), this, SLOT(viewSettings()));
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800289
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800290 // create the "change folder" action
291 m_changeFolder = new QAction(tr("&Change Folder"), this);
292 connect(m_changeFolder, SIGNAL(triggered()), this, SLOT(openFileDialog()));
Jared Lindblomb4032e22013-01-17 23:50:51 -0800293
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -0800294#ifdef SPARKLE_SUPPORTED
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800295 m_checkForUpdates = new QAction(tr("Check For Updates"), this);
296 connect(m_checkForUpdates, SIGNAL(triggered()), this, SLOT(onCheckForUpdates()));
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -0800297#endif
298
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800299 // create the "quit program" action
300 m_quitProgram = new QAction(tr("&Quit"), this);
301 connect(m_quitProgram, SIGNAL(triggered()), qApp, SLOT(quit()));
Jared Lindblom06405c42013-01-17 20:48:39 -0800302}
303
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800304void
305ChronoShareGui::createTrayIcon()
Jared Lindblom06405c42013-01-17 20:48:39 -0800306{
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800307 // create a new icon menu
308 m_trayIconMenu = new QMenu(this);
Jared Lindblom06405c42013-01-17 20:48:39 -0800309
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800310 // add actions to the menu
311 m_trayIconMenu->addAction(m_openFolder);
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800312 m_trayIconMenu->addAction(m_openWeb);
313 m_trayIconMenu->addMenu(m_recentFilesMenu);
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800314 m_trayIconMenu->addSeparator();
315 m_trayIconMenu->addAction(m_viewSettings);
316 m_trayIconMenu->addAction(m_changeFolder);
Alexander Afanasyeve8496a32013-03-03 16:10:43 -0800317
318#ifdef SPARKLE_SUPPORTED
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800319 m_trayIconMenu->addSeparator();
Zhenkai Zhu3857d712013-02-27 22:51:25 -0800320 m_trayIconMenu->addAction(m_checkForUpdates);
Alexander Afanasyeve8496a32013-03-03 16:10:43 -0800321#endif
322
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800323 m_trayIconMenu->addSeparator();
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800324 m_trayIconMenu->addAction(m_quitProgram);
Jared Lindblom06405c42013-01-17 20:48:39 -0800325
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800326 // create new tray icon
327 m_trayIcon = new QSystemTrayIcon(this);
Jared Lindblomb4032e22013-01-17 23:50:51 -0800328
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800329 // associate the menu with the tray icon
330 m_trayIcon->setContextMenu(m_trayIconMenu);
Jared Lindblomb4032e22013-01-17 23:50:51 -0800331
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800332 // handle left click of icon
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800333 connect(m_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this,
334 SLOT(trayIconClicked(QSystemTrayIcon::ActivationReason)));
Jared Lindblom06405c42013-01-17 20:48:39 -0800335}
336
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800337void
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -0800338ChronoShareGui::onCheckForUpdates()
339{
Zhenkai Zhu249fbc72013-02-27 21:50:25 -0800340#ifdef SPARKLE_SUPPORTED
Zhenkai Zhu3857d712013-02-27 22:51:25 -0800341 cout << "+++++++++++ trying to update +++++++ " << endl;
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -0800342 m_autoUpdate->checkForUpdates();
Zhenkai Zhu3857d712013-02-27 22:51:25 -0800343 cout << "+++++++++++ end trying to update +++++++ " << endl;
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -0800344#endif
Zhenkai Zhu249fbc72013-02-27 21:50:25 -0800345}
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -0800346
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800347void
348ChronoShareGui::setIcon()
Jared Lindblom06405c42013-01-17 20:48:39 -0800349{
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800350 // set the icon image
Alexander Afanasyevcf771142013-03-19 11:15:27 -0700351 m_trayIcon->setIcon(QIcon(ICON_TRAY_FILE));
Jared Lindblom06405c42013-01-17 20:48:39 -0800352}
353
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800354void
355ChronoShareGui::openSharedFolder()
Jared Lindblom06405c42013-01-17 20:48:39 -0800356{
Lijing Wang152449c2015-05-31 16:25:16 -0700357 fs::path realPathToFolder(m_dirPath.toStdString());
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800358 realPathToFolder /= m_sharedFolderName.toStdString();
Zhenkai Zhu872cc592013-03-04 14:20:46 -0800359 QString path = QDir::toNativeSeparators(realPathToFolder.string().c_str());
Zhenkai Zhuce04cb42013-02-25 15:29:31 -0800360 QDesktopServices::openUrl(QUrl("file:///" + path));
Jared Lindblom06405c42013-01-17 20:48:39 -0800361}
Jared Lindblomb4032e22013-01-17 23:50:51 -0800362
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800363void
364ChronoShareGui::openInWebBrowser()
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800365{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800366 QUrl url("http://localhost:9001/");
367 url.setFragment("folderHistory&"
368 "user=" +
369 QUrl::toPercentEncoding(m_username) + "&"
370 "folder=" +
371 QUrl::toPercentEncoding(m_sharedFolderName));
Alexander Afanasyevf63a5142013-02-28 02:21:42 -0800372
373 // i give up. there is double encoding and I have no idea how to fight it...
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800374 QDesktopServices::openUrl(url);
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800375}
376
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800377void
378ChronoShareGui::openFile()
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800379{
380 // figure out who sent the signal
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800381 QAction* pAction = qobject_cast<QAction*>(sender());
382 if (pAction->isEnabled()) {
383// we stored full path of the file in this toolTip field
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800384#ifdef Q_WS_MAC
385 // we do some hack so we could show the file in Finder highlighted
386 QStringList args;
387 args << "-e";
388 args << "tell application \"Finder\"";
389 args << "-e";
390 args << "activate";
391 args << "-e";
392 args << "select POSIX file \"" + pAction->toolTip() + "/" + pAction->text() + "\"";
393 args << "-e";
394 args << "end tell";
395 QProcess::startDetached("osascript", args);
396#else
Lijing Wang152449c2015-05-31 16:25:16 -0700397 // too bad qt couldn't do highlighting for Linux(or Mac)
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800398 QDesktopServices::openUrl(QUrl("file:///" + pAction->toolTip()));
399#endif
400 }
401}
402
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800403void
404ChronoShareGui::updateRecentFilesMenu()
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800405{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800406 for (int i = 0; i < 5; i++) {
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800407 m_fileActions[i]->setVisible(false);
408 }
Lijing Wang152449c2015-05-31 16:25:16 -0700409 m_dispatcher->LookupRecentFileActions(std::bind(&ChronoShareGui::checkFileAction, this, _1, _2, _3),
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800410 5);
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800411}
412
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800413void
414ChronoShareGui::checkFileAction(const std::string& filename, int action, int index)
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800415{
Lijing Wang152449c2015-05-31 16:25:16 -0700416 fs::path realPathToFolder(m_dirPath.toStdString());
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800417 realPathToFolder /= m_sharedFolderName.toStdString();
Zhenkai Zhu872cc592013-03-04 14:20:46 -0800418 realPathToFolder /= filename;
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800419 QString fullPath = realPathToFolder.string().c_str();
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800420 QFileInfo fileInfo(fullPath);
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800421 if (fileInfo.exists()) {
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800422 // This is a hack, we just use some field to store the path
423 m_fileActions[index]->setToolTip(fileInfo.absolutePath());
424 m_fileActions[index]->setEnabled(true);
425 }
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800426 else {
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800427 // after half an hour frustrating test and search around,
428 // I think it's the problem of Qt.
429 // According to the Qt doc, the action cannot be clicked
430 // and the area would be grey, but it didn't happen
431 // User can still trigger the action, and not greyed
432 // added check in SLOT to see if the action is "enalbed"
433 // as a remedy
434 // Give up at least for now
435 m_fileActions[index]->setEnabled(false);
436 // UPDATE, file not fetched yet
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800437 if (action == 0) {
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800438 QFont font;
439 // supposed by change the font, didn't happen
440 font.setWeight(QFont::Light);
441 m_fileActions[index]->setFont(font);
442 m_fileActions[index]->setToolTip(tr("Fetching..."));
443 }
444 // DELETE
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800445 else {
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800446 QFont font;
447 // supposed by change the font, didn't happen
448 font.setStrikeOut(true);
449 m_fileActions[index]->setFont(font);
450 m_fileActions[index]->setToolTip(tr("Deleted..."));
451 }
452 }
453 m_fileActions[index]->setText(fileInfo.fileName());
454 m_fileActions[index]->setVisible(true);
455}
456
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800457void
458ChronoShareGui::changeSettings()
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800459{
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800460 QString oldUsername = m_username;
461 QString oldSharedFolderName = m_sharedFolderName;
462
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800463 if (!editUsername->text().isEmpty())
Zhenkai Zhuf3ea97a2013-01-30 15:20:52 -0800464 m_username = editUsername->text().trimmed();
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800465 else
466 editUsername->setText(m_username);
467
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800468 if (!editSharedFolder->text().isEmpty())
Zhenkai Zhuf3ea97a2013-01-30 15:20:52 -0800469 m_sharedFolderName = editSharedFolder->text().trimmed();
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800470 else
471 editSharedFolder->setText(m_sharedFolderName);
472
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800473 if (m_username.isNull() || m_username == "" || m_sharedFolderName.isNull() ||
474 m_sharedFolderName == "") {
475 openMessageBox("Error", "Username and shared folder name cannot be empty");
476 }
477 else {
478 saveSettings();
479 this->hide();
Zhenkai Zhud5756312013-01-31 13:49:45 -0800480
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800481 if (m_username != oldUsername || oldSharedFolderName != m_sharedFolderName) {
482 startBackend(true); // restart dispatcher/fswatcher
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800483 }
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800484 }
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800485}
486
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800487void
488ChronoShareGui::openFileDialog()
Jared Lindblomb4032e22013-01-17 23:50:51 -0800489{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800490 while (true) {
491 // prompt user for new directory
492 QString tempPath =
493 QFileDialog::getExistingDirectory(this, tr("Choose ChronoShare folder"), m_dirPath,
494 QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
495 if (tempPath.isNull()) {
496 if (m_dirPath.isNull()) {
497 openMessageBox("Notice",
498 "ChronoShare will use [" + QDir::homePath() +
499 "/ChronoShare]. \n\nYou can change it later selecting \"Change Folder\" menu.");
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800500
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800501 m_dirPath = QDir::homePath() + "/ChronoShare";
502 editSharedFolderPath->setText(m_dirPath);
503 break;
504 }
505 else {
506 // user just cancelled, no need to do anything else
507 return;
508 }
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800509 }
Jared Lindblomb4032e22013-01-17 23:50:51 -0800510
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800511 QFileInfo qFileInfo(tempPath);
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800512
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800513 if (!qFileInfo.isDir()) {
514 openMessageBox("Error", "Please select an existing folder or create a new one.");
515 continue;
516 }
517
518 if (m_dirPath == tempPath) {
519 // user selected the same directory, no need to do anything
520 return;
521 }
522
523 m_dirPath = tempPath;
524 editSharedFolderPath->setText(m_dirPath);
525 break;
526 }
527
528 _LOG_DEBUG("Selected path: " << m_dirPath.toStdString());
529 // save settings
530 saveSettings();
531
532 startBackend(true); // restart dispatcher/fswatcher
Jared Lindblomb4032e22013-01-17 23:50:51 -0800533}
534
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800535void
536ChronoShareGui::trayIconClicked(QSystemTrayIcon::ActivationReason reason)
Jared Lindblomb4032e22013-01-17 23:50:51 -0800537{
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800538 // if double clicked, open shared folder
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800539 if (reason == QSystemTrayIcon::DoubleClick) {
540 openSharedFolder();
541 }
Jared Lindblomb4032e22013-01-17 23:50:51 -0800542}
543
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800544void
545ChronoShareGui::viewSettings()
Jared Lindblomb4032e22013-01-17 23:50:51 -0800546{
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800547 //simple for now
548 this->show();
Zhenkai Zhud5756312013-01-31 13:49:45 -0800549 this->raise();
550 this->activateWindow();
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800551}
552
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800553bool
554ChronoShareGui::loadSettings()
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800555{
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800556 bool successful = true;
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800557
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800558 // Load Settings
559 // QSettings settings(m_settingsFilePath, QSettings::NativeFormat);
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800560 QSettings settings(QSettings::NativeFormat, QSettings::UserScope, "irl.cs.ucla.edu", "ChronoShare");
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800561
Lijing Wang152449c2015-05-31 16:25:16 -0700562 // _LOG_DEBUG(lexical_cast<string>(settings.allKeys()));
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800563
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800564 if (settings.contains("username")) {
565 m_username = settings.value("username", "admin").toString();
566 }
567 else {
568 successful = false;
569 }
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800570
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800571 editUsername->setText(m_username);
572
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800573 if (settings.contains("sharedfoldername")) {
574 m_sharedFolderName = settings.value("sharedfoldername", "shared").toString();
575 }
576 else {
577 successful = false;
578 }
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800579
580 editSharedFolder->setText(m_sharedFolderName);
581
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800582 if (settings.contains("dirPath")) {
583 m_dirPath = settings.value("dirPath", QDir::homePath()).toString();
584 }
585 else {
586 successful = false;
587 }
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800588
589 editSharedFolderPath->setText(m_dirPath);
590
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800591 _LOG_DEBUG("Found configured path: " << (successful ? m_dirPath.toStdString() : std::string("no")));
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800592
593 return successful;
Jared Lindblomb4032e22013-01-17 23:50:51 -0800594}
595
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800596void
597ChronoShareGui::saveSettings()
Jared Lindblomb4032e22013-01-17 23:50:51 -0800598{
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800599 // Save 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 Lindblomc8ffd592013-01-25 00:00:30 -0800602
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800603 settings.setValue("dirPath", m_dirPath);
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800604 settings.setValue("username", m_username);
605 settings.setValue("sharedfoldername", m_sharedFolderName);
Jared Lindblomb4032e22013-01-17 23:50:51 -0800606}
607
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800608void
609ChronoShareGui::closeEvent(QCloseEvent* event)
Jared Lindblomb4032e22013-01-17 23:50:51 -0800610{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800611 _LOG_DEBUG("Close Event");
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800612
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800613 if (m_username.isNull() || m_username == "" || m_sharedFolderName.isNull() ||
614 m_sharedFolderName == "") {
615 openMessageBox("ChronoShare is not active",
Lijing Wang152449c2015-05-31 16:25:16 -0700616 "Username and/or shared folder name are not set.\n\n To activate ChronoShare, "
617 "open Settings menu and configure your username and shared folder name");
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800618 }
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800619
620 this->hide();
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800621 event->ignore(); // don't let the event propagate to the base class
Jared Lindblomb4032e22013-01-17 23:50:51 -0800622}
Jared Lindblomdc845f02013-01-18 17:29:40 -0800623
Lijing Wang152449c2015-05-31 16:25:16 -0700624} // namespace chronoshare
625} // namespace ndn
626
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800627#include "chronosharegui.moc"