mirror of
https://github.com/RetroShare/RetroShare.git
synced 2024-12-27 00:19:25 -05:00
981 lines
31 KiB
C++
981 lines
31 KiB
C++
/****************************************************************
|
|
* This file is distributed under the following license:
|
|
*
|
|
* Copyright (c) 2006-2007, crypton
|
|
* Copyright (c) 2006, Matt Edman, Justin Hipple
|
|
*
|
|
* 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
****************************************************************/
|
|
|
|
#include <QBuffer>
|
|
#include <QDateTime>
|
|
#include <QDir>
|
|
#include <QFileOpenEvent>
|
|
#include <QLocale>
|
|
#include <QLocalSocket>
|
|
#include <QRegExp>
|
|
#include <QSharedMemory>
|
|
#include <QShortcut>
|
|
#include <QString>
|
|
#include <QStyle>
|
|
#include <QStyleFactory>
|
|
#include <QTextStream>
|
|
#include <QTimer>
|
|
#ifdef __APPLE__
|
|
#include <QUrl>
|
|
#endif
|
|
|
|
#include <iostream>
|
|
#include <stdlib.h>
|
|
|
|
#include <gui/common/html.h>
|
|
#include <gui/common/vmessagebox.h>
|
|
#include <gui/gxs/GxsIdDetails.h>
|
|
#include <gui/settings/rsharesettings.h>
|
|
#include <lang/languagesupport.h>
|
|
#include <util/stringutil.h>
|
|
|
|
#include <retroshare/rsinit.h>
|
|
#include <retroshare/rsversion.h>
|
|
#include <retroshare/rsplugin.h>
|
|
|
|
#include "rshare.h"
|
|
|
|
/* Available command-line arguments. */
|
|
#define ARG_RESET "reset" /**< Reset Rshare's saved settings. */
|
|
#define ARG_DATADIR "datadir" /**< Directory to use for data files. */
|
|
#define ARG_LOGFILE "logfile" /**< Location of our logfile. */
|
|
#define ARG_LOGLEVEL "loglevel" /**< Log verbosity. */
|
|
#define ARG_GUISTYLE "style" /**< Argument specfying GUI style. */
|
|
#define ARG_GUISTYLESHEET "stylesheet" /**< Argument specfying GUI style. */
|
|
#define ARG_LANGUAGE "lang" /**< Argument specifying language. */
|
|
#define ARG_OPMODE_S "o" /**< OpMode (Full, NoTurtle, Gaming, Minimal) */
|
|
#define ARG_OPMODE_L "opmode" /**< OpMode (Full, NoTurtle, Gaming, Minimal) */
|
|
#define ARG_RSLINK_S "r" /**< Open RsLink with protocol retroshare:// */
|
|
#define ARG_RSLINK_L "link" /**< Open RsLink with protocol retroshare:// */
|
|
#define ARG_RSFILE_S "f" /**< Open RsFile with or without arg. */
|
|
#define ARG_RSFILE_L "rsfile" /**< Open RsFile with or without arg. */
|
|
//Other defined for server in /libretroshare/src/rsserver/rsinit.cc:351
|
|
|
|
// The arguments here can be send to a running instance.
|
|
// If the command line contains arguments not listed here, we have to start a new instance.
|
|
// For example, the user wants to start a second instance using --base-dir arg of libretroshare.
|
|
static const char* const forwardableArgs[] = {
|
|
ARG_OPMODE_S,
|
|
ARG_OPMODE_L,
|
|
ARG_RSLINK_S,
|
|
ARG_RSLINK_L,
|
|
ARG_RSFILE_S,
|
|
ARG_RSFILE_L,
|
|
NULL,
|
|
};
|
|
|
|
/* Static member variables */
|
|
QMap<QString, QString> Rshare::_args; /**< List of command-line arguments. */
|
|
Log Rshare::_log; /**< Logs debugging messages to file or stdout. */
|
|
QString Rshare::_style; /**< The current GUI style. */
|
|
QString Rshare::_stylesheet; /**< The current GUI stylesheet. */
|
|
QString Rshare::_language; /**< The current language. */
|
|
QString Rshare::_dateformat; /**< The format of dates in feed items etc. */
|
|
QString Rshare::_opmode; /**< The operating mode passed by args. */
|
|
QStringList Rshare::_links; /**< List of links passed by arguments. */
|
|
QStringList Rshare::_files; /**< List of files passed by arguments. */
|
|
QDateTime Rshare::mStartupTime;
|
|
bool Rshare::useConfigDir;
|
|
QString Rshare::configDir;
|
|
QLocalServer* Rshare::localServer;
|
|
|
|
/** Catches debugging messages from Qt and sends them to RetroShare's logs. If Qt
|
|
* emits a QtFatalMsg, we will write the message to the log and then abort().
|
|
*/
|
|
#if QT_VERSION >= QT_VERSION_CHECK (5, 0, 0)
|
|
void qt_msg_handler(QtMsgType type, const QMessageLogContext &, const QString &msg)
|
|
#else
|
|
void qt_msg_handler(QtMsgType type, const char *msg)
|
|
#endif
|
|
{
|
|
switch (type) {
|
|
case QtDebugMsg:
|
|
rDebug(QString("QtDebugMsg: %1").arg(msg));
|
|
break;
|
|
case QtWarningMsg:
|
|
rNotice(QString("QtWarningMsg: %1").arg(msg));
|
|
break;
|
|
case QtCriticalMsg:
|
|
rWarn(QString("QtCriticalMsg: %1").arg(msg));
|
|
break;
|
|
case QtFatalMsg:
|
|
rError(QString("QtFatalMsg: %1").arg(msg));
|
|
break;
|
|
#if QT_VERSION >= QT_VERSION_CHECK (5, 5, 0)
|
|
case QtInfoMsg:
|
|
break;
|
|
#endif
|
|
}
|
|
if (type == QtFatalMsg) {
|
|
rError("Fatal Qt error. Aborting.");
|
|
abort();
|
|
}
|
|
}
|
|
|
|
static bool notifyRunningInstance()
|
|
{
|
|
// Connect to the Local Server of the main process to notify it
|
|
// that a new process had been started
|
|
QLocalSocket localSocket;
|
|
localSocket.connectToServer(QString(TARGET));
|
|
|
|
std::cerr << "Rshare::Rshare waitForConnected to other instance." << std::endl;
|
|
if( localSocket.waitForConnected(100) )
|
|
{
|
|
std::cerr << "Rshare::Rshare Connection etablished. Waiting for disconnection." << std::endl;
|
|
localSocket.waitForDisconnected(1000);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "Rshare::Rshare failed to connect to other instance." << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/** Constructor. Parses the command-line arguments, resets Rshare's
|
|
* configuration (if requested), and sets up the GUI style and language
|
|
* translation. */
|
|
Rshare::Rshare(QStringList args, int &argc, char **argv, const QString &dir)
|
|
: QApplication(argc, argv)
|
|
{
|
|
mStartupTime = QDateTime::currentDateTime();
|
|
localServer = NULL;
|
|
|
|
//Initialize connection to LocalServer to know if other process runs.
|
|
{
|
|
QString serverName = QString(TARGET);
|
|
|
|
// check if another instance is running
|
|
bool haveRunningInstance = notifyRunningInstance();
|
|
|
|
bool sendArgsToRunningInstance = haveRunningInstance;
|
|
if(args.empty())
|
|
sendArgsToRunningInstance = false;
|
|
// if we find non-forwardable args, start a new instance
|
|
for(int i = 0; i < args.size(); ++i)
|
|
{
|
|
const char* const* argit = forwardableArgs;
|
|
bool found = false;
|
|
while(*argit && i < args.size())
|
|
{
|
|
if(args.value(i) == "-"+QString(*argit) || args.value(i) == "--"+QString(*argit))
|
|
{
|
|
found = true;
|
|
if(argNeedsValue(*argit))
|
|
i++;
|
|
}
|
|
argit++;
|
|
}
|
|
if(!found)
|
|
sendArgsToRunningInstance = false;
|
|
}
|
|
|
|
if (sendArgsToRunningInstance) {
|
|
// load into shared memory
|
|
QBuffer buffer;
|
|
buffer.open(QBuffer::ReadWrite);
|
|
QDataStream out(&buffer);
|
|
out << args;
|
|
int size = buffer.size();
|
|
|
|
QSharedMemory newArgs;
|
|
newArgs.setKey(serverName + "_newArgs");
|
|
if (newArgs.isAttached()) newArgs.detach();
|
|
|
|
if (!newArgs.create(size)) {
|
|
std::cerr << "(EE) Rshare::Rshare Unable to create shared memory segment of size:"
|
|
<< size << " error:" << newArgs.errorString().toStdString() << "." << std::endl;
|
|
#ifdef Q_OS_UNIX
|
|
std::cerr << "Look with `ipcs -m` for nattch==0 segment. And remove it with `ipcrm -m 'shmid'`." << std::endl;
|
|
//No need for windows, as it removes shared segment directly even when crash.
|
|
#endif
|
|
newArgs.detach();
|
|
::exit(EXIT_FAILURE);
|
|
}
|
|
newArgs.lock();
|
|
char *to = (char*)newArgs.data();
|
|
const char *from = buffer.data().data();
|
|
memcpy(to, from, qMin(newArgs.size(), size));
|
|
newArgs.unlock();
|
|
|
|
std::cerr << "Rshare::Rshare waitForConnected to other instance." << std::endl;
|
|
if(notifyRunningInstance())
|
|
{
|
|
newArgs.detach();
|
|
std::cerr << "Rshare::Rshare Arguments was sended." << std::endl
|
|
<< " To disable it, in Options - General - Misc," << std::endl
|
|
<< " uncheck \"Use Local Server to get new Arguments\"." << std::endl;
|
|
::exit(EXIT_SUCCESS); // Terminate the program using STDLib's exit function
|
|
}
|
|
else
|
|
std::cerr << "Rshare::Rshare failed to connect to other instance." << std::endl;
|
|
newArgs.detach();
|
|
}
|
|
|
|
if(!haveRunningInstance)
|
|
{
|
|
// No main process exists
|
|
// Or started without arguments
|
|
// So we start a Local Server to listen for connections from new process
|
|
localServer= new QLocalServer();
|
|
QObject::connect(localServer, SIGNAL(newConnection()), this, SLOT(slotConnectionEstablished()));
|
|
updateLocalServer();
|
|
}
|
|
}
|
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK (5, 0, 0)
|
|
qInstallMessageHandler(qt_msg_handler);
|
|
#else
|
|
qInstallMsgHandler(qt_msg_handler);
|
|
#endif
|
|
|
|
#ifndef __APPLE__
|
|
|
|
/* set default window icon */
|
|
setWindowIcon(QIcon(":/icons/logo_128.png"));
|
|
|
|
#endif
|
|
|
|
mBlink = true;
|
|
QTimer *timer = new QTimer(this);
|
|
timer->setInterval(500);
|
|
connect(timer, SIGNAL(timeout()), this, SLOT(blinkTimer()));
|
|
timer->start();
|
|
|
|
timer = new QTimer(this);
|
|
timer->setInterval(60000);
|
|
connect(timer, SIGNAL(timeout()), this, SIGNAL(minuteTick()));
|
|
timer->start();
|
|
|
|
/* Read in all our command-line arguments. */
|
|
parseArguments(args);
|
|
|
|
/* Check if we're supposed to reset our config before proceeding. */
|
|
if (_args.contains(ARG_RESET)) {
|
|
Settings->reset();
|
|
}
|
|
|
|
/* Handle the -loglevel and -logfile options. */
|
|
if (_args.contains(ARG_LOGFILE))
|
|
_log.open(_args.value(ARG_LOGFILE));
|
|
if (_args.contains(ARG_LOGLEVEL)) {
|
|
_log.setLogLevel(Log::stringToLogLevel(
|
|
_args.value(ARG_LOGLEVEL)));
|
|
if (!_args.contains(ARG_LOGFILE))
|
|
_log.open(stdout);
|
|
}
|
|
if (!_args.contains(ARG_LOGLEVEL) &&
|
|
!_args.contains(ARG_LOGFILE))
|
|
_log.setLogLevel(Log::Off);
|
|
|
|
/* config directory */
|
|
useConfigDir = false;
|
|
if (dir != "")
|
|
{
|
|
setConfigDirectory(dir);
|
|
}
|
|
|
|
/** Initialize support for language translations. */
|
|
//LanguageSupport::initialize();
|
|
|
|
resetLanguageAndStyle();
|
|
|
|
/* Switch off auto shutdown */
|
|
setQuitOnLastWindowClosed ( false );
|
|
|
|
/* Initialize GxsIdDetails */
|
|
GxsIdDetails::initialize();
|
|
}
|
|
|
|
/** Destructor */
|
|
Rshare::~Rshare()
|
|
{
|
|
/* Cleanup GxsIdDetails */
|
|
GxsIdDetails::cleanup();
|
|
if (localServer)
|
|
{
|
|
localServer->close();
|
|
delete localServer;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Executed when new instance connect command is sent to LocalServer
|
|
*/
|
|
void Rshare::slotConnectionEstablished()
|
|
{
|
|
QLocalSocket *socket = localServer->nextPendingConnection();
|
|
socket->close();
|
|
delete socket;
|
|
|
|
QSharedMemory newArgs;
|
|
newArgs.setKey(QString(TARGET) + "_newArgs");
|
|
|
|
if (!newArgs.attach())
|
|
{
|
|
std::cerr << "(EE) Rshare::slotConnectionEstablished() Unable to attach to shared memory segment."
|
|
<< newArgs.errorString().toStdString() << std::endl;
|
|
return;
|
|
}
|
|
|
|
QBuffer buffer;
|
|
QDataStream in(&buffer);
|
|
QStringList args;
|
|
|
|
newArgs.lock();
|
|
buffer.setData((char*)newArgs.constData(), newArgs.size());
|
|
buffer.open(QBuffer::ReadOnly);
|
|
in >> args;
|
|
newArgs.unlock();
|
|
newArgs.detach();
|
|
|
|
emit newArgsReceived(args);
|
|
while (!args.empty())
|
|
{
|
|
std::cerr << "Rshare::slotConnectionEstablished args:" << QString(args.takeFirst()).toStdString() << std::endl;
|
|
}
|
|
}
|
|
|
|
QString Rshare::retroshareVersion(bool withRevision)
|
|
{
|
|
QString version = QString("%1.%2.%3%4").arg(RS_MAJOR_VERSION).arg(RS_MINOR_VERSION).arg(RS_BUILD_NUMBER).arg(RS_BUILD_NUMBER_ADD);
|
|
if (withRevision) {
|
|
version += QString(" %1 %2").arg(tr("Revision")).arg(QString::number(RS_REVISION_NUMBER,16));
|
|
}
|
|
|
|
return version;
|
|
}
|
|
|
|
/** Enters the main event loop and waits until exit() is called. The signal
|
|
* running() will be emitted when the event loop has started. */
|
|
int
|
|
Rshare::run()
|
|
{
|
|
QTimer::singleShot(0, rApp, SLOT(onEventLoopStarted()));
|
|
return rApp->exec();
|
|
}
|
|
|
|
QDateTime Rshare::startupTime()
|
|
{
|
|
return mStartupTime;
|
|
}
|
|
|
|
/** Called when the application's main event loop has started. This method
|
|
* will emit the running() signal to indicate that the application's event
|
|
* loop is running. */
|
|
void
|
|
Rshare::onEventLoopStarted()
|
|
{
|
|
emit running();
|
|
}
|
|
|
|
/** Display usage information regarding command-line arguments. */
|
|
/*void
|
|
Rshare::printUsage(QString errmsg)
|
|
{
|
|
QTextStream out(stdout);*/
|
|
|
|
/* If there was an error message, print it out. */
|
|
/*if (!errmsg.isEmpty()) {
|
|
out << "** " << errmsg << " **" << endl << endl;
|
|
}*/
|
|
|
|
/* Now print the application usage */
|
|
//out << "Usage: " << endl;
|
|
//out << "\t" << qApp->arguments().at(0) << " [options]" << endl;
|
|
|
|
/* And available options */
|
|
//out << endl << "Available Options:" << endl;
|
|
//out << "\t-"ARG_RESET"\t\tResets ALL stored Rshare settings." << endl;
|
|
//out << "\t-"ARG_DATADIR"\tSets the directory Rshare uses for data files"<< endl;
|
|
//out << "\t-"ARG_GUISTYLE"\t\tSets Rshare's interface style." << endl;
|
|
//out << "\t-"ARG_GUISTYLESHEET"\t\tSets Rshare's stylesheet." << endl;
|
|
//out << "\t\t\t[" << QStyleFactory::keys().join("|") << "]" << endl;
|
|
//out << "\t-"ARG_LANGUAGE"\t\tSets Rshare's language." << endl;
|
|
//out << "\t\t\t[" << LanguageSupport::languageCodes().join("|") << "]" << endl;
|
|
//}
|
|
|
|
/** Displays usage information for command-line args. */
|
|
void
|
|
Rshare::showUsageMessageBox()
|
|
{
|
|
QString usage;
|
|
QTextStream out(&usage);
|
|
|
|
out << "Available Options:" << endl;
|
|
out << "<table>";
|
|
//out << trow(tcol("-"ARG_HELP) +
|
|
// tcol(tr("Displays this usage message and exits.")));
|
|
out << trow(tcol("-" ARG_RESET) +
|
|
tcol(tr("Resets ALL stored RetroShare settings.")));
|
|
out << trow(tcol("-" ARG_DATADIR" <dir>") +
|
|
tcol(tr("Sets the directory RetroShare uses for data files.")));
|
|
out << trow(tcol("-" ARG_LOGFILE" <" + tr("filename") + ">") +
|
|
tcol(tr("Sets the name and location of RetroShare's logfile.")));
|
|
out << trow(tcol("-" ARG_LOGLEVEL" <" + tr("level") + ">") +
|
|
tcol(tr("Sets the verbosity of RetroShare's logging.") +
|
|
"<br>[" + Log::logLevels().join("|") +"]"));
|
|
out << trow(tcol("-" ARG_GUISTYLE" <" + tr("style") +">") +
|
|
tcol(tr("Sets RetroShare's interface style.") +
|
|
"<br>[" + QStyleFactory::keys().join("|") + "]"));
|
|
out << trow(tcol("-" ARG_GUISTYLESHEET" <" + tr("stylesheet") + ">") +
|
|
tcol(tr("Sets RetroShare's interface stylesheets.")));
|
|
out << trow(tcol("-" ARG_LANGUAGE" <" + tr("language") + ">") +
|
|
tcol(tr("Sets RetroShare's language.") +
|
|
"<br>[" + LanguageSupport::languageCodes().join("|") + "]"));
|
|
out << trow(tcol("--" ARG_OPMODE_L" <" + tr("opmode") + ">") +
|
|
tcol(tr("Sets RetroShare's operating mode.") +
|
|
"<br>[full|noturtle|gaming|minimal]"));
|
|
out << trow(tcol("-" ARG_RSLINK_L" <" + tr("RsLinkURL") + ">") +
|
|
tcol(tr("Open RsLink with protocol retroshare://")));
|
|
out << trow(tcol("-" ARG_RSFILE_L" <" + tr("filename") + ">") +
|
|
tcol(tr("Open RsFile with or without arg.")));
|
|
out << "</table>";
|
|
|
|
VMessageBox::information(0,
|
|
tr("RetroShare GUI Usage Information"), usage, VMessageBox::Ok);
|
|
}
|
|
|
|
/** Returns true if the specified argument expects a value. */
|
|
bool
|
|
Rshare::argNeedsValue(QString argName)
|
|
{
|
|
return (
|
|
argName == ARG_DATADIR ||
|
|
argName == ARG_LOGFILE ||
|
|
argName == ARG_LOGLEVEL ||
|
|
argName == ARG_GUISTYLE ||
|
|
argName == ARG_GUISTYLESHEET ||
|
|
argName == ARG_LANGUAGE ||
|
|
argName == ARG_OPMODE_S ||
|
|
argName == ARG_OPMODE_L ||
|
|
argName == ARG_RSLINK_S ||
|
|
argName == ARG_RSLINK_L ||
|
|
argName == ARG_RSFILE_S ||
|
|
argName == ARG_RSFILE_L );
|
|
}
|
|
|
|
/** Parses the list of command-line arguments for their argument names and
|
|
* values. */
|
|
void
|
|
Rshare::parseArguments(QStringList args, bool firstRun)
|
|
{
|
|
QString arg, value;
|
|
|
|
/* Loop through all command-line args/values and put them in a map */
|
|
for (int i = 0; i < args.size(); ++i) {
|
|
/* Get the argument name and set a blank value */
|
|
arg = args.at(i);//.toLower(); Need Upper case for file name.
|
|
if (arg.toLower() == "empty") continue;
|
|
value = "";
|
|
|
|
/* Check if it starts with a - or -- */
|
|
if (arg.startsWith("-")) {
|
|
arg = arg.mid((arg.startsWith("--") ? 2 : 1));
|
|
/* Check if it takes a value and there is one on the command-line */
|
|
if (i < args.size()-1 && argNeedsValue(arg.toLower())) {
|
|
value = args.at(++i);
|
|
}
|
|
} else {
|
|
/* Check if links or files without arg */
|
|
if (arg.toLower().startsWith("retroshare://")) {
|
|
value = arg;
|
|
arg = ARG_RSLINK_L;
|
|
} else {
|
|
if (QFile(arg).exists()) {
|
|
value = arg;
|
|
arg = ARG_RSFILE_L;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* handle opmode that could be change while running.*/
|
|
QString omValue = QString(value).prepend(";").append(";").toLower();
|
|
QString omValues = QString(";full;noturtle;gaming;minimal;");
|
|
if ((arg == ARG_OPMODE_S || arg == ARG_OPMODE_L ) &&
|
|
omValues.contains(omValue)) {
|
|
_opmode = value;
|
|
}
|
|
|
|
/* Don't send theses argument to _args map to allow multiple. */
|
|
if (arg == ARG_RSLINK_S || arg == ARG_RSLINK_L) {
|
|
_links.append(value);
|
|
} else if (arg == ARG_RSFILE_S || arg == ARG_RSFILE_L) {
|
|
_files.append(value);
|
|
} else if (firstRun) {
|
|
/* Place this arg/value in the map only first time*/
|
|
_args.insert(arg, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Verifies that all specified arguments were valid. */
|
|
bool
|
|
Rshare::validateArguments(QString &errmsg)
|
|
{
|
|
/* Check for a writable log file */
|
|
if (_args.contains(ARG_LOGFILE) && !_log.isOpen()) {
|
|
errmsg = tr("Unable to open log file '%1': %2")
|
|
.arg(_args.value(ARG_LOGFILE))
|
|
.arg(_log.errorString());
|
|
return false;
|
|
}
|
|
/* Check for a valid log level */
|
|
if (_args.contains(ARG_LOGLEVEL) &&
|
|
!Log::logLevels().contains(_args.value(ARG_LOGLEVEL))) {
|
|
errmsg = tr("Invalid log level specified:")+" " + _args.value(ARG_LOGLEVEL);
|
|
return false;
|
|
}
|
|
/* Check for a valid GUI style */
|
|
if (_args.contains(ARG_GUISTYLE) &&
|
|
!QStyleFactory::keys().contains(_args.value(ARG_GUISTYLE),
|
|
Qt::CaseInsensitive)) {
|
|
errmsg = tr("Invalid GUI style specified:")+" " + _args.value(ARG_GUISTYLE);
|
|
return false;
|
|
}
|
|
/* Check for a language that Retroshare recognizes. */
|
|
if (_args.contains(ARG_LANGUAGE) &&
|
|
!LanguageSupport::isValidLanguageCode(_args.value(ARG_LANGUAGE))) {
|
|
errmsg = tr("Invalid language code specified:")+" " + _args.value(ARG_LANGUAGE);
|
|
return false;
|
|
}
|
|
/* Check for an opmode that Retroshare recognizes. */
|
|
if (_args.contains(ARG_OPMODE_S) &&
|
|
!QString(";full;noturtle;gaming;minimal;").contains(QString(_args.value(ARG_OPMODE_S)).prepend(";").append(";").toLower())) {
|
|
errmsg = tr("Invalid operating mode specified:")+" " + _args.value(ARG_OPMODE_S);
|
|
return false;
|
|
}
|
|
/* Check for an opmode that Retroshare recognizes. */
|
|
if (_args.contains(ARG_OPMODE_L) &&
|
|
!QString(";full;noturtle;gaming;minimal;").contains(QString(_args.value(ARG_OPMODE_L)).prepend(";").append(";").toLower())) {
|
|
errmsg = tr("Invalid operating mode specified:")+" " + _args.value(ARG_OPMODE_L);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/** Sets the translation RetroShare will use. If one was specified on the
|
|
* command-line, we will use that. Otherwise, we'll check to see if one was
|
|
* saved previously. If not, we'll default to one appropriate for the system
|
|
* locale. */
|
|
bool
|
|
Rshare::setLanguage(QString languageCode)
|
|
{
|
|
/* If the language code is empty, use the previously-saved setting */
|
|
if (languageCode.isEmpty()) {
|
|
languageCode = Settings->getLanguageCode();
|
|
}
|
|
/* Translate into the desired language */
|
|
if (LanguageSupport::translate(languageCode)) {
|
|
_language = languageCode;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/** Sets the locale RetroShare will use. If a language was specified on the
|
|
* command-line, we will use one according to that. Otherwise, we'll check to see if a language was
|
|
* saved previously. If not, we'll default to the system
|
|
* locale. */
|
|
bool
|
|
Rshare::setLocale(QString languageCode)
|
|
{
|
|
bool retVal = false;
|
|
/* If the language code is empty, use the previously-saved setting */
|
|
if (languageCode.isEmpty()) {
|
|
languageCode = Settings->getLanguageCode();
|
|
}
|
|
/* Set desired locale as default locale */
|
|
if (LanguageSupport::localize(languageCode)) {
|
|
retVal=true;
|
|
}
|
|
customizeDateFormat();
|
|
return retVal;
|
|
}
|
|
|
|
/** customize date format for feeds etc. */
|
|
void Rshare::customizeDateFormat()
|
|
{
|
|
QLocale locale = QLocale(); // set to default locale
|
|
/* get long date format without weekday */
|
|
_dateformat = locale.dateFormat(QLocale::LongFormat);
|
|
_dateformat.replace(QRegExp("^dddd,*[^ ]* *('[^']+' )*"), "");
|
|
_dateformat.replace(QRegExp(",* *dddd"), "");
|
|
_dateformat = _dateformat.trimmed();
|
|
}
|
|
|
|
/** Get custom date format (defaultlongformat) */
|
|
QString Rshare::customDateFormat()
|
|
{
|
|
return _dateformat;
|
|
}
|
|
|
|
/** Sets the GUI style RetroShare will use. If one was specified on the
|
|
* command-line, we will use that. Otherwise, we'll check to see if one was
|
|
* saved previously. If not, we'll default to one appropriate for the
|
|
* operating system. */
|
|
bool
|
|
Rshare::setStyle(QString styleKey)
|
|
{
|
|
/* If no style was specified, use the previously-saved setting */
|
|
if (styleKey.isEmpty()) {
|
|
styleKey = Settings->getInterfaceStyle();
|
|
}
|
|
/* Apply the specified GUI style */
|
|
if (QApplication::setStyle(styleKey)) {
|
|
_style = styleKey;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
Rshare::setSheet(QString sheet)
|
|
{
|
|
/* If no stylesheet was specified, use the previously-saved setting */
|
|
if (sheet.isEmpty()) {
|
|
sheet = Settings->getSheetName();
|
|
}
|
|
/* Apply the specified GUI stylesheet */
|
|
_stylesheet = sheet;
|
|
|
|
/* load the StyleSheet*/
|
|
loadStyleSheet(_stylesheet);
|
|
|
|
return true;
|
|
}
|
|
|
|
void Rshare::resetLanguageAndStyle()
|
|
{
|
|
/** Translate the GUI to the appropriate language. */
|
|
setLanguage(_args.value(ARG_LANGUAGE));
|
|
|
|
/** Set the locale appropriately. */
|
|
setLocale(_args.value(ARG_LANGUAGE));
|
|
|
|
/** Set the GUI style appropriately. */
|
|
setStyle(_args.value(ARG_GUISTYLE));
|
|
|
|
/** Set the GUI stylesheet appropriately. */
|
|
setSheet(_args.value(ARG_GUISTYLESHEET));
|
|
}
|
|
|
|
// RetroShare:
|
|
// Default:
|
|
// :/qss/stylesheet/qss.default
|
|
// :/qss/stylesheet/qss.<locale>
|
|
// Internal:
|
|
// :/qss/stylesheet/<name>.qss
|
|
// External:
|
|
// <ConfigDirectory|DataDirectory>/qss/<name>.qss
|
|
// Language depended stylesheet
|
|
// <Internal|External>_<locale>.lqss
|
|
//
|
|
// Plugin:
|
|
// Default:
|
|
// :/qss/stylesheet/<plugin>/<plugin>_qss.default
|
|
// :/qss/stylesheet/<plugin>/<plugin>_qss.<locale>
|
|
// Internal:
|
|
// :/qss/stylesheet/<plugin>/<plugin>_<name>.qss
|
|
// External:
|
|
// <ConfigDirectory|DataDirectory>/qss/<plugin>/<plugin>_<name>.qss
|
|
// Language depended stylesheet
|
|
// <Internal|External>_<locale>.lqss
|
|
|
|
void Rshare::loadStyleSheet(const QString &sheetName)
|
|
{
|
|
QString locale = QLocale().name();
|
|
QString styleSheet;
|
|
|
|
QStringList names;
|
|
names.push_back(""); // RetroShare
|
|
|
|
/* Get stylesheet from plugins */
|
|
if (rsPlugins) {
|
|
int count = rsPlugins->nbPlugins();
|
|
for (int i = 0; i < count; ++i) {
|
|
RsPlugin* plugin = rsPlugins->plugin(i);
|
|
if (plugin) {
|
|
QString pluginStyleSheetName = QString::fromUtf8(plugin->qt_stylesheet().c_str());
|
|
if (!pluginStyleSheetName.isEmpty()) {
|
|
names.push_back(QString("%1/%1_").arg(pluginStyleSheetName));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (QString name, names) {
|
|
/* load the default stylesheet */
|
|
QFile file(QString(":/qss/stylesheet/%1qss.default").arg(name));
|
|
if (file.open(QFile::ReadOnly)) {
|
|
styleSheet += QLatin1String(file.readAll()) + "\n";
|
|
file.close();
|
|
}
|
|
|
|
/* load locale depended default stylesheet */
|
|
file.setFileName(QString(":/qss/stylesheet/%1qss.%2").arg(name, locale));
|
|
if (file.open(QFile::ReadOnly)) {
|
|
styleSheet += QLatin1String(file.readAll()) + "\n";
|
|
file.close();
|
|
}
|
|
|
|
if (!sheetName.isEmpty()) {
|
|
/* load stylesheet */
|
|
if (sheetName.left(1) == ":") {
|
|
/* internal stylesheet */
|
|
file.setFileName(QString(":/qss/stylesheet/%1%2.qss").arg(name, sheetName.mid(1)));
|
|
} else {
|
|
/* external stylesheet */
|
|
file.setFileName(QString("%1/qss/%2%3.qss").arg(QString::fromUtf8(RsAccounts::ConfigDirectory().c_str()), name, sheetName));
|
|
if (!file.exists()) {
|
|
file.setFileName(QString("%1/qss/%2%3.qss").arg(QString::fromUtf8(RsAccounts::DataDirectory().c_str()), name, sheetName));
|
|
}
|
|
}
|
|
if (file.open(QFile::ReadOnly)) {
|
|
styleSheet += QLatin1String(file.readAll()) + "\n";
|
|
file.close();
|
|
|
|
/* load language depended stylesheet */
|
|
QFileInfo fileInfo(file.fileName());
|
|
file.setFileName(fileInfo.path() + "/" + fileInfo.baseName() + "_" + locale + ".lqss");
|
|
if (file.open(QFile::ReadOnly)) {
|
|
styleSheet += QLatin1String(file.readAll()) + "\n";
|
|
file.close();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
qApp->setStyleSheet(styleSheet);
|
|
}
|
|
|
|
/** get list of available stylesheets **/
|
|
void Rshare::getAvailableStyleSheets(QMap<QString, QString> &styleSheets)
|
|
{
|
|
QFileInfoList fileInfoList = QDir(":/qss/stylesheet/").entryInfoList(QStringList("*.qss"));
|
|
QFileInfo fileInfo;
|
|
foreach (fileInfo, fileInfoList) {
|
|
if (fileInfo.isFile()) {
|
|
QString name = fileInfo.baseName();
|
|
styleSheets.insert(QString("%1 (%2)").arg(name, tr("built-in")), ":" + name);
|
|
}
|
|
}
|
|
fileInfoList = QDir(QString::fromUtf8(RsAccounts::ConfigDirectory().c_str()) + "/qss/").entryInfoList(QStringList("*.qss"));
|
|
foreach (fileInfo, fileInfoList) {
|
|
if (fileInfo.isFile()) {
|
|
QString name = fileInfo.baseName();
|
|
styleSheets.insert(name, name);
|
|
}
|
|
}
|
|
fileInfoList = QDir(QString::fromUtf8(RsAccounts::DataDirectory().c_str()) + "/qss/").entryInfoList(QStringList("*.qss"));
|
|
foreach (fileInfo, fileInfoList) {
|
|
if (fileInfo.isFile()) {
|
|
QString name = fileInfo.baseName();
|
|
if (!styleSheets.contains(name)) {
|
|
styleSheets.insert(name, name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Rshare::refreshStyleSheet(QWidget *widget, bool processChildren)
|
|
{
|
|
if (widget != NULL) {
|
|
// force widget to recalculate valid style
|
|
widget->style()->unpolish(widget);
|
|
widget->style()->polish(widget);
|
|
// widget might need to recalculate properties (like margins) depending on style
|
|
QEvent event(QEvent::StyleChange);
|
|
QApplication::sendEvent(widget, &event);
|
|
// repaint widget
|
|
widget->update();
|
|
|
|
if (processChildren == true) {
|
|
// process children recursively
|
|
QObjectList childList = widget->children ();
|
|
for (QObjectList::Iterator it = childList.begin(); it != childList.end(); ++it) {
|
|
QWidget *child = qobject_cast<QWidget*>(*it);
|
|
if (child != NULL) {
|
|
refreshStyleSheet(child, processChildren);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Initialize plugins. */
|
|
void Rshare::initPlugins()
|
|
{
|
|
loadStyleSheet(_stylesheet);
|
|
LanguageSupport::translatePlugins(_language);
|
|
}
|
|
|
|
/** Returns the directory RetroShare uses for its data files. */
|
|
QString
|
|
Rshare::dataDirectory()
|
|
{
|
|
if (useConfigDir)
|
|
{
|
|
return configDir;
|
|
}
|
|
else if (_args.contains(ARG_DATADIR)) {
|
|
return _args.value(ARG_DATADIR);
|
|
}
|
|
return defaultDataDirectory();
|
|
}
|
|
|
|
/** Returns the default location of RetroShare's data directory. */
|
|
QString
|
|
Rshare::defaultDataDirectory()
|
|
{
|
|
#if defined(Q_OS_WIN)
|
|
return (win32_app_data_folder() + "\\RetroShare");
|
|
#else
|
|
return (QDir::homePath() + "/.RetroShare");
|
|
#endif
|
|
}
|
|
|
|
/** Creates Rshare's data directory, if it doesn't already exist. */
|
|
bool
|
|
Rshare::createDataDirectory(QString *errmsg)
|
|
{
|
|
QDir datadir(dataDirectory());
|
|
if (!datadir.exists()) {
|
|
QString path = datadir.absolutePath();
|
|
if (!datadir.mkpath(path)) {
|
|
return err(errmsg,
|
|
tr("Could not create data directory: %1").arg(path));
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/** Set Rshare's data directory - externally */
|
|
bool Rshare::setConfigDirectory(QString dir)
|
|
{
|
|
useConfigDir = true;
|
|
configDir = dir;
|
|
return true;
|
|
}
|
|
|
|
/** Writes <b>msg</b> with severity <b>level</b> to RetroShare's log. */
|
|
Log::LogMessage
|
|
Rshare::log(Log::LogLevel level, QString msg)
|
|
{
|
|
return _log.log(level, msg);
|
|
}
|
|
|
|
/** Creates and binds a shortcut such that when <b>key</b> is pressed in
|
|
* <b>sender</b>'s context, <b>receiver</b>'s <b>slot</b> will be called. */
|
|
void
|
|
Rshare::createShortcut(const QKeySequence &key, QWidget *sender,
|
|
QWidget *receiver, const char *slot)
|
|
{
|
|
QShortcut *s = new QShortcut(key, sender);
|
|
connect(s, SIGNAL(activated()), receiver, slot);
|
|
}
|
|
|
|
#ifdef __APPLE__
|
|
bool Rshare::event(QEvent *event)
|
|
{
|
|
switch (event->type()) {
|
|
case QEvent::FileOpen:{
|
|
QFileOpenEvent* fileOE = static_cast<QFileOpenEvent *>(event);
|
|
QStringList args;
|
|
if (! fileOE->file().isEmpty()) {
|
|
_files.append(fileOE->file());
|
|
emit newArgsReceived(QStringList());
|
|
return true;
|
|
} else if (! fileOE->url().isEmpty()) {
|
|
_links.append(fileOE->url().toString());
|
|
emit newArgsReceived(QStringList());
|
|
return true;
|
|
}
|
|
}
|
|
default:
|
|
return QApplication::event(event);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void Rshare::blinkTimer()
|
|
{
|
|
mBlink = !mBlink;
|
|
emit blink(mBlink);
|
|
|
|
if (mBlink) {
|
|
/* Tick every second (create an own timer when needed) */
|
|
emit secondTick();
|
|
}
|
|
}
|
|
|
|
bool Rshare::loadCertificate(const RsPeerId &accountId, bool autoLogin)
|
|
{
|
|
if (!RsAccounts::SelectAccount(accountId))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
std::string lockFile;
|
|
int retVal = RsInit::LockAndLoadCertificates(autoLogin, lockFile);
|
|
switch (retVal) {
|
|
case 0: break;
|
|
case 1: QMessageBox::warning( 0,
|
|
QObject::tr("Multiple instances"),
|
|
QObject::tr("Another RetroShare using the same profile is "
|
|
"already running on your system. Please close "
|
|
"that instance first\n Lock file:\n") +
|
|
QString::fromUtf8(lockFile.c_str()));
|
|
return false;
|
|
case 2: QMessageBox::critical( 0,
|
|
QObject::tr("Multiple instances"),
|
|
QObject::tr("An unexpected error occurred when Retroshare "
|
|
"tried to acquire the single instance lock\n Lock file:\n") +
|
|
QString::fromUtf8(lockFile.c_str()));
|
|
return false;
|
|
case 3:
|
|
// case 3: QMessageBox::critical( 0,
|
|
// QObject::tr("Login Failure"),
|
|
// QObject::tr("Maybe password is wrong") );
|
|
return false;
|
|
default: std::cerr << "Rshare::loadCertificate() unexpected switch value " << retVal << std::endl;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Rshare::updateLocalServer()
|
|
{
|
|
if (localServer) {
|
|
QString serverName = QString(TARGET);
|
|
if (Settings->getUseLocalServer()) {
|
|
localServer->removeServer(serverName);
|
|
localServer->listen(serverName);
|
|
return true;
|
|
}
|
|
localServer->close();
|
|
}
|
|
return false;
|
|
}
|