nextcloud-desktop/src/gui/application.cpp

570 lines
18 KiB
C++
Raw Normal View History

2011-04-06 13:48:02 +04:00
/*
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
* Copyright (C) by Daniel Molkentin <danimo@owncloud.com>
2011-04-06 13:48:02 +04:00
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "application.h"
2012-06-15 15:36:38 +04:00
#include <iostream>
#include "config.h"
#include "account.h"
#include "accountstate.h"
#include "connectionvalidator.h"
#include "folder.h"
#include "folderman.h"
#include "logger.h"
2014-11-10 01:25:57 +03:00
#include "configfile.h"
#include "socketapi.h"
#include "sslerrordialog.h"
#include "theme.h"
#include "utility.h"
#include "clientproxy.h"
#include "sharedialog.h"
2011-02-17 02:21:45 +03:00
#include "updater/updater.h"
#include "creds/abstractcredentials.h"
#include "config.h"
#if defined(Q_OS_WIN)
2013-06-05 18:14:20 +04:00
#include <windows.h>
#endif
2012-05-21 18:48:49 +04:00
#include <QTranslator>
2013-08-28 22:58:22 +04:00
#include <QMenu>
#include <QMessageBox>
2012-05-21 18:48:49 +04:00
class QSocket;
2014-11-10 00:34:07 +03:00
namespace OCC {
2011-02-17 02:21:45 +03:00
namespace {
static const char optionsC[] =
"Options:\n"
" -h --help : show this help screen.\n"
" --logwindow : open a window to show log output.\n"
" --logfile <filename> : write log output to file <filename>.\n"
" --logdir <name> : write each sync log output in a new file\n"
" in directory <name>.\n"
" --logexpire <hours> : removes logs older than <hours> hours.\n"
" (to be used with --logdir)\n"
" --logflush : flush the log file after every write.\n"
" --confdir <dirname> : Use the given configuration directory.\n"
;
QString applicationTrPath()
{
2014-04-29 13:02:44 +04:00
#if defined(Q_OS_WIN)
return QApplication::applicationDirPath();
2014-04-29 13:02:44 +04:00
#elif defined(Q_OS_MAC)
return QApplication::applicationDirPath()+QLatin1String("/../Resources/Translations"); // path defaults to app dir.
#elif defined(Q_OS_UNIX)
return QString::fromLatin1(DATADIR "/" APPLICATION_EXECUTABLE "/i18n/");
#endif
}
}
// ----------------------------------------------------------------------------------
2012-04-20 15:17:48 +04:00
Application::Application(int &argc, char **argv) :
SharedTools::QtSingleApplication(Theme::instance()->appName() ,argc, argv),
_gui(0),
_theme(Theme::instance()),
_helpOnly(false),
_startupNetworkError(false),
_showLogWindow(false),
_logExpire(0),
_logFlush(false),
_userTriggeredConnect(false),
_debugMode(false)
2011-02-17 02:21:45 +03:00
{
// TODO: Can't set this without breaking current config pathes
// setOrganizationName(QLatin1String(APPLICATION_VENDOR));
setOrganizationDomain(QLatin1String(APPLICATION_REV_DOMAIN));
setApplicationName( _theme->appNameGUI() );
setWindowIcon( _theme->applicationIcon() );
2012-02-23 14:44:44 +04:00
parseOptions(arguments());
//no need to waste time;
if ( _helpOnly ) return;
if (isRunning())
return;
setupLogging();
setupTranslations();
_folderManager.reset(new FolderMan);
connect( this, SIGNAL(messageReceived(QString, QObject*)), SLOT(slotParseOptions(QString, QObject*)));
// Create the account info manager to ensure it's listening to the
// account manager.
AccountStateManager::instance();
AccountPtr account = Account::restore();
if (account) {
account->setSslErrorHandler(new SslDialogErrorHandler);
AccountManager::instance()->setAccount(account);
}
2013-10-24 02:29:08 +04:00
FolderMan::instance()->setSyncEnabled(false);
setQuitOnLastWindowClosed(false);
qRegisterMetaType<Progress::Info>("Progress::Info");
2014-11-10 00:30:29 +03:00
ConfigFile cfg;
_theme->setSystrayUseMonoIcons(cfg.monoIcons());
connect (_theme, SIGNAL(systrayUseMonoIconsChanged(bool)), SLOT(slotUseMonoIconsChanged(bool)));
FolderMan::instance()->setupFolders();
_proxy.setupQtProxyFromConfig(); // folders have to be defined first, than we set up the Qt proxy.
_gui = new ownCloudGui(this);
if( _showLogWindow ) {
_gui->slotToggleLogBrowser(); // _showLogWindow is set in parseOptions.
}
connect(AccountStateManager::instance(), SIGNAL(accountStateAdded(AccountState*)),
SLOT(slotAccountStateAdded(AccountState*)));
connect(AccountStateManager::instance(), SIGNAL(accountStateRemoved(AccountState*)),
SLOT(slotAccountStateRemoved(AccountState*)));
if (AccountState *ai = AccountStateManager::instance()->accountState()) {
slotAccountStateAdded(ai);
}
connect(FolderMan::instance()->socketApi(), SIGNAL(shareCommandReceived(QString, QString)),
_gui, SLOT(slotShowShareDialog(QString, QString)));
// startup procedure.
connect(&_checkConnectionTimer, SIGNAL(timeout()), this, SLOT(slotCheckConnection()));
_checkConnectionTimer.setInterval(32 * 1000); // check for connection every 32 seconds.
_checkConnectionTimer.start();
// Also check immediatly
QTimer::singleShot( 0, this, SLOT( slotCheckConnection() ));
2012-02-23 14:44:44 +04:00
if( cfg.skipUpdateCheck() ) {
qDebug() << Q_FUNC_INFO << "Skipping update check";
} else {
QTimer::singleShot( 3000, this, SLOT( slotStartUpdateDetector() ));
}
connect (this, SIGNAL(aboutToQuit()), SLOT(slotCleanup()));
2011-02-17 02:21:45 +03:00
}
Application::~Application()
{
2014-11-10 00:35:17 +03:00
// qDebug() << "* OCC shutdown";
}
void Application::slotLogin()
{
AccountState *a = AccountStateManager::instance()->accountState();
if (a) {
FolderMan::instance()->setupFolders();
a->setSignedOut(false);
}
}
void Application::slotLogout()
{
AccountState* ai = AccountStateManager::instance()->accountState();
if (ai) {
AccountPtr a = ai->account();
// invalidate & forget token/password
a->credentials()->invalidateToken();
// terminate all syncs and unload folders
FolderMan *folderMan = FolderMan::instance();
folderMan->setSyncEnabled(false);
folderMan->terminateSyncProcess();
ai->setSignedOut(true);
// show result
_gui->slotComputeOverallSyncStatus();
}
}
void Application::slotAccountStateRemoved(AccountState *accountState)
{
disconnect(accountState, SIGNAL(stateChanged(int)), _gui, SLOT(slotAccountStateChanged()));
disconnect(accountState, SIGNAL(stateChanged(int)), this, SLOT(slotAccountStateChanged(int)));
connect(accountState->quotaInfo(), SIGNAL(quotaUpdated(qint64,qint64)),
_gui, SLOT(slotRefreshQuotaDisplay(qint64,qint64)));
}
void Application::slotAccountStateAdded(AccountState *accountState)
{
connect(accountState, SIGNAL(stateChanged(int)), _gui, SLOT(slotAccountStateChanged()));
connect(accountState, SIGNAL(stateChanged(int)), this, SLOT(slotAccountStateChanged(int)));
connect(accountState->quotaInfo(), SIGNAL(quotaUpdated(qint64,qint64)),
_gui, SLOT(slotRefreshQuotaDisplay(qint64,qint64)));
}
void Application::slotCleanup()
{
// explicitly close windows. This is somewhat of a hack to ensure
// that saving the geometries happens ASAP during a OS shutdown
AccountPtr account = AccountManager::instance()->account();
2013-10-24 02:29:08 +04:00
if (account) {
account->save();
2013-10-24 02:29:08 +04:00
}
FolderMan::instance()->unloadAllFolders();
_gui->slotShutdown();
_gui->deleteLater();
}
void Application::slotStartUpdateDetector()
{
Updater *updater = Updater::instance();
updater->backgroundCheckForUpdate();
}
void Application::slotCheckConnection()
{
AccountState *accountState = AccountStateManager::instance()->accountState();
if( accountState ) {
accountState->checkConnectivity();
} else {
// let gui open the setup wizard
_gui->slotOpenSettingsDialog( true );
_checkConnectionTimer.stop(); // don't popup the wizard on interval;
}
}
void Application::slotAccountStateChanged(int state)
{
FolderMan* folderMan = FolderMan::instance();
switch (state) {
case AccountState::Connected:
qDebug() << "Enabling sync scheduler, scheduling all folders";
folderMan->setSyncEnabled(true);
folderMan->slotScheduleAllFolders();
break;
case AccountState::SignedOut:
case AccountState::ConfigurationError:
case AccountState::NetworkError:
case AccountState::Disconnected:
qDebug() << "Disabling sync scheduler, terminating sync";
folderMan->setSyncEnabled(false);
folderMan->terminateSyncProcess();
break;
}
// Stop checking the connection if we're manually signed out or
// when the error is permanent.
if (state == AccountState::SignedOut
|| state == AccountState::ConfigurationError) {
_checkConnectionTimer.stop();
} else if (! _checkConnectionTimer.isActive()) {
_checkConnectionTimer.start();
}
slotUpdateConnectionErrors(state);
}
void Application::slotCrash()
{
Utility::crash();
}
void Application::slotUpdateConnectionErrors(int accountState)
{
bool isConnected = accountState == AccountState::Connected;
if( !isConnected ) {
_startupNetworkError = accountState == AccountState::NetworkError;
}
AccountState *as = AccountStateManager::instance()->accountState();
if (as) {
_gui->setConnectionErrors( isConnected, as->connectionErrors() );
}
}
2012-11-28 22:25:12 +04:00
void Application::slotownCloudWizardDone( int res )
{
FolderMan *folderMan = FolderMan::instance();
2012-11-28 22:25:12 +04:00
if( res == QDialog::Accepted ) {
int cnt = folderMan->setupFolders();
qDebug() << "Set up " << cnt << " folders.";
// We have some sort of configuration. Enable autostart
2013-07-07 00:38:33 +04:00
Utility::setLaunchOnStartup(_theme->appName(), _theme->appNameGUI(), true);
if (cnt == 0) {
// The folder configuration was skipped
_gui->slotShowSettings();
}
2012-11-28 22:25:12 +04:00
}
folderMan->setSyncEnabled( true );
if( res == QDialog::Accepted ) {
_checkConnectionTimer.start();
slotCheckConnection();
}
2012-11-28 22:25:12 +04:00
}
void Application::setupLogging()
{
// might be called from second instance
Logger::instance()->setLogFile(_logFile);
Logger::instance()->setLogDir(_logDir);
Logger::instance()->setLogExpire(_logExpire);
Logger::instance()->setLogFlush(_logFlush);
Logger::instance()->enterNextLogFile();
2012-06-15 15:04:23 +04:00
qDebug() << QString::fromLatin1( "################## %1 %2 (%3) %4").arg(_theme->appName())
.arg( QLocale::system().name() )
.arg(property("ui_lang").toString())
.arg(_theme->version());
}
void Application::slotUseMonoIconsChanged(bool)
{
_gui->slotComputeOverallSyncStatus();
}
void Application::slotParseOptions(const QString &opts, QObject*)
{
QStringList options = opts.split(QLatin1Char('|'));
parseOptions(options);
setupLogging();
//This function is calld happens when someone tries to run another instance of ownCloud
// show the settings dialog
showSettingsDialog();
}
void Application::parseOptions(const QStringList &options)
{
QStringListIterator it(options);
// skip file name;
if (it.hasNext()) it.next();
//parse options; if help or bad option exit
while (it.hasNext()) {
QString option = it.next();
if (option == QLatin1String("--help") || option == QLatin1String("-h")) {
setHelp();
break;
} else if (option == QLatin1String("--logwindow") ||
option == QLatin1String("-l")) {
_showLogWindow = true;
} else if (option == QLatin1String("--logfile")) {
if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) {
_logFile = it.next();
} else {
setHelp();
}
} else if (option == QLatin1String("--logdir")) {
if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) {
_logDir = it.next();
} else {
setHelp();
}
} else if (option == QLatin1String("--logexpire")) {
if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) {
_logExpire = it.next().toInt();
} else {
setHelp();
}
} else if (option == QLatin1String("--logflush")) {
_logFlush = true;
} else if (option == QLatin1String("--confdir")) {
if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) {
QString confDir = it.next();
2014-11-10 00:30:29 +03:00
ConfigFile::setConfDir( confDir );
} else {
showHelp();
}
} else if (option == QLatin1String("--debug")) {
_debugMode = true;
} else {
setHelp();
break;
}
}
}
// Helpers for displaying messages. Note that there is no console on Windows.
#ifdef Q_OS_WIN
// Format as <pre> HTML
static inline void toHtml(QString &t)
{
t.replace(QLatin1Char('&'), QLatin1String("&amp;"));
t.replace(QLatin1Char('<'), QLatin1String("&lt;"));
t.replace(QLatin1Char('>'), QLatin1String("&gt;"));
t.insert(0, QLatin1String("<html><pre>"));
t.append(QLatin1String("</pre></html>"));
}
static void displayHelpText(QString t) // No console on Windows.
{
toHtml(t);
QMessageBox::information(0, Theme::instance()->appNameGUI(), t);
}
#else
static void displayHelpText(const QString &t)
{
std::cout << qPrintable(t);
}
#endif
2012-06-15 15:36:38 +04:00
void Application::showHelp()
{
setHelp();
QString helpText;
QTextStream stream(&helpText);
stream << _theme->appName().toLatin1().constData()
<< QLatin1String(" version ")
<< _theme->version().toLatin1().constData() << endl;
stream << QLatin1String("File synchronisation desktop utility.") << endl << endl
<< QLatin1String(optionsC);
if (_theme->appName() == QLatin1String("ownCloud"))
stream << endl << "For more information, see http://www.owncloud.org" << endl << endl;
displayHelpText(helpText);
}
bool Application::debugMode()
{
return _debugMode;
}
void Application::setHelp()
{
2012-06-15 15:36:38 +04:00
_helpOnly = true;
}
2014-01-28 18:30:58 +04:00
#if defined(Q_OS_WIN) && QT_VERSION < QT_VERSION_CHECK(5,0,0)
2013-06-05 18:14:20 +04:00
bool Application::winEventFilter(MSG *pMsg, long *result)
{
if (pMsg->message == WM_POWERBROADCAST) {
switch(pMsg->wParam) {
case PBT_APMPOWERSTATUSCHANGE:
qDebug() << "WM_POWERBROADCAST: Power state changed";
break;
case PBT_APMSUSPEND:
qDebug() << "WM_POWERBROADCAST: Entering low power state";
break;
case PBT_APMRESUMEAUTOMATIC:
qDebug() << "WM_POWERBROADCAST: Resuming from low power state";
break;
default:
break;
}
return true;
}
2013-06-10 17:37:29 +04:00
return SharedTools::QtSingleApplication::winEventFilter(pMsg, result);
2013-06-05 18:14:20 +04:00
}
#endif
QString substLang(const QString &lang)
{
// Map the more apropriate script codes
// to country codes as used by Qt and
// transifex translation conventions.
// Simplified Chinese
if (lang == QLatin1String("zh_Hans"))
return QLatin1String("zh_CN");
// Traditional Chinese
if (lang == QLatin1String("zh_Hant"))
return QLatin1String("zh_TW");
return lang;
}
void Application::setupTranslations()
{
QStringList uiLanguages;
// uiLanguages crashes on Windows with 4.8.0 release builds
#if (QT_VERSION >= 0x040801) || (QT_VERSION >= 0x040800 && !defined(Q_OS_WIN))
uiLanguages = QLocale::system().uiLanguages();
#else
// older versions need to fall back to the systems locale
uiLanguages << QLocale::system().name();
#endif
QString enforcedLocale = Theme::instance()->enforcedLocale();
if (!enforcedLocale.isEmpty())
uiLanguages.prepend(enforcedLocale);
QTranslator *translator = new QTranslator(this);
QTranslator *qtTranslator = new QTranslator(this);
QTranslator *qtkeychainTranslator = new QTranslator(this);
foreach(QString lang, uiLanguages) {
lang.replace(QLatin1Char('-'), QLatin1Char('_')); // work around QTBUG-25973
lang = substLang(lang);
const QString trPath = applicationTrPath();
const QString trFile = QLatin1String("mirall_") + lang;
if (translator->load(trFile, trPath) ||
lang.startsWith(QLatin1String("en"))) {
// Permissive approach: Qt and keychain translations
// may be missing, but Qt translations must be there in order
// for us to accept the language. Otherwise, we try with the next.
// "en" is an exeption as it is the default language and may not
// have a translation file provided.
qDebug() << Q_FUNC_INFO << "Using" << lang << "translation";
setProperty("ui_lang", lang);
const QString qtTrPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
const QString qtTrFile = QLatin1String("qt_") + lang;
const QString qtBaseTrFile = QLatin1String("qtbase_") + lang;
if (!qtTranslator->load(qtTrFile, qtTrPath)) {
if (!qtTranslator->load(qtTrFile, trPath)) {
qtTranslator->load(qtBaseTrFile, trPath);
}
}
const QString qtkeychainTrFile = QLatin1String("qtkeychain_") + lang;
if (!qtkeychainTranslator->load(qtkeychainTrFile, qtTrPath)) {
qtkeychainTranslator->load(qtkeychainTrFile, trPath);
}
if (!translator->isEmpty())
installTranslator(translator);
if (!qtTranslator->isEmpty())
installTranslator(qtTranslator);
if (!qtkeychainTranslator->isEmpty())
installTranslator(qtkeychainTranslator);
2013-02-21 18:16:02 +04:00
break;
}
if (property("ui_lang").isNull())
setProperty("ui_lang", "C");
}
}
2012-06-15 15:36:38 +04:00
bool Application::giveHelp()
{
return _helpOnly;
}
void Application::showSettingsDialog()
{
_gui->slotShowSettings();
}
2014-11-10 00:34:07 +03:00
} // namespace OCC
2011-02-17 02:21:45 +03:00