Commit c5897d0e authored by David Jarvie's avatar David Jarvie

Bug 374520: Fix not showing main window if re-activated

If another instance of KAlarm was activated while already running with
--tray, the main window was not displayed. This made it impossible to
show the main window if the system tray icon was inaccessible for any
reason.

Also fix --help, --version and option errors not being reported on the
terminal if another instance of KAlarm is already running.
parent 422a3e47
KAlarm Change Log
=== Version 2.11.13 --- 29 January 2017 ===
=== Version 2.11.13 --- 8 February 2017 ===
+ Fix system tray icon used for "some alarms disabled"
+ Improved system tray icons (requires Plasma 5.9) [KDE Bug 362631]
+ Don't show misleading "Failed to update alarm" if command alarm fails [KDE Bug 375615]
+ Fix not showing main window if activated again while already running with --tray [Bug 374520]
+ Fix --help, --version and option errors not being reported if KAlarm is already running.
=== Version 2.11.12 (KDE Applications 16.12.1) --- 1 January 2017 ===
+ Fix Export Alarms file save error [KDE Bug 374337]
......
/*
* commandoptions.cpp - extract command line options
* Program: kalarm
* Copyright © 2001-2016 by David Jarvie <djarvie@kde.org>
* Copyright © 2001-2017 by David Jarvie <djarvie@kde.org>
*
* 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
......@@ -38,15 +38,27 @@ namespace
bool convInterval(const QString& timeParam, KARecurrence::Type&, int& timeInterval, bool allowMonthYear = false);
}
CommandOptions* CommandOptions::mInstance = Q_NULLPTR;
QCommandLineParser* CommandOptions::mParser = Q_NULLPTR;
QVector<QCommandLineOption*> CommandOptions::mOptions(Num_Options, Q_NULLPTR);
QStringList CommandOptions::mExecArguments;
CommandOptions* CommandOptions::mFirstInstance = Q_NULLPTR;
void CommandOptions::setError(const QString& error)
CommandOptions::CommandOptions()
: mParser(Q_NULLPTR),
mOptions(Num_Options, Q_NULLPTR),
mCommand(NONE),
mEditActionSet(false),
mRecurrence(Q_NULLPTR),
mRepeatCount(0),
mRepeatInterval(0),
mLateCancel(0),
mBgColour(Preferences::defaultBgColour()),
mFgColour(Preferences::defaultFgColour()),
mReminderMinutes(0),
mAudioVolume(-1),
mFromID(0),
mFlags(KAEvent::DEFAULT_FONT),
mDisableAll(false)
{
if (mError.isEmpty())
mError = error;
if (!mFirstInstance)
mFirstInstance = this;
}
/******************************************************************************
......@@ -221,13 +233,13 @@ QStringList CommandOptions::setOptions(QCommandLineParser* parser, const QString
QStringLiteral("[message]"));
// Check for any options which eat up all following arguments.
QStringList arguments;
mNonExecArguments.clear();
for (int i = 0; i < args.size(); ++i)
{
const QString arg = args[i];
if (arg == QStringLiteral("--nofork"))
continue; // Ignore debugging option
arguments << arg;
mNonExecArguments << arg;
if (arg == optionName(EXEC) || arg == optionName(EXEC, true)
|| arg == optionName(EXEC_DISPLAY) || arg == optionName(EXEC_DISPLAY, true))
{
......@@ -239,31 +251,35 @@ QStringList CommandOptions::setOptions(QCommandLineParser* parser, const QString
mExecArguments << args[i];
}
}
qCDebug(KALARM_LOG) << arguments;
return arguments;
return mNonExecArguments;
}
void CommandOptions::process()
void CommandOptions::parse()
{
if (!mInstance)
mInstance = new CommandOptions();
if (!mParser->parse(mNonExecArguments))
{
setError(mParser->errorText());
return;
}
if (mParser->isSet(QStringLiteral("help")))
{
mCommand = EXIT;
mError = mParser->helpText();
return;
}
if (mParser->isSet(QStringLiteral("version")))
{
mCommand = EXIT;
mError = QCoreApplication::applicationName() + QStringLiteral(" ") + QCoreApplication::applicationVersion();
return;
}
}
CommandOptions::CommandOptions()
: mCommand(NONE),
mEditActionSet(false),
mRecurrence(Q_NULLPTR),
mRepeatCount(0),
mRepeatInterval(0),
mLateCancel(0),
mBgColour(Preferences::defaultBgColour()),
mFgColour(Preferences::defaultFgColour()),
mReminderMinutes(0),
mAudioVolume(-1),
mFromID(0),
mFlags(KAEvent::DEFAULT_FONT),
mDisableAll(false)
void CommandOptions::process()
{
if (mCommand == CMD_ERROR || mCommand == EXIT)
return;
#ifndef NDEBUG
if (mParser->isSet(*mOptions[TEST_SET_TIME]))
{
......@@ -684,15 +700,19 @@ CommandOptions::CommandOptions()
}
if (!mError.isEmpty())
{
printError(mError);
mCommand = CMD_ERROR;
}
setError(mError);
}
void CommandOptions::setError(const QString& errmsg)
{
qCWarning(KALARM_LOG) << errmsg;
mCommand = CMD_ERROR;
if (mError.isEmpty())
mError = errmsg + i18nc("@info:shell", "\nUse --help to get a list of available command line options.\n");
}
void CommandOptions::printError(const QString& errmsg)
{
qCDebug(KALARM_LOG)<<"ERROR=====================";
// Note: we can't use mArgs->usage() since that also quits any other
// running 'instances' of the program.
std::cerr << errmsg.toLocal8Bit().data()
......@@ -754,7 +774,7 @@ QString CommandOptions::arg(int n)
return (n < args.size()) ? args[n] : QString();
}
QString CommandOptions::optionName(Option opt, bool shortName)
QString CommandOptions::optionName(Option opt, bool shortName) const
{
if (opt == Opt_Message)
return QStringLiteral("message");
......
/*
* commandoptions.h - extract command line options
* Program: kalarm
* Copyright © 2001-2016 by David Jarvie <djarvie@kde.org>
* Copyright © 2001-2017 by David Jarvie <djarvie@kde.org>
*
* 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
......@@ -43,6 +43,7 @@ class CommandOptions
{
CMD_ERROR, // error in command line options
NONE, // no command
EXIT, // print outputText() and exit
TRAY, // --tray
TRIGGER_EVENT, // --triggerEvent
CANCEL_EVENT, // --cancelEvent
......@@ -52,9 +53,11 @@ class CommandOptions
NEW, // --file, --exec-display, --exec, --mail, message
LIST // --list
};
static QStringList setOptions(QCommandLineParser*, const QStringList& args);
static void process();
static CommandOptions* instance() { return mInstance; }
QStringList setOptions(QCommandLineParser*, const QStringList& args);
static CommandOptions* firstInstance() { return mFirstInstance; }
CommandOptions();
void parse();
void process();
Command command() const { return mCommand; }
QString commandName() const { return optionName(mCommandOpt); }
EventId eventId() const { return mEventId; }
......@@ -78,6 +81,7 @@ class CommandOptions
uint fromID() const { return mFromID; }
KAEvent::Flags flags() const { return mFlags; }
bool disableAll() const { return mDisableAll; }
QString outputText() const { return mError; }
#ifndef NDEBUG
KDateTime simulationTime() const { return mSimulationTime; }
#endif
......@@ -132,22 +136,22 @@ class CommandOptions
Opt_Message // special value representing "message"
};
explicit CommandOptions();
bool checkCommand(Option, Command, EditAlarmDlg::Type = EditAlarmDlg::NO_TYPE);
inline void setError(const QString& error);
void setError(const QString& error);
void setErrorRequires(Option opt, Option opt2, Option opt3 = Num_Options);
void setErrorParameter(Option);
void setErrorIncompatible(Option opt1, Option opt2);
void checkEditType(EditAlarmDlg::Type type, Option opt)
{ checkEditType(type, EditAlarmDlg::NO_TYPE, opt); }
void checkEditType(EditAlarmDlg::Type, EditAlarmDlg::Type, Option);
static QString arg(int n);
static QString optionName(Option, bool shortName = false);
QString arg(int n);
QString optionName(Option, bool shortName = false) const;
static CommandOptions* mInstance; // the unique instance
static QCommandLineParser* mParser;
static QVector<QCommandLineOption*> mOptions; // all possible command line options
static QStringList mExecArguments; // arguments for --exec or --exec-display
static CommandOptions* mFirstInstance; // the first instance
QCommandLineParser* mParser;
QVector<QCommandLineOption*> mOptions; // all possible command line options
QStringList mNonExecArguments; // arguments except for --exec or --exec-display parameters
QStringList mExecArguments; // arguments for --exec or --exec-display
QString mError; // error message
Command mCommand; // the selected command
Option mCommandOpt; // option for the selected command
......
......@@ -122,11 +122,6 @@ KAlarmApp::KAlarmApp(int& argc, char** argv)
KAlarm::setTestModeConditions();
#endif
// Make this a unique application.
KDBusService* s = new KDBusService(KDBusService::Unique, this);
connect(this, &KAlarmApp::aboutToQuit, s, &KDBusService::deleteLater);
connect(s, &KDBusService::activateRequested, this, &KAlarmApp::activate);
setQuitOnLastWindowClosed(false);
Preferences::self(); // read KAlarm configuration
if (!Preferences::noAutoStart())
......@@ -324,16 +319,29 @@ bool KAlarmApp::restoreSession()
/******************************************************************************
* Called for a unique QApplication when a new instance of the application is
* started.
* Reply: exit code (>= 0), or -1 to continue execution.
* If exit code >= 0, 'outputText' holds text to output before terminating.
*/
void KAlarmApp::activate(const QStringList& args, const QString& workingDirectory)
void KAlarmApp::activateByDBus(const QStringList& args, const QString& workingDirectory)
{
activateInstance(args, workingDirectory, nullptr);
}
/******************************************************************************
* Called to start a new instance of the application.
* Reply: exit code (>= 0), or -1 to continue execution.
* If exit code >= 0, 'outputText' holds text to output before terminating.
*/
int KAlarmApp::activateInstance(const QStringList& args, const QString& workingDirectory, QString* outputText)
{
Q_UNUSED(workingDirectory)
qCDebug(KALARM_LOG);
if (outputText)
outputText->clear();
if (mFatalError)
{
quitFatal();
// return 1;
return;
return 1;
}
// The D-Bus call to activate a subsequent instance of KAlarm may not supply
......@@ -348,25 +356,65 @@ void KAlarmApp::activate(const QStringList& args, const QString& workingDirector
QCommandLineParser parser;
KAboutData::applicationData().setupCommandLine(&parser);
parser.setApplicationDescription(QApplication::applicationDisplayName());
const QStringList newArgs = CommandOptions::setOptions(&parser, fixedArgs);
parser.process(newArgs);
CommandOptions* options = new CommandOptions;
const QStringList newArgs = options->setOptions(&parser, fixedArgs);
options->parse();
KAboutData::applicationData().processCommandLine(&parser);
++mActiveCount;
int exitCode = 0; // default = success
static bool firstInstance = true;
bool dontRedisplay = false;
if (!firstInstance || !isSessionRestored())
CommandOptions::Command command = CommandOptions::NONE;
bool processOptions = (!firstInstance || !isSessionRestored());
if (processOptions)
{
CommandOptions::process();
CommandOptions* options = CommandOptions::instance(); // fetch command line options
options->process();
#ifndef NDEBUG
if (options->simulationTime().isValid())
KAlarm::setSimulatedSystemTime(options->simulationTime());
#endif
CommandOptions::Command command = options->command();
command = options->command();
if (options->disableAll())
setAlarmsEnabled(false); // disable alarm monitoring
// Handle options which exit with a terminal message, before
// making the application a unique application, since a
// unique application won't output to the terminal if another
// instance is already running.
switch (command)
{
case CommandOptions::CMD_ERROR:
if (outputText)
{
*outputText = options->outputText();
delete options;
return 1;
}
mReadOnly = true; // don't need write access to calendars
exitCode = 1;
break;
case CommandOptions::EXIT:
if (outputText)
{
*outputText = options->outputText();
delete options;
return 0;
}
exitCode = -1;
break;
default:
break;
}
}
// Make this a unique application.
KDBusService* s = new KDBusService(KDBusService::Unique, this);
connect(this, &KAlarmApp::aboutToQuit, s, &KDBusService::deleteLater);
connect(s, &KDBusService::activateRequested, this, &KAlarmApp::activateByDBus);
if (processOptions)
{
switch (command)
{
case CommandOptions::TRIGGER_EVENT:
......@@ -537,19 +585,23 @@ void KAlarmApp::activate(const QStringList& args, const QString& workingDirector
exitCode = 1;
else
{
MainWindow* win = MainWindow::create();
if (command == CommandOptions::TRAY)
win->setWindowState(win->windowState() | Qt::WindowMinimized);
win->show();
if (mTrayWindow && mTrayWindow->assocMainWindow() && !mTrayWindow->assocMainWindow()->isVisible())
mTrayWindow->showAssocMainWindow();
else
{
MainWindow* win = MainWindow::create();
if (command == CommandOptions::TRAY)
win->setWindowState(win->windowState() | Qt::WindowMinimized);
win->show();
}
}
break;
case CommandOptions::CMD_ERROR:
mReadOnly = true; // don't need write access to calendars
exitCode = 1;
default:
break;
}
}
if (options != CommandOptions::firstInstance())
delete options;
// If this is the first time through, redisplay any alarm message windows
// from last time.
......@@ -579,12 +631,12 @@ void KAlarmApp::activate(const QStringList& args, const QString& workingDirector
// Quit the application if this was the last/only running "instance" of the program.
// Executing 'return' doesn't work very well since the program continues to
// run if no windows were created.
quitIf(exitCode);
quitIf(exitCode >= 0 ? exitCode : 0);
// Check whether the KDE time zone daemon is running (but don't hold up initialisation)
QTimer::singleShot(0, this, &KAlarmApp::checkKtimezoned);
// return exitCode;
return -1; // continue executing the application instance
}
void KAlarmApp::checkKtimezoned()
......
......@@ -56,6 +56,8 @@ class KAlarmApp : public QApplication
bool wantShowInSystemTray() const;
bool alarmsEnabled() const { return mAlarmsEnabled; }
bool korganizerEnabled() const { return mKOrganizerEnabled; }
int activate(const QStringList& args, const QString& workingDirectory, QString& outputText)
{ return activateInstance(args, workingDirectory, &outputText); }
bool restoreSession();
bool quitIf() { return quitIf(0); }
void doQuit(QWidget* parent);
......@@ -92,7 +94,7 @@ class KAlarmApp : public QApplication
QString dbusList();
public Q_SLOTS:
void activate(const QStringList& args, const QString& workingDirectory);
void activateByDBus(const QStringList& args, const QString& workingDirectory);
void processQueue();
void setAlarmsEnabled(bool);
void purgeNewArchivedDefault(const Akonadi::Collection&);
......@@ -168,6 +170,7 @@ class KAlarmApp : public QApplication
KAlarmApp(int& argc, char** argv);
bool initialise();
int activateInstance(const QStringList& args, const QString& workingDirectory, QString* outputText);
bool initCheck(bool calendarOnly = false, bool waitForCollection = false, Akonadi::Collection::Id = -1);
bool quitIf(int exitCode, bool force = false);
bool checkSystemTray();
......
......@@ -31,6 +31,7 @@
#include <QDir>
#include <QScopedPointer>
#include <iostream>
#include <stdlib.h>
#define PROGRAM_NAME "kalarm"
......@@ -41,7 +42,7 @@ int main(int argc, char* argv[])
// before libraries unload, to avoid crashes during clean-up.
QScopedPointer<KAlarmApp> app(KAlarmApp::create(argc, argv));
QStringList args = app->arguments();
const QStringList args = app->arguments();
app->setAttribute(Qt::AA_UseHighDpiPixmaps, true);
app->setAttribute(Qt::AA_EnableHighDpiScaling);
......@@ -58,7 +59,17 @@ int main(int argc, char* argv[])
qCDebug(KALARM_LOG) << "initialising";
app->activate(args, QDir::currentPath());
QString outputText;
int exitCode = app->activate(args, QDir::currentPath(), outputText);
if (exitCode >= 0)
{
if (exitCode > 0)
std::cout << qPrintable(outputText) << std::endl;
else
std::cerr << qPrintable(outputText) << std::endl;
exit(exitCode);
}
app->restoreSession();
return app->exec();
}
......
......@@ -232,6 +232,19 @@ void TrayWindow::slotHaveDisabledAlarms(bool haveDisabled)
updateToolTip();
}
/******************************************************************************
* Show the associated main window.
*/
void TrayWindow::showAssocMainWindow()
{
if (mAssocMainWindow)
{
mAssocMainWindow->show();
mAssocMainWindow->raise();
mAssocMainWindow->activateWindow();
}
}
/******************************************************************************
* A left click displays the KAlarm main window.
*/
......
/*
* traywindow.h - the KDE system tray applet
* Program: kalarm
* Copyright © 2002-2012 by David Jarvie <djarvie@kde.org>
* Copyright © 2002-2017 by David Jarvie <djarvie@kde.org>
*
* 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
......@@ -45,6 +45,7 @@ class TrayWindow : public KStatusNotifierItem
void removeWindow(MainWindow*);
MainWindow* assocMainWindow() const { return mAssocMainWindow; }
void setAssocMainWindow(MainWindow* win) { mAssocMainWindow = win; }
void showAssocMainWindow();
Q_SIGNALS:
void deleted();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment