blob: 1464caebb73f72c22275cf06ba277f66709426f5 [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"
Alexander Afanasyeve83c0562016-12-24 10:20:41 -080022#include "core/chronoshare-config.hpp"
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -080023
Alexander Afanasyevf4cde4e2016-12-25 13:42:57 -080024#include "ccnx-wrapper.hpp"
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080025#include "logging.hpp"
26#include <QDesktopServices>
Zhenkai Zhuce04cb42013-02-25 15:29:31 -080027#include <QDir>
Zhenkai Zhu25e13582013-02-27 15:33:01 -080028#include <QFileInfo>
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080029#include <QValidator>
Alexander Afanasyev9ca444e2013-01-25 16:29:35 -080030
31#include <boost/make_shared.hpp>
32
33using namespace boost;
Alexander Afanasyev1dd37ed2013-08-14 18:08:09 -070034using namespace Ndnx;
Jared Lindblom06405c42013-01-17 20:48:39 -080035
Zhenkai Zhud9429222013-02-25 22:34:09 -080036static const string HTTP_SERVER_ADDRESS = "localhost";
37static const string HTTP_SERVER_PORT = "9001";
Zhenkai Zhu6b53f932013-03-02 20:46:02 -080038#ifdef _DEBUG
Zhenkai Zhu5c2475b2013-02-26 22:57:31 -080039static const string DOC_ROOT = "gui/html";
Zhenkai Zhu6b53f932013-03-02 20:46:02 -080040#else
41static const string DOC_ROOT = ":/html";
42#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 Zhud5756312013-01-31 13:49:45 -080050 , m_watcher(0)
51 , m_dispatcher(0)
Zhenkai Zhud9429222013-02-25 22:34:09 -080052 , m_httpServer(0)
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -080053#ifdef ADHOC_SUPPORTED
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080054 , m_executor(1)
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -080055#endif
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -080056#ifdef SPARKLE_SUPPORTED
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080057 , m_autoUpdate(
58 new SparkleAutoUpdate(tr("http://irl.cs.ucla.edu/~zhenkai/chronoshare_dist/chronoshare.xml")))
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -080059#endif
Jared Lindblom06405c42013-01-17 20:48:39 -080060{
Jared Lindblomf4b900e2013-02-07 18:08:47 -080061 setWindowTitle("Settings");
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080062 setMinimumWidth(600);
Jared Lindblomc8ffd592013-01-25 00:00:30 -080063
Jared Lindblomd8b76ad2013-02-07 20:37:37 -080064 labelUsername = new QLabel("Username (hint: /<username>)");
Jared Lindblomc8ffd592013-01-25 00:00:30 -080065 labelSharedFolder = new QLabel("Shared Folder Name");
66 labelSharedFolderPath = new QLabel("Shared Folder Path");
Zhenkai Zhuf3ea97a2013-01-30 15:20:52 -080067
Zhenkai Zhud5756312013-01-31 13:49:45 -080068 QRegExp regex("(/[^/]+)+$");
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080069 QValidator* prefixValidator = new QRegExpValidator(regex, this);
Zhenkai Zhuf3ea97a2013-01-30 15:20:52 -080070
Jared Lindblomc8ffd592013-01-25 00:00:30 -080071 editUsername = new QLineEdit();
Zhenkai Zhuf3ea97a2013-01-30 15:20:52 -080072 editUsername->setValidator(prefixValidator);
73
74 QRegExp noPureWhiteSpace("^\\S+.*$");
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -080075 QValidator* wsValidator = new QRegExpValidator(noPureWhiteSpace, this);
Jared Lindblomc8ffd592013-01-25 00:00:30 -080076 editSharedFolder = new QLineEdit();
Zhenkai Zhuf3ea97a2013-01-30 15:20:52 -080077 editSharedFolder->setValidator(wsValidator);
78
Jared Lindblomc8ffd592013-01-25 00:00:30 -080079 editSharedFolderPath = new QLineEdit();
80 editSharedFolderPath->setReadOnly(true);
81 QPalette pal = editSharedFolderPath->palette();
82 pal.setColor(QPalette::Active, QPalette::Base, pal.color(QPalette::Disabled, QPalette::Base));
83 editSharedFolderPath->setPalette(pal);
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -080084 button = new QPushButton("Save and apply settings");
Zhenkai Zhu630d0f12013-02-28 17:21:54 -080085
86 QString versionString = QString("Version: ChronoShare v%1").arg(CHRONOSHARE_VERSION);
87 label = new QLabel(versionString, this);
Jared Lindblomc8ffd592013-01-25 00:00:30 -080088
89 connect(button, SIGNAL(clicked()), this, SLOT(changeSettings()));
90
91 mainLayout = new QVBoxLayout; //vertically
92 mainLayout->addWidget(labelUsername);
93 mainLayout->addWidget(editUsername);
94 mainLayout->addWidget(labelSharedFolder);
95 mainLayout->addWidget(editSharedFolder);
96 mainLayout->addWidget(labelSharedFolderPath);
97 mainLayout->addWidget(editSharedFolderPath);
98 mainLayout->addWidget(button);
99 mainLayout->addWidget(label);
100 setLayout(mainLayout);
101
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800102 // create actions that result from clicking a menu option
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800103 createActionsAndMenu();
Jared Lindblomb4032e22013-01-17 23:50:51 -0800104
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800105 // create tray icon
106 createTrayIcon();
Jared Lindblomb4032e22013-01-17 23:50:51 -0800107
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800108 // set icon image
109 setIcon();
Jared Lindblom06405c42013-01-17 20:48:39 -0800110
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800111 // show tray icon
112 m_trayIcon->show();
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -0800113
Zhenkai Zhud5756312013-01-31 13:49:45 -0800114 // load settings
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800115 if (!loadSettings()) {
116 // prompt user to choose folder
117 openMessageBox("First Time Setup",
118 "Please enter a username, shared folder name and choose the shared folder path on your local filesystem.");
119 viewSettings();
120 openFileDialog();
121 viewSettings();
122 }
123 else {
124 if (m_username.isNull() || m_username == "" || m_sharedFolderName.isNull() ||
125 m_sharedFolderName == "") {
126 openMessageBox("First Time Setup",
127 "To activate ChronoShare, please configure your username and shared folder name.");
Zhenkai Zhud5756312013-01-31 13:49:45 -0800128 viewSettings();
129 }
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800130 else {
131 startBackend();
Zhenkai Zhud5756312013-01-31 13:49:45 -0800132 }
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800133 }
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800134
135#ifdef ADHOC_SUPPORTED
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800136 m_executor.start();
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800137#endif
Zhenkai Zhud5756312013-01-31 13:49:45 -0800138}
139
140void
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800141ChronoShareGui::startBackend(bool restart /*=false*/)
Zhenkai Zhud5756312013-01-31 13:49:45 -0800142{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800143 if (m_username.isNull() || m_username == "" || m_sharedFolderName.isNull() ||
144 m_sharedFolderName == "" || m_dirPath.isNull() || m_dirPath == "") {
145 _LOG_DEBUG("Don't start backend, because settings are in progress or incomplete");
146 return;
147 }
148
149 if (m_watcher != 0 && m_dispatcher != 0) {
150 if (!restart) {
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800151 return;
152 }
153
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800154 _LOG_DEBUG("Restarting Dispatcher and FileWatcher for the new location or new username");
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800155 delete m_watcher; // stop filewatching ASAP
156 delete m_dispatcher; // stop dispatcher ASAP, but after watcher (to prevent triggering callbacks on deleted object)
Zhenkai Zhud5756312013-01-31 13:49:45 -0800157 }
158
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800159 filesystem::path realPathToFolder(m_dirPath.toStdString());
160 realPathToFolder /= m_sharedFolderName.toStdString();
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800161
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800162 m_dispatcher = new Dispatcher(m_username.toStdString(), m_sharedFolderName.toStdString(),
163 realPathToFolder, make_shared<CcnxWrapper>());
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800164
165 // Alex: this **must** be here, otherwise m_dirPath will be uninitialized
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800166 m_watcher = new FsWatcher(realPathToFolder.string().c_str(),
167 bind(&Dispatcher::Did_LocalFile_AddOrModify, m_dispatcher, _1),
168 bind(&Dispatcher::Did_LocalFile_Delete, m_dispatcher, _1));
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800169
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800170 if (m_httpServer != 0) {
171 // no need to restart webserver if it already exists
172 return;
173 }
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800174
Zhenkai Zhud9429222013-02-25 22:34:09 -0800175 QFileInfo indexHtmlInfo(":/html/index.html");
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800176 if (indexHtmlInfo.exists()) {
177 try {
Zhenkai Zhu8da33652013-03-04 10:52:03 -0800178 m_httpServer = new http::server::server(HTTP_SERVER_ADDRESS, HTTP_SERVER_PORT, DOC_ROOT);
179 m_httpServerThread = boost::thread(&http::server::server::run, m_httpServer);
180 }
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800181 catch (std::exception& e) {
182 _LOG_ERROR("Start http server failed");
Zhenkai Zhu8da33652013-03-04 10:52:03 -0800183 m_httpServer = 0; // just to make sure
184 QMessageBox msgBox;
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800185 msgBox.setText("WARNING: Cannot start http server!");
186 msgBox.setIcon(QMessageBox::Warning);
187 msgBox.setInformativeText(
188 QString(
189 "Starting http server failed. You will not be able to check history from web brower. Exception caused: %1")
190 .arg(e.what()));
Zhenkai Zhu8da33652013-03-04 10:52:03 -0800191 msgBox.setStandardButtons(QMessageBox::Ok);
192 msgBox.exec();
193 }
Zhenkai Zhud9429222013-02-25 22:34:09 -0800194 }
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800195 else {
196 _LOG_ERROR("Http server doc root dir does not exist!");
Zhenkai Zhud9429222013-02-25 22:34:09 -0800197 }
Jared Lindblom06405c42013-01-17 20:48:39 -0800198}
199
200ChronoShareGui::~ChronoShareGui()
201{
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800202#ifdef ADHOC_SUPPORTED
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800203 m_executor.shutdown();
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800204#endif
205
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800206 delete m_watcher; // stop filewatching ASAP
Alexander Afanasyev9ca444e2013-01-25 16:29:35 -0800207 delete m_dispatcher; // stop dispatcher ASAP, but after watcher (to prevent triggering callbacks on deleted object)
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800208 if (m_httpServer != 0) {
Zhenkai Zhu8da33652013-03-04 10:52:03 -0800209 m_httpServer->handle_stop();
210 m_httpServerThread.join();
211 delete m_httpServer;
212 }
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -0800213
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800214 // cleanup
215 delete m_trayIcon;
216 delete m_trayIconMenu;
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800217#ifdef ADHOC_SUPPORTED
218 delete m_wifiAction;
219#endif
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -0800220#ifdef SPARKLE_SUPPORTED
221 delete m_autoUpdate;
Zhenkai Zhubb170d42013-02-25 13:48:59 -0800222 delete m_checkForUpdates;
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -0800223#endif
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800224 delete m_openFolder;
225 delete m_viewSettings;
226 delete m_changeFolder;
227 delete m_quitProgram;
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800228
229 delete labelUsername;
230 delete labelSharedFolder;
231 delete editUsername;
232 delete editSharedFolder;
233 delete button;
234 delete label;
235 delete mainLayout;
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800236}
237
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800238void
239ChronoShareGui::openMessageBox(QString title, QString text)
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800240{
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800241 QMessageBox messageBox(this);
242 messageBox.setWindowTitle(title);
243 messageBox.setText(text);
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800244
Alexander Afanasyevcf771142013-03-19 11:15:27 -0700245 messageBox.setIconPixmap(QPixmap(ICON_BIG_FILE));
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800246
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800247 messageBox.exec();
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800248}
249
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800250void
251ChronoShareGui::openMessageBox(QString title, QString text, QString infotext)
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800252{
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800253 QMessageBox messageBox(this);
254 messageBox.setWindowTitle(title);
255 messageBox.setText(text);
256 messageBox.setInformativeText(infotext);
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800257
Alexander Afanasyevcf771142013-03-19 11:15:27 -0700258 messageBox.setIconPixmap(QPixmap(ICON_BIG_FILE));
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800259
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800260 messageBox.exec();
Jared Lindblom06405c42013-01-17 20:48:39 -0800261}
262
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800263void
264ChronoShareGui::createActionsAndMenu()
Jared Lindblom06405c42013-01-17 20:48:39 -0800265{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800266 _LOG_DEBUG("Create actions");
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800267
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800268 // create the "open folder" action
269 m_openFolder = new QAction(tr("&Open Folder"), this);
270 connect(m_openFolder, SIGNAL(triggered()), this, SLOT(openSharedFolder()));
Jared Lindblom06405c42013-01-17 20:48:39 -0800271
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800272 m_openWeb = new QAction(tr("Open in &Browser"), this);
273 connect(m_openWeb, SIGNAL(triggered()), this, SLOT(openInWebBrowser()));
274
275 m_recentFilesMenu = new QMenu(tr("Recently Changed Files"), this);
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800276 for (int i = 0; i < 5; i++) {
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800277 m_fileActions[i] = new QAction(this);
278 m_fileActions[i]->setVisible(false);
279 connect(m_fileActions[i], SIGNAL(triggered()), this, SLOT(openFile()));
280 m_recentFilesMenu->addAction(m_fileActions[i]);
281 }
282 connect(m_recentFilesMenu, SIGNAL(aboutToShow()), this, SLOT(updateRecentFilesMenu()));
283
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800284 // create the "view settings" action
285 m_viewSettings = new QAction(tr("&View Settings"), this);
286 connect(m_viewSettings, SIGNAL(triggered()), this, SLOT(viewSettings()));
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800287
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800288 // create the "change folder" action
289 m_changeFolder = new QAction(tr("&Change Folder"), this);
290 connect(m_changeFolder, SIGNAL(triggered()), this, SLOT(openFileDialog()));
Jared Lindblomb4032e22013-01-17 23:50:51 -0800291
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800292#ifdef ADHOC_SUPPORTED
293 // create "AdHoc Wifi" action
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800294 m_wifiAction = new QAction(tr("Enable AdHoc &WiFI"), m_trayIconMenu);
295 m_wifiAction->setChecked(false);
296 m_wifiAction->setCheckable(true);
297 connect(m_wifiAction, SIGNAL(toggled(bool)), this, SLOT(onAdHocChange(bool)));
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800298#endif
299
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -0800300#ifdef SPARKLE_SUPPORTED
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800301 m_checkForUpdates = new QAction(tr("Check For Updates"), this);
302 connect(m_checkForUpdates, SIGNAL(triggered()), this, SLOT(onCheckForUpdates()));
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -0800303#endif
304
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800305 // create the "quit program" action
306 m_quitProgram = new QAction(tr("&Quit"), this);
307 connect(m_quitProgram, SIGNAL(triggered()), qApp, SLOT(quit()));
Jared Lindblom06405c42013-01-17 20:48:39 -0800308}
309
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800310void
311ChronoShareGui::createTrayIcon()
Jared Lindblom06405c42013-01-17 20:48:39 -0800312{
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800313 // create a new icon menu
314 m_trayIconMenu = new QMenu(this);
Jared Lindblom06405c42013-01-17 20:48:39 -0800315
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800316 // add actions to the menu
317 m_trayIconMenu->addAction(m_openFolder);
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800318 m_trayIconMenu->addAction(m_openWeb);
319 m_trayIconMenu->addMenu(m_recentFilesMenu);
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800320 m_trayIconMenu->addSeparator();
321 m_trayIconMenu->addAction(m_viewSettings);
322 m_trayIconMenu->addAction(m_changeFolder);
Alexander Afanasyeve8496a32013-03-03 16:10:43 -0800323
324#ifdef SPARKLE_SUPPORTED
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800325 m_trayIconMenu->addSeparator();
Zhenkai Zhu3857d712013-02-27 22:51:25 -0800326 m_trayIconMenu->addAction(m_checkForUpdates);
Alexander Afanasyeve8496a32013-03-03 16:10:43 -0800327#endif
328
329#ifdef ADHOC_SUPPORTED
Zhenkai Zhu3857d712013-02-27 22:51:25 -0800330 m_trayIconMenu->addSeparator();
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800331 m_trayIconMenu->addAction(m_wifiAction);
Alexander Afanasyeve8496a32013-03-03 16:10:43 -0800332#endif
333
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800334 m_trayIconMenu->addSeparator();
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800335 m_trayIconMenu->addAction(m_quitProgram);
Jared Lindblom06405c42013-01-17 20:48:39 -0800336
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800337 // create new tray icon
338 m_trayIcon = new QSystemTrayIcon(this);
Jared Lindblomb4032e22013-01-17 23:50:51 -0800339
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800340 // associate the menu with the tray icon
341 m_trayIcon->setContextMenu(m_trayIconMenu);
Jared Lindblomb4032e22013-01-17 23:50:51 -0800342
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800343 // handle left click of icon
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800344 connect(m_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this,
345 SLOT(trayIconClicked(QSystemTrayIcon::ActivationReason)));
Jared Lindblom06405c42013-01-17 20:48:39 -0800346}
347
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800348void
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800349ChronoShareGui::onAdHocChange(bool state)
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800350{
351#ifdef ADHOC_SUPPORTED
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800352 if (m_wifiAction->isChecked()) {
353 QMessageBox msgBox;
354 msgBox.setText("WARNING: your WiFi will be disconnected");
355 msgBox.setIcon(QMessageBox::Warning);
356 msgBox.setInformativeText(
357 "AdHoc WiFi will disconnect your current WiFi.\n Are you sure that you are OK with that?");
358 msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
359 msgBox.setDefaultButton(QMessageBox::Cancel);
360 int ret = msgBox.exec();
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800361
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800362 if (ret == QMessageBox::Cancel) {
363 m_wifiAction->setChecked(false);
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800364 }
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800365 else {
366 m_wifiAction->setText(tr("Disable AdHoc WiFI"));
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800367
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800368 // create adhoc
369 m_executor.execute(Adhoc::CreateAdhoc);
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800370 }
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800371 }
372 else {
373 m_wifiAction->setText(tr("Enable AdHoc WiFI"));
374
375 // disable adhoc
376 m_executor.execute(Adhoc::DestroyAdhoc);
377
378 // a trick in DestroyAdhoc ensures that WiFi will be reconnected to a default WiFi
379 }
Alexander Afanasyev85a4ba02013-02-24 16:30:17 -0800380#endif
381}
382
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -0800383void
384ChronoShareGui::onCheckForUpdates()
385{
Zhenkai Zhu249fbc72013-02-27 21:50:25 -0800386#ifdef SPARKLE_SUPPORTED
Zhenkai Zhu3857d712013-02-27 22:51:25 -0800387 cout << "+++++++++++ trying to update +++++++ " << endl;
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -0800388 m_autoUpdate->checkForUpdates();
Zhenkai Zhu3857d712013-02-27 22:51:25 -0800389 cout << "+++++++++++ end trying to update +++++++ " << endl;
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -0800390#endif
Zhenkai Zhu249fbc72013-02-27 21:50:25 -0800391}
Zhenkai Zhuaca6cb52013-02-24 23:34:03 -0800392
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800393void
394ChronoShareGui::setIcon()
Jared Lindblom06405c42013-01-17 20:48:39 -0800395{
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800396 // set the icon image
Alexander Afanasyevcf771142013-03-19 11:15:27 -0700397 m_trayIcon->setIcon(QIcon(ICON_TRAY_FILE));
Jared Lindblom06405c42013-01-17 20:48:39 -0800398}
399
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800400void
401ChronoShareGui::openSharedFolder()
Jared Lindblom06405c42013-01-17 20:48:39 -0800402{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800403 filesystem::path realPathToFolder(m_dirPath.toStdString());
404 realPathToFolder /= m_sharedFolderName.toStdString();
Zhenkai Zhu872cc592013-03-04 14:20:46 -0800405 QString path = QDir::toNativeSeparators(realPathToFolder.string().c_str());
Zhenkai Zhuce04cb42013-02-25 15:29:31 -0800406 QDesktopServices::openUrl(QUrl("file:///" + path));
Jared Lindblom06405c42013-01-17 20:48:39 -0800407}
Jared Lindblomb4032e22013-01-17 23:50:51 -0800408
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800409void
410ChronoShareGui::openInWebBrowser()
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800411{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800412 QUrl url("http://localhost:9001/");
413 url.setFragment("folderHistory&"
414 "user=" +
415 QUrl::toPercentEncoding(m_username) + "&"
416 "folder=" +
417 QUrl::toPercentEncoding(m_sharedFolderName));
Alexander Afanasyevf63a5142013-02-28 02:21:42 -0800418
419 // i give up. there is double encoding and I have no idea how to fight it...
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800420 QDesktopServices::openUrl(url);
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800421}
422
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800423void
424ChronoShareGui::openFile()
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800425{
426 // figure out who sent the signal
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800427 QAction* pAction = qobject_cast<QAction*>(sender());
428 if (pAction->isEnabled()) {
429// we stored full path of the file in this toolTip field
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800430#ifdef Q_WS_MAC
431 // we do some hack so we could show the file in Finder highlighted
432 QStringList args;
433 args << "-e";
434 args << "tell application \"Finder\"";
435 args << "-e";
436 args << "activate";
437 args << "-e";
438 args << "select POSIX file \"" + pAction->toolTip() + "/" + pAction->text() + "\"";
439 args << "-e";
440 args << "end tell";
441 QProcess::startDetached("osascript", args);
442#else
443 // too bad qt couldn't do highlighting for Linux (or Mac)
444 QDesktopServices::openUrl(QUrl("file:///" + pAction->toolTip()));
445#endif
446 }
447}
448
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800449void
450ChronoShareGui::updateRecentFilesMenu()
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800451{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800452 for (int i = 0; i < 5; i++) {
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800453 m_fileActions[i]->setVisible(false);
454 }
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800455 m_dispatcher->LookupRecentFileActions(boost::bind(&ChronoShareGui::checkFileAction, this, _1, _2,
456 _3),
457 5);
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800458}
459
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800460void
461ChronoShareGui::checkFileAction(const std::string& filename, int action, int index)
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800462{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800463 filesystem::path realPathToFolder(m_dirPath.toStdString());
464 realPathToFolder /= m_sharedFolderName.toStdString();
Zhenkai Zhu872cc592013-03-04 14:20:46 -0800465 realPathToFolder /= filename;
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800466 QString fullPath = realPathToFolder.string().c_str();
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800467 QFileInfo fileInfo(fullPath);
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800468 if (fileInfo.exists()) {
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800469 // This is a hack, we just use some field to store the path
470 m_fileActions[index]->setToolTip(fileInfo.absolutePath());
471 m_fileActions[index]->setEnabled(true);
472 }
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800473 else {
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800474 // after half an hour frustrating test and search around,
475 // I think it's the problem of Qt.
476 // According to the Qt doc, the action cannot be clicked
477 // and the area would be grey, but it didn't happen
478 // User can still trigger the action, and not greyed
479 // added check in SLOT to see if the action is "enalbed"
480 // as a remedy
481 // Give up at least for now
482 m_fileActions[index]->setEnabled(false);
483 // UPDATE, file not fetched yet
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800484 if (action == 0) {
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800485 QFont font;
486 // supposed by change the font, didn't happen
487 font.setWeight(QFont::Light);
488 m_fileActions[index]->setFont(font);
489 m_fileActions[index]->setToolTip(tr("Fetching..."));
490 }
491 // DELETE
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800492 else {
Zhenkai Zhu25e13582013-02-27 15:33:01 -0800493 QFont font;
494 // supposed by change the font, didn't happen
495 font.setStrikeOut(true);
496 m_fileActions[index]->setFont(font);
497 m_fileActions[index]->setToolTip(tr("Deleted..."));
498 }
499 }
500 m_fileActions[index]->setText(fileInfo.fileName());
501 m_fileActions[index]->setVisible(true);
502}
503
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800504void
505ChronoShareGui::changeSettings()
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800506{
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800507 QString oldUsername = m_username;
508 QString oldSharedFolderName = m_sharedFolderName;
509
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800510 if (!editUsername->text().isEmpty())
Zhenkai Zhuf3ea97a2013-01-30 15:20:52 -0800511 m_username = editUsername->text().trimmed();
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800512 else
513 editUsername->setText(m_username);
514
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800515 if (!editSharedFolder->text().isEmpty())
Zhenkai Zhuf3ea97a2013-01-30 15:20:52 -0800516 m_sharedFolderName = editSharedFolder->text().trimmed();
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800517 else
518 editSharedFolder->setText(m_sharedFolderName);
519
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800520 if (m_username.isNull() || m_username == "" || m_sharedFolderName.isNull() ||
521 m_sharedFolderName == "") {
522 openMessageBox("Error", "Username and shared folder name cannot be empty");
523 }
524 else {
525 saveSettings();
526 this->hide();
Zhenkai Zhud5756312013-01-31 13:49:45 -0800527
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800528 if (m_username != oldUsername || oldSharedFolderName != m_sharedFolderName) {
529 startBackend(true); // restart dispatcher/fswatcher
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800530 }
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800531 }
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800532}
533
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800534void
535ChronoShareGui::openFileDialog()
Jared Lindblomb4032e22013-01-17 23:50:51 -0800536{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800537 while (true) {
538 // prompt user for new directory
539 QString tempPath =
540 QFileDialog::getExistingDirectory(this, tr("Choose ChronoShare folder"), m_dirPath,
541 QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
542 if (tempPath.isNull()) {
543 if (m_dirPath.isNull()) {
544 openMessageBox("Notice",
545 "ChronoShare will use [" + QDir::homePath() +
546 "/ChronoShare]. \n\nYou can change it later selecting \"Change Folder\" menu.");
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800547
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800548 m_dirPath = QDir::homePath() + "/ChronoShare";
549 editSharedFolderPath->setText(m_dirPath);
550 break;
551 }
552 else {
553 // user just cancelled, no need to do anything else
554 return;
555 }
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800556 }
Jared Lindblomb4032e22013-01-17 23:50:51 -0800557
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800558 QFileInfo qFileInfo(tempPath);
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800559
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800560 if (!qFileInfo.isDir()) {
561 openMessageBox("Error", "Please select an existing folder or create a new one.");
562 continue;
563 }
564
565 if (m_dirPath == tempPath) {
566 // user selected the same directory, no need to do anything
567 return;
568 }
569
570 m_dirPath = tempPath;
571 editSharedFolderPath->setText(m_dirPath);
572 break;
573 }
574
575 _LOG_DEBUG("Selected path: " << m_dirPath.toStdString());
576 // save settings
577 saveSettings();
578
579 startBackend(true); // restart dispatcher/fswatcher
Jared Lindblomb4032e22013-01-17 23:50:51 -0800580}
581
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800582void
583ChronoShareGui::trayIconClicked(QSystemTrayIcon::ActivationReason reason)
Jared Lindblomb4032e22013-01-17 23:50:51 -0800584{
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800585 // if double clicked, open shared folder
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800586 if (reason == QSystemTrayIcon::DoubleClick) {
587 openSharedFolder();
588 }
Jared Lindblomb4032e22013-01-17 23:50:51 -0800589}
590
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800591void
592ChronoShareGui::viewSettings()
Jared Lindblomb4032e22013-01-17 23:50:51 -0800593{
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800594 //simple for now
595 this->show();
Zhenkai Zhud5756312013-01-31 13:49:45 -0800596 this->raise();
597 this->activateWindow();
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800598}
599
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800600bool
601ChronoShareGui::loadSettings()
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800602{
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800603 bool successful = true;
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800604
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800605 // Load Settings
606 // QSettings settings(m_settingsFilePath, QSettings::NativeFormat);
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800607 QSettings settings(QSettings::NativeFormat, QSettings::UserScope, "irl.cs.ucla.edu", "ChronoShare");
Jared Lindblom5d7e51e2013-01-18 00:59:34 -0800608
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800609 // _LOG_DEBUG (lexical_cast<string> (settings.allKeys()));
610
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800611 if (settings.contains("username")) {
612 m_username = settings.value("username", "admin").toString();
613 }
614 else {
615 successful = false;
616 }
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800617
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800618 editUsername->setText(m_username);
619
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800620 if (settings.contains("sharedfoldername")) {
621 m_sharedFolderName = settings.value("sharedfoldername", "shared").toString();
622 }
623 else {
624 successful = false;
625 }
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800626
627 editSharedFolder->setText(m_sharedFolderName);
628
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800629 if (settings.contains("dirPath")) {
630 m_dirPath = settings.value("dirPath", QDir::homePath()).toString();
631 }
632 else {
633 successful = false;
634 }
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800635
636 editSharedFolderPath->setText(m_dirPath);
637
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800638 _LOG_DEBUG("Found configured path: " << (successful ? m_dirPath.toStdString() : std::string("no")));
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800639
640 return successful;
Jared Lindblomb4032e22013-01-17 23:50:51 -0800641}
642
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800643void
644ChronoShareGui::saveSettings()
Jared Lindblomb4032e22013-01-17 23:50:51 -0800645{
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800646 // Save Settings
647 // QSettings settings(m_settingsFilePath, QSettings::NativeFormat);
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800648 QSettings settings(QSettings::NativeFormat, QSettings::UserScope, "irl.cs.ucla.edu", "ChronoShare");
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800649
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800650 settings.setValue("dirPath", m_dirPath);
Jared Lindblomc8ffd592013-01-25 00:00:30 -0800651 settings.setValue("username", m_username);
652 settings.setValue("sharedfoldername", m_sharedFolderName);
Jared Lindblomb4032e22013-01-17 23:50:51 -0800653}
654
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800655void
656ChronoShareGui::closeEvent(QCloseEvent* event)
Jared Lindblomb4032e22013-01-17 23:50:51 -0800657{
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800658 _LOG_DEBUG("Close Event");
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800659
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800660 if (m_username.isNull() || m_username == "" || m_sharedFolderName.isNull() ||
661 m_sharedFolderName == "") {
662 openMessageBox("ChronoShare is not active",
663 "Username and/or shared folder name are not set.\n\n To activate ChronoShare, open Settings menu and configure your username and shared folder name");
664 }
Alexander Afanasyevd437ffc2013-03-04 12:29:23 -0800665
666 this->hide();
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800667 event->ignore(); // don't let the event propagate to the base class
Jared Lindblomb4032e22013-01-17 23:50:51 -0800668}
Jared Lindblomdc845f02013-01-18 17:29:40 -0800669
670#if WAF
Jared Lindblomdc845f02013-01-18 17:29:40 -0800671#include "chronosharegui.cpp.moc"
Alexander Afanasyeveda3b7a2016-12-25 11:26:40 -0800672#include "chronosharegui.moc"
Alexander Afanasyev83a53002013-01-24 11:12:01 -0800673#endif