mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2025-12-17 12:40:35 +01:00
Replace qetsingleapplication by singleApplication.
See : https://github.com/itay-grudev/SingleApplication git-svn-id: svn+ssh://svn.tuxfamily.org/svnroot/qet/qet/trunk@5481 bfdf4180-ca20-0410-9c96-a3a8aa849046
This commit is contained in:
180
SingleApplication/CHANGELOG.md
Normal file
180
SingleApplication/CHANGELOG.md
Normal file
@@ -0,0 +1,180 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
__3.0.12a__
|
||||
----------
|
||||
|
||||
* Removed signal handling.
|
||||
|
||||
__3.0.11a__
|
||||
----------
|
||||
|
||||
* Fixed bug where the message sent by the second process was not received
|
||||
correctly when the message is sent immediately following a connection.
|
||||
|
||||
_Francis Giraldeau_
|
||||
|
||||
* Refactored code and implemented shared memory block consistency checks
|
||||
via `qChecksum()` (CRC-16).
|
||||
* Explicit `qWarning` and `qCritical` when the library is unable to initialise
|
||||
correctly.
|
||||
|
||||
__3.0.10__
|
||||
----------
|
||||
|
||||
* Removed C style casts and eliminated all clang warnings. Fixed `instanceId`
|
||||
reading from only one byte in the message deserialization. Cleaned up
|
||||
serialization code using `QDataStream`. Changed connection type to use
|
||||
`quint8 enum` rather than `char`.
|
||||
* Renamed `SingleAppConnectionType` to `ConnectionType`. Added initialization
|
||||
values to all `ConnectionType` enum cases.
|
||||
|
||||
_Jedidiah Buck McCready_
|
||||
|
||||
__3.0.9__
|
||||
---------
|
||||
|
||||
* Added SingleApplicationPrivate::primaryPid() as a solution to allow
|
||||
bringing the primary window of an application to the foreground on
|
||||
Windows.
|
||||
|
||||
_Eelco van Dam from Peacs BV_
|
||||
|
||||
__3.0.8__
|
||||
---------
|
||||
|
||||
* Bug fix - changed QApplication::instance() to QCoreApplication::instance()
|
||||
|
||||
_Evgeniy Bazhenov_
|
||||
|
||||
__3.0.7a__
|
||||
----------
|
||||
|
||||
* Fixed compilation error with Mingw32 in MXE thanks to Vitaly Tonkacheyev.
|
||||
* Removed QMutex used for thread safe behaviour. The implementation now uses
|
||||
QCoreApplication::instance() to get an instance to SingleApplication for
|
||||
memory deallocation.
|
||||
|
||||
__3.0.6a__
|
||||
----------
|
||||
|
||||
* Reverted GetUserName API usage on Windows. Fixed bug with missing library.
|
||||
* Fixed bug in the Calculator example, preventing it's window to be raised
|
||||
on Windows.
|
||||
|
||||
Special thanks to Charles Gunawan.
|
||||
|
||||
__3.0.5a__
|
||||
----------
|
||||
|
||||
* Fixed a memory leak in the SingleApplicationPrivate destructor.
|
||||
|
||||
_Sergei Moiseev_
|
||||
|
||||
__3.0.4a__
|
||||
----------
|
||||
|
||||
* Fixed shadow and uninitialised variable warnings.
|
||||
|
||||
_Paul Walmsley_
|
||||
|
||||
__3.0.3a__
|
||||
----------
|
||||
|
||||
* Removed Microsoft Windows specific code for getting username due to
|
||||
multiple problems and compiler differences on Windows platforms. On
|
||||
Windows the shared memory block in User mode now includes the user's
|
||||
home path (which contains the user's username).
|
||||
|
||||
* Explicitly getting absolute path of the user's home directory as on Unix
|
||||
a relative path (`~`) may be returned.
|
||||
|
||||
__3.0.2a__
|
||||
----------
|
||||
|
||||
* Fixed bug on Windows when username containing wide characters causes the
|
||||
library to crash.
|
||||
|
||||
_Le Liu_
|
||||
|
||||
__3.0.1a__
|
||||
----------
|
||||
|
||||
* Allows the application path and version to be excluded from the server name
|
||||
hash. The following flags were added for this purpose:
|
||||
* `SingleApplication::Mode::ExcludeAppVersion`
|
||||
* `SingleApplication::Mode::ExcludeAppPath`
|
||||
* Allow a non elevated process to connect to a local server created by an
|
||||
elevated process run by the same user on Windows
|
||||
* Fixes a problem with upper case letters in paths on Windows
|
||||
|
||||
_Le Liu_
|
||||
|
||||
__v3.0a__
|
||||
---------
|
||||
|
||||
* Depricated secondary instances count.
|
||||
* Added a sendMessage() method to send a message to the primary instance.
|
||||
* Added a receivedMessage() signal, emitted when a message is received from a
|
||||
secondary instance.
|
||||
* The SingleApplication constructor's third parameter is now a bool
|
||||
specifying if the current instance should be allowed to run as a secondary
|
||||
instance if there is already a primary instance.
|
||||
* The SingleApplication constructor accept a fourth parameter specifying if
|
||||
the SingleApplication block should be User-wide or System-wide.
|
||||
* SingleApplication no longer relies on `applicationName` and
|
||||
`organizationName` to be set. It instead concatenates all of the following
|
||||
data and computes a `SHA256` hash which is used as the key of the
|
||||
`QSharedMemory` block and the `QLocalServer`. Since at least
|
||||
`applicationFilePath` is always present there is no need to explicitly set
|
||||
any of the following prior to initialising `SingleApplication`.
|
||||
* `QCoreApplication::applicationName`
|
||||
* `QCoreApplication::applicationVersion`
|
||||
* `QCoreApplication::applicationFilePath`
|
||||
* `QCoreApplication::organizationName`
|
||||
* `QCoreApplication::organizationDomain`
|
||||
* User name or home directory path if in User mode
|
||||
* The primary instance is no longer notified when a secondary instance had
|
||||
been started by default. A `Mode` flag for this feature exists.
|
||||
* Added `instanceNumber()` which represents a unique identifier for each
|
||||
secondary instance started. When called from the primary instance will
|
||||
return `0`.
|
||||
|
||||
__v2.4__
|
||||
--------
|
||||
|
||||
* Stability improvements
|
||||
* Support for secondary instances.
|
||||
* The library now recovers safely after the primary process has crashed
|
||||
and the shared memory had not been deleted.
|
||||
|
||||
__v2.3__
|
||||
--------
|
||||
|
||||
* Improved pimpl design and inheritance safety.
|
||||
|
||||
_Vladislav Pyatnichenko_
|
||||
|
||||
__v2.2__
|
||||
--------
|
||||
|
||||
* The `QAPPLICATION_CLASS` macro can now be defined in the file including the
|
||||
Single Application header or with a `DEFINES+=` statement in the project file.
|
||||
|
||||
__v2.1__
|
||||
--------
|
||||
|
||||
* A race condition can no longer occur when starting two processes nearly
|
||||
simultaneously.
|
||||
|
||||
Fix issue [#3](https://github.com/itay-grudev/SingleApplication/issues/3)
|
||||
|
||||
__v2.0__
|
||||
--------
|
||||
|
||||
* SingleApplication is now being passed a reference to `argc` instead of a
|
||||
copy.
|
||||
|
||||
Fix issue [#1](https://github.com/itay-grudev/SingleApplication/issues/1)
|
||||
|
||||
* Improved documentation.
|
||||
24
SingleApplication/LICENSE
Normal file
24
SingleApplication/LICENSE
Normal file
@@ -0,0 +1,24 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Itay Grudev 2015 - 2016
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
Note: Some of the examples include code not distributed under the terms of the
|
||||
MIT License.
|
||||
265
SingleApplication/README.md
Normal file
265
SingleApplication/README.md
Normal file
@@ -0,0 +1,265 @@
|
||||
SingleApplication
|
||||
=================
|
||||
|
||||
This is a replacement of the QtSingleApplication for `Qt5`.
|
||||
|
||||
Keeps the Primary Instance of your Application and kills each subsequent
|
||||
instances. It can (if enabled) spawn secondary (non-related to the primary)
|
||||
instances and can send data to the primary instance from secondary instances.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
The `SingleApplication` class inherits from whatever `Q[Core|Gui]Application`
|
||||
class you specify via the `QAPPLICATION_CLASS` macro (`QCoreApplication` is the
|
||||
default). Further usage is similar to the use of the `Q[Core|Gui]Application`
|
||||
classes.
|
||||
|
||||
The library sets up a `QLocalServer` and a `QSharedMemory` block. The first
|
||||
instance of your Application is your Primary Instance. It would check if the
|
||||
shared memory block exists and if not it will start a `QLocalServer` and listen
|
||||
for connections. Each subsequent instance of your application would check if the
|
||||
shared memory block exists and if it does, it will connect to the QLocalServer
|
||||
to notify the primary instance that a new instance had been started, after which
|
||||
it would terminate with status code `0`. In the Primary Instance
|
||||
`SingleApplication` would emit the `instanceStarted()` signal upon detecting
|
||||
that a new instance had been started.
|
||||
|
||||
The library uses `stdlib` to terminate the program with the `exit()` function.
|
||||
|
||||
You can use the library as if you use any other `QCoreApplication` derived
|
||||
class:
|
||||
|
||||
```cpp
|
||||
#include <QApplication>
|
||||
#include <SingleApplication.h>
|
||||
|
||||
int main( int argc, char* argv[] )
|
||||
{
|
||||
SingleApplication app( argc, argv );
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
```
|
||||
|
||||
To include the library files I would recommend that you add it as a git
|
||||
submodule to your project and include it's contents with a `.pri` file. Here is
|
||||
how:
|
||||
|
||||
```bash
|
||||
git submodule add git@github.com:itay-grudev/SingleApplication.git singleapplication
|
||||
```
|
||||
|
||||
Then include the `singleapplication.pri` file in your `.pro` project file. Also
|
||||
don't forget to specify which `QCoreApplication` class your app is using if it
|
||||
is not `QCoreApplication`.
|
||||
|
||||
```qmake
|
||||
include(singleapplication/singleapplication.pri)
|
||||
DEFINES += QAPPLICATION_CLASS=QApplication
|
||||
```
|
||||
|
||||
The `Instance Started` signal
|
||||
------------------------
|
||||
|
||||
The SingleApplication class implements a `instanceStarted()` signal. You can
|
||||
bind to that signal to raise your application's window when a new instance had
|
||||
been started, for example.
|
||||
|
||||
```cpp
|
||||
// window is a QWindow instance
|
||||
QObject::connect(
|
||||
&app,
|
||||
&SingleApplication::instanceStarted,
|
||||
&window,
|
||||
&QWindow::raise
|
||||
);
|
||||
```
|
||||
|
||||
Using `SingleApplication::instance()` is a neat way to get the
|
||||
`SingleApplication` instance for binding to it's signals anywhere in your
|
||||
program.
|
||||
|
||||
__Note:__ On Windows the ability to bring the application windows to the
|
||||
foreground is restricted. See [Windows specific implementations](Windows.md)
|
||||
for a workaround and an example implementation.
|
||||
|
||||
|
||||
Secondary Instances
|
||||
-------------------
|
||||
|
||||
If you want to be able to launch additional Secondary Instances (not related to
|
||||
your Primary Instance) you have to enable that with the third parameter of the
|
||||
`SingleApplication` constructor. The default is `false` meaning no Secondary
|
||||
Instances. Here is an example of how you would start a Secondary Instance send
|
||||
a message with the command line arguments to the primary instance and then shut
|
||||
down.
|
||||
|
||||
```cpp
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
SingleApplication app( argc, argv, true );
|
||||
|
||||
if( app.isSecondary() ) {
|
||||
app.sendMessage( app.arguments().join(' ')).toUtf8() );
|
||||
app.exit( 0 );
|
||||
}
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
```
|
||||
|
||||
*__Note:__ A secondary instance won't cause the emission of the
|
||||
`instanceStarted()` signal by default. See `SingleApplication::Mode` for more
|
||||
details.*
|
||||
|
||||
You can check whether your instance is a primary or secondary with the following
|
||||
methods:
|
||||
|
||||
```cpp
|
||||
app.isPrimary();
|
||||
// or
|
||||
app.isSecondary();
|
||||
```
|
||||
|
||||
*__Note:__ If your Primary Instance is terminated a newly launched instance
|
||||
will replace the Primary one even if the Secondary flag has been set.*
|
||||
|
||||
API
|
||||
---
|
||||
|
||||
### Members
|
||||
|
||||
```cpp
|
||||
SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 100 )
|
||||
```
|
||||
|
||||
Depending on whether `allowSecondary` is set, this constructor may terminate
|
||||
your app if there is already a primary instance running. Additional `Options`
|
||||
can be specified to set whether the SingleApplication block should work
|
||||
user-wide or system-wide. Additionally the `Mode::SecondaryNotification` may be
|
||||
used to notify the primary instance whenever a secondary instance had been
|
||||
started (disabled by default). `timeout` specifies the maximum time in
|
||||
milliseconds to wait for blocking operations.
|
||||
|
||||
*__Note:__ `argc` and `argv` may be changed as Qt removes arguments that it
|
||||
recognizes.*
|
||||
|
||||
*__Note:__ `Mode::SecondaryNotification` only works if set on both the primary
|
||||
and the secondary instance.*
|
||||
|
||||
*__Note:__ Operating system can restrict the shared memory blocks to the same
|
||||
user, in which case the User/System modes will have no effect and the block will
|
||||
be user wide.*
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
bool SingleApplication::sendMessage( QByteArray message, int timeout = 100 )
|
||||
```
|
||||
|
||||
Sends `message` to the Primary Instance. Uses `timeout` as a the maximum timeout
|
||||
in milliseconds for blocking functions
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
bool SingleApplication::isPrimary()
|
||||
```
|
||||
|
||||
Returns if the instance is the primary instance.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
bool SingleApplication::isSecondary()
|
||||
```
|
||||
Returns if the instance is a secondary instance.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
quint32 SingleApplication::instanceId()
|
||||
```
|
||||
|
||||
Returns a unique identifier for the current instance.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
qint64 SingleApplication::primaryPid()
|
||||
```
|
||||
|
||||
Returns the process ID (PID) of the primary instance.
|
||||
|
||||
### Signals
|
||||
|
||||
```cpp
|
||||
void SingleApplication::instanceStarted()
|
||||
```
|
||||
|
||||
Triggered whenever a new instance had been started, except for secondary
|
||||
instances if the `Mode::SecondaryNotification` flag is not specified.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
void SingleApplication::receivedMessage( quint32 instanceId, QByteArray message )
|
||||
```
|
||||
|
||||
Triggered whenever there is a message received from a secondary instance.
|
||||
|
||||
---
|
||||
|
||||
### Flags
|
||||
|
||||
```cpp
|
||||
enum SingleApplication::Mode
|
||||
```
|
||||
|
||||
* `Mode::User` - The SingleApplication block should apply user wide. This adds
|
||||
user specific data to the key used for the shared memory and server name.
|
||||
This is the default functionality.
|
||||
* `Mode::System` – The SingleApplication block applies system-wide.
|
||||
* `Mode::SecondaryNotification` – Whether to trigger `instanceStarted()` even
|
||||
whenever secondary instances are started.
|
||||
* `Mode::ExcludeAppPath` – Excludes the application path from the server name
|
||||
(and memory block) hash.
|
||||
* `Mode::ExcludeAppVersion` – Excludes the application version from the server
|
||||
name (and memory block) hash.
|
||||
|
||||
*__Note:__ `Mode::SecondaryNotification` only works if set on both the primary
|
||||
and the secondary instance.*
|
||||
|
||||
*__Note:__ Operating system can restrict the shared memory blocks to the same
|
||||
user, in which case the User/System modes will have no effect and the block will
|
||||
be user wide.*
|
||||
|
||||
---
|
||||
|
||||
Versioning
|
||||
----------
|
||||
|
||||
Each major version introduces either very significant changes or is not
|
||||
backwards compatible with the previous version. Minor versions only add
|
||||
additional features, bug fixes or performance improvements and are backwards
|
||||
compatible with the previous release. See [`CHANGELOG.md`](CHANGELOG.md) for
|
||||
more details.
|
||||
|
||||
Implementation
|
||||
--------------
|
||||
|
||||
The library is implemented with a QSharedMemory block which is thread safe and
|
||||
guarantees a race condition will not occur. It also uses a QLocalSocket to
|
||||
notify the main process that a new instance had been spawned and thus invoke the
|
||||
`instanceStarted()` signal and for messaging the primary instance.
|
||||
|
||||
Additionally the library can recover from being forcefully killed on *nix
|
||||
systems and will reset the memory block given that there are no other
|
||||
instances running.
|
||||
|
||||
License
|
||||
-------
|
||||
This library and it's supporting documentation are released under
|
||||
`The MIT License (MIT)` with the exception of the Qt calculator examples which
|
||||
is distributed under the BSD license.
|
||||
46
SingleApplication/Windows.md
Normal file
46
SingleApplication/Windows.md
Normal file
@@ -0,0 +1,46 @@
|
||||
Windows Specific Implementations
|
||||
================================
|
||||
|
||||
Setting the foreground window
|
||||
-----------------------------
|
||||
|
||||
In the `instanceStarted()` example in the `README` we demonstrated how an
|
||||
application can bring it's primary instance window whenever a second copy
|
||||
of the application is started.
|
||||
|
||||
On Windows the ability to bring the application windows to the foreground is
|
||||
restricted, see [`AllowSetForegroundWindow()`][AllowSetForegroundWindow] for more
|
||||
details.
|
||||
|
||||
The background process (the primary instance) can bring its windows to the
|
||||
foreground if it is allowed by the current foreground process (the secondary
|
||||
instance). To bypass this `SingleApplication` must be initialized with the
|
||||
`allowSecondary` parameter set to `true` and the `options` parameter must
|
||||
include `Mode::SecondaryNotification`, See `SingleApplication::Mode` for more
|
||||
details.
|
||||
|
||||
Here is an example:
|
||||
|
||||
```cpp
|
||||
if( app.isSecondary() ) {
|
||||
// This API requires LIBS += User32.lib to be added to the project
|
||||
AllowSetForegroundWindow( DWORD( app.getPrimaryPid() ) );
|
||||
}
|
||||
|
||||
if( app.isPrimary() ) {
|
||||
QObject::connect(
|
||||
&app,
|
||||
&SingleApplication::instanceStarted,
|
||||
this,
|
||||
&App::instanceStarted
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
```cpp
|
||||
void App::instanceStarted() {
|
||||
QApplication::setActiveWindow( [window/widget to set to the foreground] );
|
||||
}
|
||||
```
|
||||
|
||||
[AllowSetForegroundWindow]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms632668.aspx
|
||||
5
SingleApplication/examples/basic/basic.pro
Executable file
5
SingleApplication/examples/basic/basic.pro
Executable file
@@ -0,0 +1,5 @@
|
||||
# Single Application implementation
|
||||
include(../../singleapplication.pri)
|
||||
DEFINES += QAPPLICATION_CLASS=QCoreApplication
|
||||
|
||||
SOURCES += main.cpp
|
||||
9
SingleApplication/examples/basic/main.cpp
Executable file
9
SingleApplication/examples/basic/main.cpp
Executable file
@@ -0,0 +1,9 @@
|
||||
#include <singleapplication.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Allow secondary instances
|
||||
SingleApplication app( argc, argv );
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
73
SingleApplication/examples/calculator/button.cpp
Normal file
73
SingleApplication/examples/calculator/button.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the examples of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** BSD License Usage
|
||||
** Alternatively, you may use this file under the terms of the BSD license
|
||||
** as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of The Qt Company Ltd nor the names of its
|
||||
** contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "button.h"
|
||||
|
||||
//! [0]
|
||||
Button::Button(const QString &text, QWidget *parent)
|
||||
: QToolButton(parent)
|
||||
{
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
setText(text);
|
||||
}
|
||||
//! [0]
|
||||
|
||||
//! [1]
|
||||
QSize Button::sizeHint() const
|
||||
//! [1] //! [2]
|
||||
{
|
||||
QSize size = QToolButton::sizeHint();
|
||||
size.rheight() += 20;
|
||||
size.rwidth() = qMax(size.width(), size.height());
|
||||
return size;
|
||||
}
|
||||
//! [2]
|
||||
68
SingleApplication/examples/calculator/button.h
Normal file
68
SingleApplication/examples/calculator/button.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the examples of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** BSD License Usage
|
||||
** Alternatively, you may use this file under the terms of the BSD license
|
||||
** as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of The Qt Company Ltd nor the names of its
|
||||
** contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef BUTTON_H
|
||||
#define BUTTON_H
|
||||
|
||||
#include <QToolButton>
|
||||
|
||||
//! [0]
|
||||
class Button : public QToolButton
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Button(const QString &text, QWidget *parent = 0);
|
||||
|
||||
QSize sizeHint() const Q_DECL_OVERRIDE;
|
||||
};
|
||||
//! [0]
|
||||
|
||||
#endif
|
||||
406
SingleApplication/examples/calculator/calculator.cpp
Normal file
406
SingleApplication/examples/calculator/calculator.cpp
Normal file
@@ -0,0 +1,406 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the examples of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** BSD License Usage
|
||||
** Alternatively, you may use this file under the terms of the BSD license
|
||||
** as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of The Qt Company Ltd nor the names of its
|
||||
** contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtWidgets>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "button.h"
|
||||
#include "calculator.h"
|
||||
|
||||
//! [0]
|
||||
Calculator::Calculator(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
sumInMemory = 0.0;
|
||||
sumSoFar = 0.0;
|
||||
factorSoFar = 0.0;
|
||||
waitingForOperand = true;
|
||||
//! [0]
|
||||
|
||||
//! [1]
|
||||
display = new QLineEdit("0");
|
||||
//! [1] //! [2]
|
||||
display->setReadOnly(true);
|
||||
display->setAlignment(Qt::AlignRight);
|
||||
display->setMaxLength(15);
|
||||
|
||||
QFont font = display->font();
|
||||
font.setPointSize(font.pointSize() + 8);
|
||||
display->setFont(font);
|
||||
//! [2]
|
||||
|
||||
//! [4]
|
||||
for (int i = 0; i < NumDigitButtons; ++i) {
|
||||
digitButtons[i] = createButton(QString::number(i), SLOT(digitClicked()));
|
||||
}
|
||||
|
||||
Button *pointButton = createButton(tr("."), SLOT(pointClicked()));
|
||||
Button *changeSignButton = createButton(tr("\302\261"), SLOT(changeSignClicked()));
|
||||
|
||||
Button *backspaceButton = createButton(tr("Backspace"), SLOT(backspaceClicked()));
|
||||
Button *clearButton = createButton(tr("Clear"), SLOT(clear()));
|
||||
Button *clearAllButton = createButton(tr("Clear All"), SLOT(clearAll()));
|
||||
|
||||
Button *clearMemoryButton = createButton(tr("MC"), SLOT(clearMemory()));
|
||||
Button *readMemoryButton = createButton(tr("MR"), SLOT(readMemory()));
|
||||
Button *setMemoryButton = createButton(tr("MS"), SLOT(setMemory()));
|
||||
Button *addToMemoryButton = createButton(tr("M+"), SLOT(addToMemory()));
|
||||
|
||||
Button *divisionButton = createButton(tr("\303\267"), SLOT(multiplicativeOperatorClicked()));
|
||||
Button *timesButton = createButton(tr("\303\227"), SLOT(multiplicativeOperatorClicked()));
|
||||
Button *minusButton = createButton(tr("-"), SLOT(additiveOperatorClicked()));
|
||||
Button *plusButton = createButton(tr("+"), SLOT(additiveOperatorClicked()));
|
||||
|
||||
Button *squareRootButton = createButton(tr("Sqrt"), SLOT(unaryOperatorClicked()));
|
||||
Button *powerButton = createButton(tr("x\302\262"), SLOT(unaryOperatorClicked()));
|
||||
Button *reciprocalButton = createButton(tr("1/x"), SLOT(unaryOperatorClicked()));
|
||||
Button *equalButton = createButton(tr("="), SLOT(equalClicked()));
|
||||
//! [4]
|
||||
|
||||
//! [5]
|
||||
QGridLayout *mainLayout = new QGridLayout;
|
||||
//! [5] //! [6]
|
||||
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
mainLayout->addWidget(display, 0, 0, 1, 6);
|
||||
mainLayout->addWidget(backspaceButton, 1, 0, 1, 2);
|
||||
mainLayout->addWidget(clearButton, 1, 2, 1, 2);
|
||||
mainLayout->addWidget(clearAllButton, 1, 4, 1, 2);
|
||||
|
||||
mainLayout->addWidget(clearMemoryButton, 2, 0);
|
||||
mainLayout->addWidget(readMemoryButton, 3, 0);
|
||||
mainLayout->addWidget(setMemoryButton, 4, 0);
|
||||
mainLayout->addWidget(addToMemoryButton, 5, 0);
|
||||
|
||||
for (int i = 1; i < NumDigitButtons; ++i) {
|
||||
int row = ((9 - i) / 3) + 2;
|
||||
int column = ((i - 1) % 3) + 1;
|
||||
mainLayout->addWidget(digitButtons[i], row, column);
|
||||
}
|
||||
|
||||
mainLayout->addWidget(digitButtons[0], 5, 1);
|
||||
mainLayout->addWidget(pointButton, 5, 2);
|
||||
mainLayout->addWidget(changeSignButton, 5, 3);
|
||||
|
||||
mainLayout->addWidget(divisionButton, 2, 4);
|
||||
mainLayout->addWidget(timesButton, 3, 4);
|
||||
mainLayout->addWidget(minusButton, 4, 4);
|
||||
mainLayout->addWidget(plusButton, 5, 4);
|
||||
|
||||
mainLayout->addWidget(squareRootButton, 2, 5);
|
||||
mainLayout->addWidget(powerButton, 3, 5);
|
||||
mainLayout->addWidget(reciprocalButton, 4, 5);
|
||||
mainLayout->addWidget(equalButton, 5, 5);
|
||||
setLayout(mainLayout);
|
||||
|
||||
setWindowTitle(tr("Calculator"));
|
||||
}
|
||||
//! [6]
|
||||
|
||||
//! [7]
|
||||
void Calculator::digitClicked()
|
||||
{
|
||||
Button *clickedButton = qobject_cast<Button *>(sender());
|
||||
int digitValue = clickedButton->text().toInt();
|
||||
if (display->text() == "0" && digitValue == 0.0)
|
||||
return;
|
||||
|
||||
if (waitingForOperand) {
|
||||
display->clear();
|
||||
waitingForOperand = false;
|
||||
}
|
||||
display->setText(display->text() + QString::number(digitValue));
|
||||
}
|
||||
//! [7]
|
||||
|
||||
//! [8]
|
||||
void Calculator::unaryOperatorClicked()
|
||||
//! [8] //! [9]
|
||||
{
|
||||
Button *clickedButton = qobject_cast<Button *>(sender());
|
||||
QString clickedOperator = clickedButton->text();
|
||||
double operand = display->text().toDouble();
|
||||
double result = 0.0;
|
||||
|
||||
if (clickedOperator == tr("Sqrt")) {
|
||||
if (operand < 0.0) {
|
||||
abortOperation();
|
||||
return;
|
||||
}
|
||||
result = std::sqrt(operand);
|
||||
} else if (clickedOperator == tr("x\302\262")) {
|
||||
result = std::pow(operand, 2.0);
|
||||
} else if (clickedOperator == tr("1/x")) {
|
||||
if (operand == 0.0) {
|
||||
abortOperation();
|
||||
return;
|
||||
}
|
||||
result = 1.0 / operand;
|
||||
}
|
||||
display->setText(QString::number(result));
|
||||
waitingForOperand = true;
|
||||
}
|
||||
//! [9]
|
||||
|
||||
//! [10]
|
||||
void Calculator::additiveOperatorClicked()
|
||||
//! [10] //! [11]
|
||||
{
|
||||
Button *clickedButton = qobject_cast<Button *>(sender());
|
||||
QString clickedOperator = clickedButton->text();
|
||||
double operand = display->text().toDouble();
|
||||
|
||||
//! [11] //! [12]
|
||||
if (!pendingMultiplicativeOperator.isEmpty()) {
|
||||
//! [12] //! [13]
|
||||
if (!calculate(operand, pendingMultiplicativeOperator)) {
|
||||
abortOperation();
|
||||
return;
|
||||
}
|
||||
display->setText(QString::number(factorSoFar));
|
||||
operand = factorSoFar;
|
||||
factorSoFar = 0.0;
|
||||
pendingMultiplicativeOperator.clear();
|
||||
}
|
||||
|
||||
//! [13] //! [14]
|
||||
if (!pendingAdditiveOperator.isEmpty()) {
|
||||
//! [14] //! [15]
|
||||
if (!calculate(operand, pendingAdditiveOperator)) {
|
||||
abortOperation();
|
||||
return;
|
||||
}
|
||||
display->setText(QString::number(sumSoFar));
|
||||
} else {
|
||||
sumSoFar = operand;
|
||||
}
|
||||
|
||||
//! [15] //! [16]
|
||||
pendingAdditiveOperator = clickedOperator;
|
||||
//! [16] //! [17]
|
||||
waitingForOperand = true;
|
||||
}
|
||||
//! [17]
|
||||
|
||||
//! [18]
|
||||
void Calculator::multiplicativeOperatorClicked()
|
||||
{
|
||||
Button *clickedButton = qobject_cast<Button *>(sender());
|
||||
QString clickedOperator = clickedButton->text();
|
||||
double operand = display->text().toDouble();
|
||||
|
||||
if (!pendingMultiplicativeOperator.isEmpty()) {
|
||||
if (!calculate(operand, pendingMultiplicativeOperator)) {
|
||||
abortOperation();
|
||||
return;
|
||||
}
|
||||
display->setText(QString::number(factorSoFar));
|
||||
} else {
|
||||
factorSoFar = operand;
|
||||
}
|
||||
|
||||
pendingMultiplicativeOperator = clickedOperator;
|
||||
waitingForOperand = true;
|
||||
}
|
||||
//! [18]
|
||||
|
||||
//! [20]
|
||||
void Calculator::equalClicked()
|
||||
{
|
||||
double operand = display->text().toDouble();
|
||||
|
||||
if (!pendingMultiplicativeOperator.isEmpty()) {
|
||||
if (!calculate(operand, pendingMultiplicativeOperator)) {
|
||||
abortOperation();
|
||||
return;
|
||||
}
|
||||
operand = factorSoFar;
|
||||
factorSoFar = 0.0;
|
||||
pendingMultiplicativeOperator.clear();
|
||||
}
|
||||
if (!pendingAdditiveOperator.isEmpty()) {
|
||||
if (!calculate(operand, pendingAdditiveOperator)) {
|
||||
abortOperation();
|
||||
return;
|
||||
}
|
||||
pendingAdditiveOperator.clear();
|
||||
} else {
|
||||
sumSoFar = operand;
|
||||
}
|
||||
|
||||
display->setText(QString::number(sumSoFar));
|
||||
sumSoFar = 0.0;
|
||||
waitingForOperand = true;
|
||||
}
|
||||
//! [20]
|
||||
|
||||
//! [22]
|
||||
void Calculator::pointClicked()
|
||||
{
|
||||
if (waitingForOperand)
|
||||
display->setText("0");
|
||||
if (!display->text().contains('.'))
|
||||
display->setText(display->text() + tr("."));
|
||||
waitingForOperand = false;
|
||||
}
|
||||
//! [22]
|
||||
|
||||
//! [24]
|
||||
void Calculator::changeSignClicked()
|
||||
{
|
||||
QString text = display->text();
|
||||
double value = text.toDouble();
|
||||
|
||||
if (value > 0.0) {
|
||||
text.prepend(tr("-"));
|
||||
} else if (value < 0.0) {
|
||||
text.remove(0, 1);
|
||||
}
|
||||
display->setText(text);
|
||||
}
|
||||
//! [24]
|
||||
|
||||
//! [26]
|
||||
void Calculator::backspaceClicked()
|
||||
{
|
||||
if (waitingForOperand)
|
||||
return;
|
||||
|
||||
QString text = display->text();
|
||||
text.chop(1);
|
||||
if (text.isEmpty()) {
|
||||
text = "0";
|
||||
waitingForOperand = true;
|
||||
}
|
||||
display->setText(text);
|
||||
}
|
||||
//! [26]
|
||||
|
||||
//! [28]
|
||||
void Calculator::clear()
|
||||
{
|
||||
if (waitingForOperand)
|
||||
return;
|
||||
|
||||
display->setText("0");
|
||||
waitingForOperand = true;
|
||||
}
|
||||
//! [28]
|
||||
|
||||
//! [30]
|
||||
void Calculator::clearAll()
|
||||
{
|
||||
sumSoFar = 0.0;
|
||||
factorSoFar = 0.0;
|
||||
pendingAdditiveOperator.clear();
|
||||
pendingMultiplicativeOperator.clear();
|
||||
display->setText("0");
|
||||
waitingForOperand = true;
|
||||
}
|
||||
//! [30]
|
||||
|
||||
//! [32]
|
||||
void Calculator::clearMemory()
|
||||
{
|
||||
sumInMemory = 0.0;
|
||||
}
|
||||
|
||||
void Calculator::readMemory()
|
||||
{
|
||||
display->setText(QString::number(sumInMemory));
|
||||
waitingForOperand = true;
|
||||
}
|
||||
|
||||
void Calculator::setMemory()
|
||||
{
|
||||
equalClicked();
|
||||
sumInMemory = display->text().toDouble();
|
||||
}
|
||||
|
||||
void Calculator::addToMemory()
|
||||
{
|
||||
equalClicked();
|
||||
sumInMemory += display->text().toDouble();
|
||||
}
|
||||
//! [32]
|
||||
//! [34]
|
||||
Button *Calculator::createButton(const QString &text, const char *member)
|
||||
{
|
||||
Button *button = new Button(text);
|
||||
connect(button, SIGNAL(clicked()), this, member);
|
||||
return button;
|
||||
}
|
||||
//! [34]
|
||||
|
||||
//! [36]
|
||||
void Calculator::abortOperation()
|
||||
{
|
||||
clearAll();
|
||||
display->setText(tr("####"));
|
||||
}
|
||||
//! [36]
|
||||
|
||||
//! [38]
|
||||
bool Calculator::calculate(double rightOperand, const QString &pendingOperator)
|
||||
{
|
||||
if (pendingOperator == tr("+")) {
|
||||
sumSoFar += rightOperand;
|
||||
} else if (pendingOperator == tr("-")) {
|
||||
sumSoFar -= rightOperand;
|
||||
} else if (pendingOperator == tr("\303\227")) {
|
||||
factorSoFar *= rightOperand;
|
||||
} else if (pendingOperator == tr("\303\267")) {
|
||||
if (rightOperand == 0.0)
|
||||
return false;
|
||||
factorSoFar /= rightOperand;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//! [38]
|
||||
117
SingleApplication/examples/calculator/calculator.h
Normal file
117
SingleApplication/examples/calculator/calculator.h
Normal file
@@ -0,0 +1,117 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the examples of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** BSD License Usage
|
||||
** Alternatively, you may use this file under the terms of the BSD license
|
||||
** as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of The Qt Company Ltd nor the names of its
|
||||
** contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CALCULATOR_H
|
||||
#define CALCULATOR_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QLineEdit;
|
||||
QT_END_NAMESPACE
|
||||
class Button;
|
||||
|
||||
//! [0]
|
||||
class Calculator : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Calculator(QWidget *parent = 0);
|
||||
|
||||
private slots:
|
||||
void digitClicked();
|
||||
void unaryOperatorClicked();
|
||||
void additiveOperatorClicked();
|
||||
void multiplicativeOperatorClicked();
|
||||
void equalClicked();
|
||||
void pointClicked();
|
||||
void changeSignClicked();
|
||||
void backspaceClicked();
|
||||
void clear();
|
||||
void clearAll();
|
||||
void clearMemory();
|
||||
void readMemory();
|
||||
void setMemory();
|
||||
void addToMemory();
|
||||
//! [0]
|
||||
|
||||
//! [1]
|
||||
private:
|
||||
//! [1] //! [2]
|
||||
Button *createButton(const QString &text, const char *member);
|
||||
void abortOperation();
|
||||
bool calculate(double rightOperand, const QString &pendingOperator);
|
||||
//! [2]
|
||||
|
||||
//! [3]
|
||||
double sumInMemory;
|
||||
//! [3] //! [4]
|
||||
double sumSoFar;
|
||||
//! [4] //! [5]
|
||||
double factorSoFar;
|
||||
//! [5] //! [6]
|
||||
QString pendingAdditiveOperator;
|
||||
//! [6] //! [7]
|
||||
QString pendingMultiplicativeOperator;
|
||||
//! [7] //! [8]
|
||||
bool waitingForOperand;
|
||||
//! [8]
|
||||
|
||||
//! [9]
|
||||
QLineEdit *display;
|
||||
//! [9] //! [10]
|
||||
|
||||
enum { NumDigitButtons = 10 };
|
||||
Button *digitButtons[NumDigitButtons];
|
||||
};
|
||||
//! [10]
|
||||
|
||||
#endif
|
||||
11
SingleApplication/examples/calculator/calculator.pro
Normal file
11
SingleApplication/examples/calculator/calculator.pro
Normal file
@@ -0,0 +1,11 @@
|
||||
QT += widgets
|
||||
|
||||
HEADERS = button.h \
|
||||
calculator.h
|
||||
SOURCES = button.cpp \
|
||||
calculator.cpp \
|
||||
main.cpp
|
||||
|
||||
# Single Application implementation
|
||||
include(../../singleapplication.pri)
|
||||
DEFINES += QAPPLICATION_CLASS=QApplication
|
||||
71
SingleApplication/examples/calculator/main.cpp
Normal file
71
SingleApplication/examples/calculator/main.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the examples of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** BSD License Usage
|
||||
** Alternatively, you may use this file under the terms of the BSD license
|
||||
** as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of The Qt Company Ltd nor the names of its
|
||||
** contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
#include <singleapplication.h>
|
||||
|
||||
#include "calculator.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
SingleApplication app(argc, argv);
|
||||
|
||||
Calculator calc;
|
||||
|
||||
QObject::connect( &app, &SingleApplication::instanceStarted, [ &calc ]() {
|
||||
calc.raise();
|
||||
calc.activateWindow();
|
||||
});
|
||||
|
||||
calc.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
25
SingleApplication/examples/sending_arguments/main.cpp
Executable file
25
SingleApplication/examples/sending_arguments/main.cpp
Executable file
@@ -0,0 +1,25 @@
|
||||
#include <singleapplication.h>
|
||||
#include "messagereceiver.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Allow secondary instances
|
||||
SingleApplication app( argc, argv, true );
|
||||
|
||||
MessageReceiver msgReceiver;
|
||||
|
||||
// If this is a secondary instance
|
||||
if( app.isSecondary() ) {
|
||||
app.sendMessage( app.arguments().join(' ').toUtf8() );
|
||||
return 0;
|
||||
} else {
|
||||
QObject::connect(
|
||||
&app,
|
||||
&SingleApplication::receivedMessage,
|
||||
&msgReceiver,
|
||||
&MessageReceiver::receivedMessage
|
||||
);
|
||||
}
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#include <QDebug>
|
||||
#include "messagereceiver.h"
|
||||
|
||||
MessageReceiver::MessageReceiver(QObject *parent) : QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void MessageReceiver::receivedMessage(int instanceId, QByteArray message)
|
||||
{
|
||||
qDebug() << "Received message from instance: " << instanceId;
|
||||
qDebug() << "Message Text: " << message;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef MESSAGERECEIVER_H
|
||||
#define MESSAGERECEIVER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class MessageReceiver : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MessageReceiver(QObject *parent = 0);
|
||||
public slots:
|
||||
void receivedMessage( int instanceId, QByteArray message );
|
||||
};
|
||||
|
||||
#endif // MESSAGERECEIVER_H
|
||||
9
SingleApplication/examples/sending_arguments/sending_arguments.pro
Executable file
9
SingleApplication/examples/sending_arguments/sending_arguments.pro
Executable file
@@ -0,0 +1,9 @@
|
||||
# Single Application implementation
|
||||
include(../../singleapplication.pri)
|
||||
DEFINES += QAPPLICATION_CLASS=QCoreApplication
|
||||
|
||||
SOURCES += main.cpp \
|
||||
messagereceiver.cpp
|
||||
|
||||
HEADERS += \
|
||||
messagereceiver.h
|
||||
174
SingleApplication/singleapplication.cpp
Normal file
174
SingleApplication/singleapplication.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) Itay Grudev 2015 - 2018
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#include <QtCore/QTime>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QSharedMemory>
|
||||
|
||||
#include "singleapplication.h"
|
||||
#include "singleapplication_p.h"
|
||||
|
||||
/**
|
||||
* @brief Constructor. Checks and fires up LocalServer or closes the program
|
||||
* if another instance already exists
|
||||
* @param argc
|
||||
* @param argv
|
||||
* @param {bool} allowSecondaryInstances
|
||||
*/
|
||||
SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary, Options options, int timeout )
|
||||
: app_t( argc, argv ), d_ptr( new SingleApplicationPrivate( this ) )
|
||||
{
|
||||
Q_D(SingleApplication);
|
||||
|
||||
// Store the current mode of the program
|
||||
d->options = options;
|
||||
|
||||
// Generating an application ID used for identifying the shared memory
|
||||
// block and QLocalServer
|
||||
d->genBlockServerName();
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
// By explicitly attaching it and then deleting it we make sure that the
|
||||
// memory is deleted even after the process has crashed on Unix.
|
||||
d->memory = new QSharedMemory( d->blockServerName );
|
||||
d->memory->attach();
|
||||
delete d->memory;
|
||||
#endif
|
||||
// Guarantee thread safe behaviour with a shared memory block.
|
||||
d->memory = new QSharedMemory( d->blockServerName );
|
||||
|
||||
// Create a shared memory block
|
||||
if( d->memory->create( sizeof( InstancesInfo ) ) ) {
|
||||
// Initialize the shared memory block
|
||||
d->memory->lock();
|
||||
d->initializeMemoryBlock();
|
||||
d->memory->unlock();
|
||||
} else {
|
||||
// Attempt to attach to the memory segment
|
||||
if( ! d->memory->attach() ) {
|
||||
qCritical() << "SingleApplication: Unable to attach to shared memory block.";
|
||||
qCritical() << d->memory->errorString();
|
||||
delete d;
|
||||
::exit( EXIT_FAILURE );
|
||||
}
|
||||
}
|
||||
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>( d->memory->data() );
|
||||
QTime time;
|
||||
time.start();
|
||||
|
||||
// Make sure the shared memory block is initialised and in consistent state
|
||||
while( true ) {
|
||||
d->memory->lock();
|
||||
|
||||
if( d->blockChecksum() == inst->checksum ) break;
|
||||
|
||||
if( time.elapsed() > 5000 ) {
|
||||
qWarning() << "SingleApplication: Shared memory block has been in an inconsistent state from more than 5s. Assuming primary instance failure.";
|
||||
d->initializeMemoryBlock();
|
||||
}
|
||||
|
||||
d->memory->unlock();
|
||||
|
||||
// Random sleep here limits the probability of a colision between two racing apps
|
||||
qsrand( QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max() );
|
||||
QThread::sleep( 8 + static_cast <unsigned long>( static_cast <float>( qrand() ) / RAND_MAX * 10 ) );
|
||||
}
|
||||
|
||||
if( inst->primary == false) {
|
||||
d->startPrimary();
|
||||
d->memory->unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if another instance can be started
|
||||
if( allowSecondary ) {
|
||||
inst->secondary += 1;
|
||||
inst->checksum = d->blockChecksum();
|
||||
d->instanceNumber = inst->secondary;
|
||||
d->startSecondary();
|
||||
if( d->options & Mode::SecondaryNotification ) {
|
||||
d->connectToPrimary( timeout, SingleApplicationPrivate::SecondaryInstance );
|
||||
}
|
||||
d->memory->unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
d->memory->unlock();
|
||||
|
||||
d->connectToPrimary( timeout, SingleApplicationPrivate::NewInstance );
|
||||
|
||||
delete d;
|
||||
|
||||
::exit( EXIT_SUCCESS );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
SingleApplication::~SingleApplication()
|
||||
{
|
||||
Q_D(SingleApplication);
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool SingleApplication::isPrimary()
|
||||
{
|
||||
Q_D(SingleApplication);
|
||||
return d->server != nullptr;
|
||||
}
|
||||
|
||||
bool SingleApplication::isSecondary()
|
||||
{
|
||||
Q_D(SingleApplication);
|
||||
return d->server == nullptr;
|
||||
}
|
||||
|
||||
quint32 SingleApplication::instanceId()
|
||||
{
|
||||
Q_D(SingleApplication);
|
||||
return d->instanceNumber;
|
||||
}
|
||||
|
||||
qint64 SingleApplication::primaryPid()
|
||||
{
|
||||
Q_D(SingleApplication);
|
||||
return d->primaryPid();
|
||||
}
|
||||
|
||||
bool SingleApplication::sendMessage( QByteArray message, int timeout )
|
||||
{
|
||||
Q_D(SingleApplication);
|
||||
|
||||
// Nobody to connect to
|
||||
if( isPrimary() ) return false;
|
||||
|
||||
// Make sure the socket is connected
|
||||
d->connectToPrimary( timeout, SingleApplicationPrivate::Reconnect );
|
||||
|
||||
d->socket->write( message );
|
||||
bool dataWritten = d->socket->flush();
|
||||
d->socket->waitForBytesWritten( timeout );
|
||||
return dataWritten;
|
||||
}
|
||||
135
SingleApplication/singleapplication.h
Normal file
135
SingleApplication/singleapplication.h
Normal file
@@ -0,0 +1,135 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) Itay Grudev 2015 - 2018
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#ifndef SINGLE_APPLICATION_H
|
||||
#define SINGLE_APPLICATION_H
|
||||
|
||||
#include <QtCore/QtGlobal>
|
||||
#include <QtNetwork/QLocalSocket>
|
||||
|
||||
#ifndef QAPPLICATION_CLASS
|
||||
#define QAPPLICATION_CLASS QCoreApplication
|
||||
#endif
|
||||
|
||||
#include QT_STRINGIFY(QAPPLICATION_CLASS)
|
||||
|
||||
class SingleApplicationPrivate;
|
||||
|
||||
/**
|
||||
* @brief The SingleApplication class handles multipe instances of the same
|
||||
* Application
|
||||
* @see QCoreApplication
|
||||
*/
|
||||
class SingleApplication : public QAPPLICATION_CLASS
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
typedef QAPPLICATION_CLASS app_t;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Mode of operation of SingleApplication.
|
||||
* Whether the block should be user-wide or system-wide and whether the
|
||||
* primary instance should be notified when a secondary instance had been
|
||||
* started.
|
||||
* @note Operating system can restrict the shared memory blocks to the same
|
||||
* user, in which case the User/System modes will have no effect and the
|
||||
* block will be user wide.
|
||||
* @enum
|
||||
*/
|
||||
enum Mode {
|
||||
User = 1 << 0,
|
||||
System = 1 << 1,
|
||||
SecondaryNotification = 1 << 2,
|
||||
ExcludeAppVersion = 1 << 3,
|
||||
ExcludeAppPath = 1 << 4
|
||||
};
|
||||
Q_DECLARE_FLAGS(Options, Mode)
|
||||
|
||||
/**
|
||||
* @brief Intitializes a SingleApplication instance with argc command line
|
||||
* arguments in argv
|
||||
* @arg {int &} argc - Number of arguments in argv
|
||||
* @arg {const char *[]} argv - Supplied command line arguments
|
||||
* @arg {bool} allowSecondary - Whether to start the instance as secondary
|
||||
* if there is already a primary instance.
|
||||
* @arg {Mode} mode - Whether for the SingleApplication block to be applied
|
||||
* User wide or System wide.
|
||||
* @arg {int} timeout - Timeout to wait in miliseconds.
|
||||
* @note argc and argv may be changed as Qt removes arguments that it
|
||||
* recognizes
|
||||
* @note Mode::SecondaryNotification only works if set on both the primary
|
||||
* instance and the secondary instance.
|
||||
* @note The timeout is just a hint for the maximum time of blocking
|
||||
* operations. It does not guarantee that the SingleApplication
|
||||
* initialisation will be completed in given time, though is a good hint.
|
||||
* Usually 4*timeout would be the worst case (fail) scenario.
|
||||
* @see See the corresponding QAPPLICATION_CLASS constructor for reference
|
||||
*/
|
||||
explicit SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000 );
|
||||
~SingleApplication();
|
||||
|
||||
/**
|
||||
* @brief Returns if the instance is the primary instance
|
||||
* @returns {bool}
|
||||
*/
|
||||
bool isPrimary();
|
||||
|
||||
/**
|
||||
* @brief Returns if the instance is a secondary instance
|
||||
* @returns {bool}
|
||||
*/
|
||||
bool isSecondary();
|
||||
|
||||
/**
|
||||
* @brief Returns a unique identifier for the current instance
|
||||
* @returns {qint32}
|
||||
*/
|
||||
quint32 instanceId();
|
||||
|
||||
/**
|
||||
* @brief Returns the process ID (PID) of the primary instance
|
||||
* @returns {qint64}
|
||||
*/
|
||||
qint64 primaryPid();
|
||||
|
||||
/**
|
||||
* @brief Sends a message to the primary instance. Returns true on success.
|
||||
* @param {int} timeout - Timeout for connecting
|
||||
* @returns {bool}
|
||||
* @note sendMessage() will return false if invoked from the primary
|
||||
* instance.
|
||||
*/
|
||||
bool sendMessage( QByteArray message, int timeout = 100 );
|
||||
|
||||
Q_SIGNALS:
|
||||
void instanceStarted();
|
||||
void receivedMessage( quint32 instanceId, QByteArray message );
|
||||
|
||||
private:
|
||||
SingleApplicationPrivate *d_ptr;
|
||||
Q_DECLARE_PRIVATE(SingleApplication)
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(SingleApplication::Options)
|
||||
|
||||
#endif // SINGLE_APPLICATION_H
|
||||
19
SingleApplication/singleapplication.pri
Normal file
19
SingleApplication/singleapplication.pri
Normal file
@@ -0,0 +1,19 @@
|
||||
QT += core network
|
||||
CONFIG += c++11
|
||||
|
||||
HEADERS += $$PWD/singleapplication.h \
|
||||
$$PWD/singleapplication_p.h
|
||||
SOURCES += $$PWD/singleapplication.cpp \
|
||||
$$PWD/singleapplication_p.cpp
|
||||
|
||||
INCLUDEPATH += $$PWD
|
||||
|
||||
win32 {
|
||||
msvc:LIBS += Advapi32.lib
|
||||
gcc:LIBS += -ladvapi32
|
||||
}
|
||||
|
||||
DISTFILES += \
|
||||
$$PWD/README.md \
|
||||
$$PWD/CHANGELOG.md \
|
||||
$$PWD/Windows.md
|
||||
349
SingleApplication/singleapplication_p.cpp
Normal file
349
SingleApplication/singleapplication_p.cpp
Normal file
@@ -0,0 +1,349 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) Itay Grudev 2015 - 2018
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
//
|
||||
// W A R N I N G !!!
|
||||
// -----------------
|
||||
//
|
||||
// This file is not part of the SingleApplication API. It is used purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or may even be removed.
|
||||
//
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QProcess>
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QSemaphore>
|
||||
#include <QtCore/QDataStream>
|
||||
#include <QtCore/QStandardPaths>
|
||||
#include <QtCore/QCryptographicHash>
|
||||
#include <QtNetwork/QLocalServer>
|
||||
#include <QtNetwork/QLocalSocket>
|
||||
|
||||
#include "singleapplication.h"
|
||||
#include "singleapplication_p.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
#include <lmcons.h>
|
||||
#endif
|
||||
|
||||
SingleApplicationPrivate::SingleApplicationPrivate( SingleApplication *q_ptr )
|
||||
: q_ptr( q_ptr )
|
||||
{
|
||||
server = nullptr;
|
||||
socket = nullptr;
|
||||
}
|
||||
|
||||
SingleApplicationPrivate::~SingleApplicationPrivate()
|
||||
{
|
||||
if( socket != nullptr ) {
|
||||
socket->close();
|
||||
delete socket;
|
||||
}
|
||||
|
||||
memory->lock();
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
|
||||
if( server != nullptr ) {
|
||||
server->close();
|
||||
delete server;
|
||||
inst->primary = false;
|
||||
inst->primaryPid = -1;
|
||||
inst->checksum = blockChecksum();
|
||||
}
|
||||
memory->unlock();
|
||||
|
||||
delete memory;
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::genBlockServerName()
|
||||
{
|
||||
QCryptographicHash appData( QCryptographicHash::Sha256 );
|
||||
appData.addData( "SingleApplication", 17 );
|
||||
appData.addData( SingleApplication::app_t::applicationName().toUtf8() );
|
||||
appData.addData( SingleApplication::app_t::organizationName().toUtf8() );
|
||||
appData.addData( SingleApplication::app_t::organizationDomain().toUtf8() );
|
||||
|
||||
if( ! (options & SingleApplication::Mode::ExcludeAppVersion) ) {
|
||||
appData.addData( SingleApplication::app_t::applicationVersion().toUtf8() );
|
||||
}
|
||||
|
||||
if( ! (options & SingleApplication::Mode::ExcludeAppPath) ) {
|
||||
#ifdef Q_OS_WIN
|
||||
appData.addData( SingleApplication::app_t::applicationFilePath().toLower().toUtf8() );
|
||||
#else
|
||||
appData.addData( SingleApplication::app_t::applicationFilePath().toUtf8() );
|
||||
#endif
|
||||
}
|
||||
|
||||
// User level block requires a user specific data in the hash
|
||||
if( options & SingleApplication::Mode::User ) {
|
||||
#ifdef Q_OS_WIN
|
||||
Q_UNUSED(timeout);
|
||||
wchar_t username [ UNLEN + 1 ];
|
||||
// Specifies size of the buffer on input
|
||||
DWORD usernameLength = UNLEN + 1;
|
||||
if( GetUserNameW( username, &usernameLength ) ) {
|
||||
appData.addData( QString::fromWCharArray(username).toUtf8() );
|
||||
} else {
|
||||
appData.addData( QStandardPaths::standardLocations( QStandardPaths::HomeLocation ).join("").toUtf8() );
|
||||
}
|
||||
#endif
|
||||
#ifdef Q_OS_UNIX
|
||||
QProcess process;
|
||||
process.start( "whoami" );
|
||||
if( process.waitForFinished( 100 ) &&
|
||||
process.exitCode() == QProcess::NormalExit) {
|
||||
appData.addData( process.readLine() );
|
||||
} else {
|
||||
appData.addData(
|
||||
QDir(
|
||||
QStandardPaths::standardLocations( QStandardPaths::HomeLocation ).first()
|
||||
).absolutePath().toUtf8()
|
||||
);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with
|
||||
// server naming requirements.
|
||||
blockServerName = appData.result().toBase64().replace("/", "_");
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::initializeMemoryBlock()
|
||||
{
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>( memory->data() );
|
||||
inst->primary = false;
|
||||
inst->secondary = 0;
|
||||
inst->primaryPid = -1;
|
||||
inst->checksum = blockChecksum();
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::startPrimary()
|
||||
{
|
||||
Q_Q(SingleApplication);
|
||||
|
||||
// Successful creation means that no main process exists
|
||||
// So we start a QLocalServer to listen for connections
|
||||
QLocalServer::removeServer( blockServerName );
|
||||
server = new QLocalServer();
|
||||
|
||||
// Restrict access to the socket according to the
|
||||
// SingleApplication::Mode::User flag on User level or no restrictions
|
||||
if( options & SingleApplication::Mode::User ) {
|
||||
server->setSocketOptions( QLocalServer::UserAccessOption );
|
||||
} else {
|
||||
server->setSocketOptions( QLocalServer::WorldAccessOption );
|
||||
}
|
||||
|
||||
server->listen( blockServerName );
|
||||
QObject::connect(
|
||||
server,
|
||||
&QLocalServer::newConnection,
|
||||
this,
|
||||
&SingleApplicationPrivate::slotConnectionEstablished
|
||||
);
|
||||
|
||||
// Reset the number of connections
|
||||
InstancesInfo* inst = static_cast <InstancesInfo*>( memory->data() );
|
||||
|
||||
inst->primary = true;
|
||||
inst->primaryPid = q->applicationPid();
|
||||
inst->checksum = blockChecksum();
|
||||
|
||||
instanceNumber = 0;
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::startSecondary()
|
||||
{
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::connectToPrimary( int msecs, ConnectionType connectionType )
|
||||
{
|
||||
// Connect to the Local Server of the Primary Instance if not already
|
||||
// connected.
|
||||
if( socket == nullptr ) {
|
||||
socket = new QLocalSocket();
|
||||
}
|
||||
|
||||
// If already connected - we are done;
|
||||
if( socket->state() == QLocalSocket::ConnectedState )
|
||||
return;
|
||||
|
||||
// If not connect
|
||||
if( socket->state() == QLocalSocket::UnconnectedState ||
|
||||
socket->state() == QLocalSocket::ClosingState ) {
|
||||
socket->connectToServer( blockServerName );
|
||||
}
|
||||
|
||||
// Wait for being connected
|
||||
if( socket->state() == QLocalSocket::ConnectingState ) {
|
||||
socket->waitForConnected( msecs );
|
||||
}
|
||||
|
||||
// Initialisation message according to the SingleApplication protocol
|
||||
if( socket->state() == QLocalSocket::ConnectedState ) {
|
||||
// Notify the parent that a new instance had been started;
|
||||
QByteArray initMsg;
|
||||
QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
|
||||
writeStream.setVersion(QDataStream::Qt_5_6);
|
||||
writeStream << blockServerName.toLatin1();
|
||||
writeStream << static_cast<quint8>(connectionType);
|
||||
writeStream << instanceNumber;
|
||||
quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
|
||||
writeStream << checksum;
|
||||
|
||||
// The header indicates the message length that follows
|
||||
QByteArray header;
|
||||
QDataStream headerStream(&header, QIODevice::WriteOnly);
|
||||
headerStream.setVersion(QDataStream::Qt_5_6);
|
||||
headerStream << static_cast <quint64>( initMsg.length() );
|
||||
|
||||
socket->write( header );
|
||||
socket->write( initMsg );
|
||||
socket->flush();
|
||||
socket->waitForBytesWritten( msecs );
|
||||
}
|
||||
}
|
||||
|
||||
quint16 SingleApplicationPrivate::blockChecksum()
|
||||
{
|
||||
return qChecksum(
|
||||
static_cast <const char *>( memory->data() ),
|
||||
offsetof( InstancesInfo, checksum )
|
||||
);
|
||||
}
|
||||
|
||||
qint64 SingleApplicationPrivate::primaryPid()
|
||||
{
|
||||
qint64 pid;
|
||||
|
||||
memory->lock();
|
||||
InstancesInfo* inst = static_cast<InstancesInfo*>( memory->data() );
|
||||
pid = inst->primaryPid;
|
||||
memory->unlock();
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Executed when a connection has been made to the LocalServer
|
||||
*/
|
||||
void SingleApplicationPrivate::slotConnectionEstablished()
|
||||
{
|
||||
Q_Q(SingleApplication);
|
||||
|
||||
QLocalSocket *nextConnSocket = server->nextPendingConnection();
|
||||
|
||||
quint32 instanceId = 0;
|
||||
ConnectionType connectionType = InvalidConnection;
|
||||
if( nextConnSocket->waitForReadyRead( 100 ) ) {
|
||||
// read the fields in same order and format as written
|
||||
QDataStream headerStream(nextConnSocket);
|
||||
headerStream.setVersion( QDataStream::Qt_5_6 );
|
||||
|
||||
// Read the header to know the message length
|
||||
quint64 msgLen = 0;
|
||||
headerStream >> msgLen;
|
||||
|
||||
if( msgLen >= sizeof( quint16 ) ) {
|
||||
// Read the message body
|
||||
QByteArray msgBytes = nextConnSocket->read(msgLen);
|
||||
QDataStream readStream(msgBytes);
|
||||
readStream.setVersion( QDataStream::Qt_5_6 );
|
||||
|
||||
// server name
|
||||
QByteArray latin1Name;
|
||||
readStream >> latin1Name;
|
||||
|
||||
// connection type
|
||||
quint8 connType = InvalidConnection;
|
||||
readStream >> connType;
|
||||
connectionType = static_cast <ConnectionType>( connType );
|
||||
|
||||
// instance id
|
||||
readStream >> instanceId;
|
||||
|
||||
// checksum
|
||||
quint16 msgChecksum = 0;
|
||||
readStream >> msgChecksum;
|
||||
|
||||
const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast<quint32>(msgBytes.length() - sizeof(quint16)));
|
||||
|
||||
if (readStream.status() != QDataStream::Ok || QLatin1String(latin1Name) != blockServerName || msgChecksum != actualChecksum) {
|
||||
connectionType = InvalidConnection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( connectionType == InvalidConnection ) {
|
||||
nextConnSocket->close();
|
||||
delete nextConnSocket;
|
||||
return;
|
||||
}
|
||||
|
||||
QObject::connect(
|
||||
nextConnSocket,
|
||||
&QLocalSocket::aboutToClose,
|
||||
this,
|
||||
[nextConnSocket, instanceId, this]() {
|
||||
Q_EMIT this->slotClientConnectionClosed( nextConnSocket, instanceId );
|
||||
}
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
nextConnSocket,
|
||||
&QLocalSocket::readyRead,
|
||||
this,
|
||||
[nextConnSocket, instanceId, this]() {
|
||||
Q_EMIT this->slotDataAvailable( nextConnSocket, instanceId );
|
||||
}
|
||||
);
|
||||
|
||||
if( connectionType == NewInstance || (
|
||||
connectionType == SecondaryInstance &&
|
||||
options & SingleApplication::Mode::SecondaryNotification
|
||||
)
|
||||
) {
|
||||
Q_EMIT q->instanceStarted();
|
||||
}
|
||||
|
||||
if( nextConnSocket->bytesAvailable() > 0 ) {
|
||||
Q_EMIT this->slotDataAvailable( nextConnSocket, instanceId );
|
||||
}
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::slotDataAvailable( QLocalSocket *dataSocket, quint32 instanceId )
|
||||
{
|
||||
Q_Q(SingleApplication);
|
||||
Q_EMIT q->receivedMessage( instanceId, dataSocket->readAll() );
|
||||
}
|
||||
|
||||
void SingleApplicationPrivate::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId )
|
||||
{
|
||||
if( closedSocket->bytesAvailable() > 0 )
|
||||
Q_EMIT slotDataAvailable( closedSocket, instanceId );
|
||||
closedSocket->deleteLater();
|
||||
}
|
||||
83
SingleApplication/singleapplication_p.h
Normal file
83
SingleApplication/singleapplication_p.h
Normal file
@@ -0,0 +1,83 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) Itay Grudev 2015 - 2016
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
//
|
||||
// W A R N I N G !!!
|
||||
// -----------------
|
||||
//
|
||||
// This file is not part of the SingleApplication API. It is used purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or may even be removed.
|
||||
//
|
||||
|
||||
#ifndef SINGLEAPPLICATION_P_H
|
||||
#define SINGLEAPPLICATION_P_H
|
||||
|
||||
#include <QtCore/QSharedMemory>
|
||||
#include <QtNetwork/QLocalServer>
|
||||
#include <QtNetwork/QLocalSocket>
|
||||
#include "singleapplication.h"
|
||||
|
||||
struct InstancesInfo {
|
||||
bool primary;
|
||||
quint32 secondary;
|
||||
qint64 primaryPid;
|
||||
quint16 checksum;
|
||||
};
|
||||
|
||||
class SingleApplicationPrivate : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum ConnectionType : quint8 {
|
||||
InvalidConnection = 0,
|
||||
NewInstance = 1,
|
||||
SecondaryInstance = 2,
|
||||
Reconnect = 3
|
||||
};
|
||||
Q_DECLARE_PUBLIC(SingleApplication)
|
||||
|
||||
SingleApplicationPrivate( SingleApplication *q_ptr );
|
||||
~SingleApplicationPrivate();
|
||||
|
||||
void genBlockServerName();
|
||||
void initializeMemoryBlock();
|
||||
void startPrimary();
|
||||
void startSecondary();
|
||||
void connectToPrimary(int msecs, ConnectionType connectionType );
|
||||
quint16 blockChecksum();
|
||||
qint64 primaryPid();
|
||||
|
||||
SingleApplication *q_ptr;
|
||||
QSharedMemory *memory;
|
||||
QLocalSocket *socket;
|
||||
QLocalServer *server;
|
||||
quint32 instanceNumber;
|
||||
QString blockServerName;
|
||||
SingleApplication::Options options;
|
||||
|
||||
public Q_SLOTS:
|
||||
void slotConnectionEstablished();
|
||||
void slotDataAvailable( QLocalSocket*, quint32 );
|
||||
void slotClientConnectionClosed( QLocalSocket*, quint32 );
|
||||
};
|
||||
|
||||
#endif // SINGLEAPPLICATION_P_H
|
||||
@@ -70,6 +70,8 @@ DEFINES += QET_ALLOW_OVERRIDE_CD_OPTION
|
||||
include(sources/PropertiesEditor/PropertiesEditor.pri)
|
||||
include(sources/QetGraphicsItemModeler/QetGraphicsItemModeler.pri)
|
||||
include(sources/QPropertyUndoCommand/QPropertyUndoCommand.pri)
|
||||
include(SingleApplication/singleapplication.pri)
|
||||
DEFINES += QAPPLICATION_CLASS=QApplication
|
||||
|
||||
TEMPLATE = app
|
||||
DEPENDPATH += .
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "qetapp.h"
|
||||
#include "singleapplication.h"
|
||||
#include "qet.h"
|
||||
|
||||
/**
|
||||
* @brief main
|
||||
@@ -37,5 +39,21 @@ int main(int argc, char **argv)
|
||||
#else
|
||||
qputenv("QT_DEVICE_PIXEL_RATIO", QByteArray("auto"));
|
||||
#endif
|
||||
return(QETApp(argc, argv).exec());
|
||||
|
||||
SingleApplication app(argc, argv);
|
||||
|
||||
if (app.isSecondary())
|
||||
{
|
||||
QStringList strl = app.arguments();
|
||||
//Remove the first argument, it's the binary file
|
||||
strl.takeFirst();
|
||||
QString message = "launched-with-args: " + QET::joinWithSpaces(strl);
|
||||
app.sendMessage(message.toUtf8());
|
||||
return 0;
|
||||
}
|
||||
|
||||
QETApp qetapp;
|
||||
QObject::connect(&app, &SingleApplication::receivedMessage, &qetapp, &QETApp::receiveMessage);
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
@@ -60,50 +60,28 @@ RecentFiles *QETApp::m_elements_recent_files = nullptr;
|
||||
TitleBlockTemplate *QETApp::default_titleblock_template_ = nullptr;
|
||||
QString QETApp::m_user_common_elements_dir = QString();
|
||||
QString QETApp::m_user_custom_elements_dir = QString();
|
||||
QETApp *QETApp::m_qetapp = nullptr;
|
||||
|
||||
|
||||
/**
|
||||
Constructeur
|
||||
@param argc Nombre d'arguments passes a l'application
|
||||
@param argv Arguments passes a l'application
|
||||
*/
|
||||
QETApp::QETApp(int &argc, char **argv) :
|
||||
QETSingleApplication(argc, argv, QString("qelectrotech-" + QETApp::userName())),
|
||||
* @brief QETApp::QETApp
|
||||
*/
|
||||
QETApp::QETApp() :
|
||||
m_splash_screen(nullptr),
|
||||
non_interactive_execution_(false)
|
||||
{
|
||||
m_qetapp = this;
|
||||
parseArguments();
|
||||
initConfiguration();
|
||||
initLanguage();
|
||||
QET::Icons::initIcons();
|
||||
initStyle();
|
||||
|
||||
if (!non_interactive_execution_ && isRunning()) {
|
||||
// envoie les arguments a l'instance deja existante
|
||||
non_interactive_execution_ = sendMessage(
|
||||
"launched-with-args: " +
|
||||
QET::joinWithSpaces(QStringList(qet_arguments_.arguments()))
|
||||
);
|
||||
}
|
||||
|
||||
if (non_interactive_execution_) {
|
||||
std::exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
initSplashScreen();
|
||||
initSystemTray();
|
||||
|
||||
// prise en compte des messages des autres instances
|
||||
connect(this, SIGNAL(messageAvailable(QString)), this, SLOT(messageReceived(const QString&)));
|
||||
|
||||
// nettoyage avant de quitter l'application
|
||||
connect(this, SIGNAL(aboutToQuit()), this, SLOT(cleanup()));
|
||||
|
||||
// connexion pour le signalmapper
|
||||
connect(&signal_map, SIGNAL(mapped(QWidget *)), this, SLOT(invertMainWindowVisibility(QWidget *)));
|
||||
|
||||
setQuitOnLastWindowClosed(false);
|
||||
connect(this, SIGNAL(lastWindowClosed()), this, SLOT(checkRemainingWindows()));
|
||||
qApp->setQuitOnLastWindowClosed(false);
|
||||
connect(qApp, &QApplication::lastWindowClosed, this, &QETApp::checkRemainingWindows);
|
||||
|
||||
setSplashScreenStep(tr("Chargement... Initialisation du cache des collections d'éléments", "splash screen caption"));
|
||||
if (!collections_cache_) {
|
||||
@@ -112,14 +90,16 @@ QETApp::QETApp(int &argc, char **argv) :
|
||||
collections_cache_->setLocale(langFromSetting());
|
||||
}
|
||||
|
||||
// on ouvre soit les fichiers passes en parametre soit un nouvel editeur de projet
|
||||
if (qet_arguments_.files().isEmpty()) {
|
||||
if (qet_arguments_.files().isEmpty())
|
||||
{
|
||||
setSplashScreenStep(tr("Chargement... Éditeur de schéma", "splash screen caption"));
|
||||
new QETDiagramEditor();
|
||||
} else {
|
||||
} else
|
||||
{
|
||||
setSplashScreenStep(tr("Chargement... Ouverture des fichiers", "splash screen caption"));
|
||||
openFiles(qet_arguments_);
|
||||
}
|
||||
|
||||
buildSystemTrayMenu();
|
||||
m_splash_screen -> hide();
|
||||
|
||||
@@ -160,8 +140,9 @@ QETApp::~QETApp()
|
||||
/**
|
||||
@return l'instance de la QETApp
|
||||
*/
|
||||
QETApp *QETApp::instance() {
|
||||
return(static_cast<QETApp *>(qApp));
|
||||
QETApp *QETApp::instance()
|
||||
{
|
||||
return m_qetapp;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -176,7 +157,7 @@ void QETApp::setLanguage(const QString &desired_language) {
|
||||
if (!qtTranslator.load("qt_" + desired_language, qt_l10n_path)) {
|
||||
qtTranslator.load("qt_" + desired_language, languages_path);
|
||||
}
|
||||
installTranslator(&qtTranslator);
|
||||
qApp->installTranslator(&qtTranslator);
|
||||
|
||||
// charge les traductions pour l'application QET
|
||||
if (!qetTranslator.load("qet_" + desired_language, languages_path)) {
|
||||
@@ -186,7 +167,7 @@ void QETApp::setLanguage(const QString &desired_language) {
|
||||
qetTranslator.load("qet_en", languages_path);
|
||||
}
|
||||
}
|
||||
installTranslator(&qetTranslator);
|
||||
qApp->installTranslator(&qetTranslator);
|
||||
|
||||
QString ltr_special_string = tr(
|
||||
"LTR",
|
||||
@@ -211,7 +192,7 @@ QString QETApp::langFromSetting()
|
||||
Switches the application to the provided layout.
|
||||
*/
|
||||
void QETApp::switchLayout(Qt::LayoutDirection direction) {
|
||||
setLayoutDirection(direction);
|
||||
qApp->setLayoutDirection(direction);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -440,26 +421,6 @@ TitleBlockTemplatesCollection *QETApp::titleBlockTemplatesCollection(const QStri
|
||||
return(nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
@return le nom de l'utilisateur courant
|
||||
*/
|
||||
QString QETApp::userName() {
|
||||
QProcess * process = new QProcess();
|
||||
QString str;
|
||||
#ifndef Q_OS_WIN32
|
||||
// return(QString(getenv("USER")));
|
||||
str = (process->processEnvironment()).value("USER", "UNKNOWN");
|
||||
delete process;
|
||||
return(str);
|
||||
#else
|
||||
// return(QString(getenv("USERNAME")));
|
||||
str = (process->processEnvironment()).value("USERNAME", "UNKNOWN");
|
||||
delete process;
|
||||
return(str);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief QETApp::commonElementsDir
|
||||
* @return the dir path of the common elements collection.
|
||||
@@ -1015,11 +976,18 @@ QList<QETElementEditor *> QETApp::elementEditors(QETProject *project) {
|
||||
return(editors);
|
||||
}
|
||||
|
||||
/**
|
||||
Nettoie certaines choses avant que l'application ne quitte
|
||||
*/
|
||||
void QETApp::cleanup() {
|
||||
m_qsti -> hide();
|
||||
void QETApp::receiveMessage(int instanceId, QByteArray message)
|
||||
{
|
||||
Q_UNUSED(instanceId);
|
||||
|
||||
QString str(message);
|
||||
|
||||
if (str.startsWith("launched-with-args: "))
|
||||
{
|
||||
QString my_message(str.mid(20));
|
||||
QStringList args_list = QET::splitWithSpaces(my_message);
|
||||
openFiles(QETArguments(args_list));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1028,7 +996,7 @@ void QETApp::cleanup() {
|
||||
*/
|
||||
template <class T> QList<T *> QETApp::detectWindows() const {
|
||||
QList<T *> windows;
|
||||
foreach(QWidget *widget, topLevelWidgets()) {
|
||||
foreach(QWidget *widget, qApp->topLevelWidgets()) {
|
||||
if (!widget -> isWindow()) continue;
|
||||
if (T *window = qobject_cast<T *>(widget)) {
|
||||
windows << window;
|
||||
@@ -1118,8 +1086,8 @@ if defined(Q_OS_WIN)
|
||||
*/
|
||||
void QETApp::useSystemPalette(bool use) {
|
||||
if (use) {
|
||||
setPalette(initial_palette_);
|
||||
setStyleSheet(
|
||||
qApp->setPalette(initial_palette_);
|
||||
qApp->setStyleSheet(
|
||||
"QTabBar::tab:!selected { background-color: transparent; }"
|
||||
"QAbstractScrollArea#mdiarea {"
|
||||
"background-color -> setPalette(initial_palette_);"
|
||||
@@ -1129,7 +1097,7 @@ void QETApp::useSystemPalette(bool use) {
|
||||
QFile file(configDir() + "style.css");
|
||||
file.open(QFile::ReadOnly);
|
||||
QString styleSheet = QLatin1String(file.readAll());
|
||||
setStyleSheet(styleSheet);
|
||||
qApp->setStyleSheet(styleSheet);
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
@@ -1140,7 +1108,7 @@ void QETApp::useSystemPalette(bool use) {
|
||||
*/
|
||||
void QETApp::quitQET() {
|
||||
if (closeEveryEditor()) {
|
||||
quit();
|
||||
qApp->quit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1158,25 +1126,12 @@ void QETApp::checkRemainingWindows() {
|
||||
QTimer::singleShot(500, this, SLOT(checkRemainingWindows()));
|
||||
} else {
|
||||
if (!diagramEditors().count() && !elementEditors().count()) {
|
||||
quit();
|
||||
qApp->quit();
|
||||
}
|
||||
}
|
||||
sleep = !sleep;
|
||||
}
|
||||
|
||||
/**
|
||||
Gere les messages recus
|
||||
@param message Message recu
|
||||
*/
|
||||
void QETApp::messageReceived(const QString &message) {
|
||||
if (message.startsWith("launched-with-args: ")) {
|
||||
QString my_message(message.mid(20));
|
||||
// les arguments sont separes par des espaces non echappes
|
||||
QStringList args_list = QET::splitWithSpaces(my_message);
|
||||
openFiles(QETArguments(args_list));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Ouvre les fichiers passes en arguments
|
||||
@param args Objet contenant des arguments ; les fichiers
|
||||
@@ -1371,7 +1326,7 @@ void QETApp::openTitleBlockTemplateFiles(const QStringList &files_list) {
|
||||
*/
|
||||
void QETApp::configureQET() {
|
||||
// determine le widget parent a utiliser pour le dialogue
|
||||
QWidget *parent_widget = activeWindow();
|
||||
QWidget *parent_widget = qApp->activeWindow();
|
||||
|
||||
// cree le dialogue
|
||||
ConfigDialog cd;
|
||||
@@ -1399,7 +1354,7 @@ void QETApp::configureQET() {
|
||||
*/
|
||||
void QETApp::aboutQET()
|
||||
{
|
||||
AboutQET aq(activeWindow());
|
||||
AboutQET aq(qApp->activeWindow());
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
aq.setWindowFlags(Qt::Sheet);
|
||||
@@ -1413,7 +1368,7 @@ void QETApp::aboutQET()
|
||||
*/
|
||||
QList<QWidget *> QETApp::floatingToolbarsAndDocksForMainWindow(QMainWindow *window) const {
|
||||
QList<QWidget *> widgets;
|
||||
foreach(QWidget *qw, topLevelWidgets()) {
|
||||
foreach(QWidget *qw, qApp->topLevelWidgets()) {
|
||||
if (!qw -> isWindow()) continue;
|
||||
if (qobject_cast<QToolBar *>(qw) || qobject_cast<QDockWidget *>(qw)) {
|
||||
if (qw -> parent() == window) widgets << qw;
|
||||
@@ -1436,7 +1391,7 @@ QList<QWidget *> QETApp::floatingToolbarsAndDocksForMainWindow(QMainWindow *wind
|
||||
*/
|
||||
void QETApp::parseArguments() {
|
||||
// recupere les arguments
|
||||
QList<QString> arguments_list(arguments());
|
||||
QList<QString> arguments_list(qApp->arguments());
|
||||
|
||||
// enleve le premier argument : il s'agit du fichier binaire
|
||||
arguments_list.takeFirst();
|
||||
@@ -1499,7 +1454,7 @@ void QETApp::setSplashScreenStep(const QString &message) {
|
||||
if (!message.isEmpty()) {
|
||||
m_splash_screen -> showMessage(message, Qt::AlignBottom | Qt::AlignLeft);
|
||||
}
|
||||
processEvents();
|
||||
qApp->processEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1514,7 +1469,7 @@ void QETApp::initLanguage() {
|
||||
* Setup the gui style
|
||||
*/
|
||||
void QETApp::initStyle() {
|
||||
initial_palette_ = palette();
|
||||
initial_palette_ = qApp->palette();
|
||||
|
||||
//Apply or not the system style
|
||||
QSettings settings;
|
||||
@@ -1823,7 +1778,7 @@ bool QETApp::event(QEvent *e) {
|
||||
*/
|
||||
void QETApp::printHelp() {
|
||||
QString help(
|
||||
tr("Usage : ") + QFileInfo(applicationFilePath()).fileName() + tr(" [options] [fichier]...\n\n") +
|
||||
tr("Usage : ") + QFileInfo(qApp->applicationFilePath()).fileName() + tr(" [options] [fichier]...\n\n") +
|
||||
tr("QElectroTech, une application de réalisation de schémas électriques.\n\n"
|
||||
"Options disponibles : \n"
|
||||
" --help Afficher l'aide sur les options\n"
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
#include <QTranslator>
|
||||
#include <QSystemTrayIcon>
|
||||
#include <QPalette>
|
||||
#include <QByteArray>
|
||||
|
||||
#include "qetsingleapplication.h"
|
||||
#include "elementslocation.h"
|
||||
#include "templatelocation.h"
|
||||
#include "qetarguments.h"
|
||||
@@ -50,12 +50,12 @@ class RecentFiles;
|
||||
/**
|
||||
This class represents the QElectroTech application.
|
||||
*/
|
||||
class QETApp : public QETSingleApplication
|
||||
class QETApp : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
// constructors, destructor
|
||||
public:
|
||||
QETApp(int &, char **);
|
||||
QETApp();
|
||||
~QETApp() override;
|
||||
|
||||
private:
|
||||
@@ -81,7 +81,6 @@ class QETApp : public QETSingleApplication
|
||||
static QList<TitleBlockTemplatesCollection *> availableTitleBlockTemplatesCollections();
|
||||
static TitleBlockTemplatesCollection *titleBlockTemplatesCollection(const QString &);
|
||||
|
||||
static QString userName();
|
||||
static QString commonElementsDir();
|
||||
static QString customElementsDir();
|
||||
static QString commonElementsDirN();
|
||||
@@ -148,6 +147,7 @@ class QETApp : public QETSingleApplication
|
||||
|
||||
// attributes
|
||||
private:
|
||||
static QETApp *m_qetapp;
|
||||
QTranslator qtTranslator;
|
||||
QTranslator qetTranslator;
|
||||
QSystemTrayIcon *m_qsti;
|
||||
@@ -207,7 +207,6 @@ class QETApp : public QETSingleApplication
|
||||
void useSystemPalette(bool);
|
||||
void quitQET();
|
||||
void checkRemainingWindows();
|
||||
void messageReceived(const QString &);
|
||||
void openFiles(const QETArguments &);
|
||||
void openProjectFiles(const QStringList &);
|
||||
void openElementFiles(const QStringList &);
|
||||
@@ -217,9 +216,7 @@ class QETApp : public QETSingleApplication
|
||||
void openTitleBlockTemplateFiles(const QStringList &);
|
||||
void configureQET();
|
||||
void aboutQET();
|
||||
|
||||
private slots:
|
||||
void cleanup();
|
||||
void receiveMessage(int instanceId, QByteArray message);
|
||||
|
||||
private:
|
||||
template <class T> QList<T *> detectWindows() const;
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
Copyright 2006-2017 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech 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.
|
||||
|
||||
QElectroTech is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTAvBILITY 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 QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "qetsingleapplication.h"
|
||||
#include <QLocalSocket>
|
||||
|
||||
const int QETSingleApplication::timeout_ = 10000;
|
||||
|
||||
/**
|
||||
Constructeur
|
||||
@param argc Nombre d'arguments passes au programme par le systeme
|
||||
@param argv Tableau des arguments passes au programme par le systeme
|
||||
@param unique_key Cle unique
|
||||
*/
|
||||
QETSingleApplication::QETSingleApplication(int &argc, char **argv, const QString& unique_key) :
|
||||
QApplication(argc, argv),
|
||||
unique_key_(unique_key)
|
||||
{
|
||||
// verifie s'il y a un segment de memoire partage correspondant a la cle unique
|
||||
#if defined (Q_OS_OS2)
|
||||
#define QT_NO_SHAREDMEMORY
|
||||
{
|
||||
#else
|
||||
shared_memory_.setKey(unique_key_);
|
||||
if (shared_memory_.attach()) {
|
||||
// oui : l'application est deja en cours d'execution
|
||||
is_running_ = true;
|
||||
} else {
|
||||
// non : il s'agit du premier demarrage de l'application pour cette cle unique
|
||||
is_running_ = false;
|
||||
|
||||
// initialisation du segment de memoire partage
|
||||
if (!shared_memory_.create(1)) {
|
||||
qDebug() << "QETSingleApplication::QETSingleApplication() : Impossible de créer l'instance unique" << qPrintable(unique_key_);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// initialisation d'un serveur local pour recevoir les messages des autres instances
|
||||
local_server_ = new QLocalServer(this);
|
||||
connect(local_server_, SIGNAL(newConnection()), this, SLOT(receiveMessage()));
|
||||
// la cle unique est egalement utilise pour le serveur
|
||||
local_server_ -> listen(unique_key_);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Destructeur
|
||||
*/
|
||||
QETSingleApplication::~QETSingleApplication() {
|
||||
}
|
||||
|
||||
/**
|
||||
Slot gerant la reception des messages.
|
||||
Lorsque l'application recoit un message, ce slot emet le signal
|
||||
messageAvailable avec le message recu.
|
||||
*/
|
||||
void QETSingleApplication::receiveMessage() {
|
||||
QLocalSocket *local_socket = local_server_ -> nextPendingConnection();
|
||||
if (!local_socket -> waitForReadyRead(timeout_)) {
|
||||
qDebug() << "QETSingleApplication::receiveMessage() :" << qPrintable(local_socket -> errorString()) << "(" << qPrintable(unique_key_) << ")";
|
||||
return;
|
||||
}
|
||||
QByteArray byteArray = local_socket -> readAll();
|
||||
QString message = QString::fromUtf8(byteArray.constData());
|
||||
emit(messageAvailable(message));
|
||||
local_socket -> disconnectFromServer();
|
||||
}
|
||||
|
||||
/**
|
||||
@return true si l'application est deja en cours d'execution
|
||||
*/
|
||||
bool QETSingleApplication::isRunning() {
|
||||
return(is_running_);
|
||||
}
|
||||
|
||||
/**
|
||||
Envoie un message a l'application. Si celle-ci n'est pas en cours
|
||||
d'execution, cette methode ne fait rien.
|
||||
@param message Message a transmettre a l'application
|
||||
@return true si le message a ete tranmis, false sinon
|
||||
*/
|
||||
bool QETSingleApplication::sendMessage(const QString &message) {
|
||||
// l'application doit etre en cours d'execution
|
||||
if (!is_running_) {
|
||||
return(false);
|
||||
}
|
||||
|
||||
// se connecte a l'application, avec gestion du timeout
|
||||
QLocalSocket local_socket(this);
|
||||
local_socket.connectToServer(unique_key_, QIODevice::WriteOnly);
|
||||
if (!local_socket.waitForConnected(timeout_)) {
|
||||
qDebug() << "QETSingleApplication::sendMessage() :" << qPrintable(local_socket.errorString()) << "(" << qPrintable(unique_key_) << ")";
|
||||
return(false);
|
||||
}
|
||||
|
||||
// envoi du message, avec gestion du timeout
|
||||
local_socket.write(message.toUtf8());
|
||||
if (!local_socket.waitForBytesWritten(timeout_)) {
|
||||
qDebug() << "QETSingleApplication::sendMessage() :" << qPrintable(local_socket.errorString()) << "(" << qPrintable(unique_key_) << ")";
|
||||
return(false);
|
||||
}
|
||||
|
||||
// deconnexion
|
||||
local_socket.disconnectFromServer();
|
||||
return(true);
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
Copyright 2006-2017 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech 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.
|
||||
|
||||
QElectroTech 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 QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef QET_SINGLE_APPLICATION_H
|
||||
#define QET_SINGLE_APPLICATION_H
|
||||
#include <QApplication>
|
||||
#include <QSharedMemory>
|
||||
#include <QLocalServer>
|
||||
/**
|
||||
This class represents a Qt Application executing only a single instance
|
||||
depending on a unique string key.
|
||||
*/
|
||||
class QETSingleApplication : public QApplication {
|
||||
Q_OBJECT
|
||||
// constructors, destructor
|
||||
public:
|
||||
QETSingleApplication(int &, char **, const QString&);
|
||||
~QETSingleApplication() override;
|
||||
|
||||
private:
|
||||
QETSingleApplication(const QETSingleApplication &);
|
||||
|
||||
// methods
|
||||
public:
|
||||
bool isRunning();
|
||||
bool sendMessage(const QString &);
|
||||
|
||||
public slots:
|
||||
void receiveMessage();
|
||||
|
||||
signals:
|
||||
void messageAvailable(QString);
|
||||
|
||||
// attributes
|
||||
private:
|
||||
bool is_running_;
|
||||
QString unique_key_;
|
||||
#if defined (Q_OS_OS2)
|
||||
#define QT_NO_SHAREDMEMORY
|
||||
#else
|
||||
QSharedMemory shared_memory_;
|
||||
#endif
|
||||
QLocalServer *local_server_;
|
||||
static const int timeout_;
|
||||
};
|
||||
#endif
|
||||
Reference in New Issue
Block a user