[qet] qet/qet: [5481] Replace qetsingleapplication by singleApplication. |
[ Thread Index |
Date Index
| More lists.tuxfamily.org/qet Archives
]
Revision: 5481
Author: blacksun
Date: 2018-08-15 14:45:55 +0200 (Wed, 15 Aug 2018)
Log Message:
-----------
Replace qetsingleapplication by singleApplication.
See : https://github.com/itay-grudev/SingleApplication
Modified Paths:
--------------
trunk/qelectrotech.pro
trunk/sources/main.cpp
trunk/sources/qetapp.cpp
trunk/sources/qetapp.h
Added Paths:
-----------
trunk/SingleApplication/
trunk/SingleApplication/CHANGELOG.md
trunk/SingleApplication/LICENSE
trunk/SingleApplication/README.md
trunk/SingleApplication/Windows.md
trunk/SingleApplication/examples/
trunk/SingleApplication/examples/basic/
trunk/SingleApplication/examples/basic/basic.pro
trunk/SingleApplication/examples/basic/main.cpp
trunk/SingleApplication/examples/calculator/
trunk/SingleApplication/examples/calculator/button.cpp
trunk/SingleApplication/examples/calculator/button.h
trunk/SingleApplication/examples/calculator/calculator.cpp
trunk/SingleApplication/examples/calculator/calculator.h
trunk/SingleApplication/examples/calculator/calculator.pro
trunk/SingleApplication/examples/calculator/main.cpp
trunk/SingleApplication/examples/sending_arguments/
trunk/SingleApplication/examples/sending_arguments/main.cpp
trunk/SingleApplication/examples/sending_arguments/messagereceiver.cpp
trunk/SingleApplication/examples/sending_arguments/messagereceiver.h
trunk/SingleApplication/examples/sending_arguments/sending_arguments.pro
trunk/SingleApplication/singleapplication.cpp
trunk/SingleApplication/singleapplication.h
trunk/SingleApplication/singleapplication.pri
trunk/SingleApplication/singleapplication_p.cpp
trunk/SingleApplication/singleapplication_p.h
Removed Paths:
-------------
trunk/sources/qetsingleapplication.cpp
trunk/sources/qetsingleapplication.h
Added: trunk/SingleApplication/CHANGELOG.md
===================================================================
--- trunk/SingleApplication/CHANGELOG.md (rev 0)
+++ trunk/SingleApplication/CHANGELOG.md 2018-08-15 12:45:55 UTC (rev 5481)
@@ -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.
Added: trunk/SingleApplication/LICENSE
===================================================================
--- trunk/SingleApplication/LICENSE (rev 0)
+++ trunk/SingleApplication/LICENSE 2018-08-15 12:45:55 UTC (rev 5481)
@@ -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.
Added: trunk/SingleApplication/README.md
===================================================================
--- trunk/SingleApplication/README.md (rev 0)
+++ trunk/SingleApplication/README.md 2018-08-15 12:45:55 UTC (rev 5481)
@@ -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@xxxxxxxxxx: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.
Added: trunk/SingleApplication/Windows.md
===================================================================
--- trunk/SingleApplication/Windows.md (rev 0)
+++ trunk/SingleApplication/Windows.md 2018-08-15 12:45:55 UTC (rev 5481)
@@ -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
Added: trunk/SingleApplication/examples/basic/basic.pro
===================================================================
--- trunk/SingleApplication/examples/basic/basic.pro (rev 0)
+++ trunk/SingleApplication/examples/basic/basic.pro 2018-08-15 12:45:55 UTC (rev 5481)
@@ -0,0 +1,5 @@
+# Single Application implementation
+include(../../singleapplication.pri)
+DEFINES += QAPPLICATION_CLASS=QCoreApplication
+
+SOURCES += main.cpp
Property changes on: trunk/SingleApplication/examples/basic/basic.pro
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Added: trunk/SingleApplication/examples/basic/main.cpp
===================================================================
--- trunk/SingleApplication/examples/basic/main.cpp (rev 0)
+++ trunk/SingleApplication/examples/basic/main.cpp 2018-08-15 12:45:55 UTC (rev 5481)
@@ -0,0 +1,9 @@
+#include <singleapplication.h>
+
+int main(int argc, char *argv[])
+{
+ // Allow secondary instances
+ SingleApplication app( argc, argv );
+
+ return app.exec();
+}
Property changes on: trunk/SingleApplication/examples/basic/main.cpp
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Added: trunk/SingleApplication/examples/calculator/button.cpp
===================================================================
--- trunk/SingleApplication/examples/calculator/button.cpp (rev 0)
+++ trunk/SingleApplication/examples/calculator/button.cpp 2018-08-15 12:45:55 UTC (rev 5481)
@@ -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]
Added: trunk/SingleApplication/examples/calculator/button.h
===================================================================
--- trunk/SingleApplication/examples/calculator/button.h (rev 0)
+++ trunk/SingleApplication/examples/calculator/button.h 2018-08-15 12:45:55 UTC (rev 5481)
@@ -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
Added: trunk/SingleApplication/examples/calculator/calculator.cpp
===================================================================
--- trunk/SingleApplication/examples/calculator/calculator.cpp (rev 0)
+++ trunk/SingleApplication/examples/calculator/calculator.cpp 2018-08-15 12:45:55 UTC (rev 5481)
@@ -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]
Added: trunk/SingleApplication/examples/calculator/calculator.h
===================================================================
--- trunk/SingleApplication/examples/calculator/calculator.h (rev 0)
+++ trunk/SingleApplication/examples/calculator/calculator.h 2018-08-15 12:45:55 UTC (rev 5481)
@@ -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
Added: trunk/SingleApplication/examples/calculator/calculator.pro
===================================================================
--- trunk/SingleApplication/examples/calculator/calculator.pro (rev 0)
+++ trunk/SingleApplication/examples/calculator/calculator.pro 2018-08-15 12:45:55 UTC (rev 5481)
@@ -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
Added: trunk/SingleApplication/examples/calculator/main.cpp
===================================================================
--- trunk/SingleApplication/examples/calculator/main.cpp (rev 0)
+++ trunk/SingleApplication/examples/calculator/main.cpp 2018-08-15 12:45:55 UTC (rev 5481)
@@ -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();
+}
Added: trunk/SingleApplication/examples/sending_arguments/main.cpp
===================================================================
--- trunk/SingleApplication/examples/sending_arguments/main.cpp (rev 0)
+++ trunk/SingleApplication/examples/sending_arguments/main.cpp 2018-08-15 12:45:55 UTC (rev 5481)
@@ -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();
+}
Property changes on: trunk/SingleApplication/examples/sending_arguments/main.cpp
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Added: trunk/SingleApplication/examples/sending_arguments/messagereceiver.cpp
===================================================================
--- trunk/SingleApplication/examples/sending_arguments/messagereceiver.cpp (rev 0)
+++ trunk/SingleApplication/examples/sending_arguments/messagereceiver.cpp 2018-08-15 12:45:55 UTC (rev 5481)
@@ -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;
+}
Added: trunk/SingleApplication/examples/sending_arguments/messagereceiver.h
===================================================================
--- trunk/SingleApplication/examples/sending_arguments/messagereceiver.h (rev 0)
+++ trunk/SingleApplication/examples/sending_arguments/messagereceiver.h 2018-08-15 12:45:55 UTC (rev 5481)
@@ -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
Added: trunk/SingleApplication/examples/sending_arguments/sending_arguments.pro
===================================================================
--- trunk/SingleApplication/examples/sending_arguments/sending_arguments.pro (rev 0)
+++ trunk/SingleApplication/examples/sending_arguments/sending_arguments.pro 2018-08-15 12:45:55 UTC (rev 5481)
@@ -0,0 +1,9 @@
+# Single Application implementation
+include(../../singleapplication.pri)
+DEFINES += QAPPLICATION_CLASS=QCoreApplication
+
+SOURCES += main.cpp \
+ messagereceiver.cpp
+
+HEADERS += \
+ messagereceiver.h
Property changes on: trunk/SingleApplication/examples/sending_arguments/sending_arguments.pro
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Added: trunk/SingleApplication/singleapplication.cpp
===================================================================
--- trunk/SingleApplication/singleapplication.cpp (rev 0)
+++ trunk/SingleApplication/singleapplication.cpp 2018-08-15 12:45:55 UTC (rev 5481)
@@ -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;
+}
Added: trunk/SingleApplication/singleapplication.h
===================================================================
--- trunk/SingleApplication/singleapplication.h (rev 0)
+++ trunk/SingleApplication/singleapplication.h 2018-08-15 12:45:55 UTC (rev 5481)
@@ -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
Added: trunk/SingleApplication/singleapplication.pri
===================================================================
--- trunk/SingleApplication/singleapplication.pri (rev 0)
+++ trunk/SingleApplication/singleapplication.pri 2018-08-15 12:45:55 UTC (rev 5481)
@@ -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
Added: trunk/SingleApplication/singleapplication_p.cpp
===================================================================
--- trunk/SingleApplication/singleapplication_p.cpp (rev 0)
+++ trunk/SingleApplication/singleapplication_p.cpp 2018-08-15 12:45:55 UTC (rev 5481)
@@ -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();
+}
Added: trunk/SingleApplication/singleapplication_p.h
===================================================================
--- trunk/SingleApplication/singleapplication_p.h (rev 0)
+++ trunk/SingleApplication/singleapplication_p.h 2018-08-15 12:45:55 UTC (rev 5481)
@@ -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
Modified: trunk/qelectrotech.pro
===================================================================
--- trunk/qelectrotech.pro 2018-08-13 17:57:33 UTC (rev 5480)
+++ trunk/qelectrotech.pro 2018-08-15 12:45:55 UTC (rev 5481)
@@ -70,6 +70,8 @@
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 += .
Modified: trunk/sources/main.cpp
===================================================================
--- trunk/sources/main.cpp 2018-08-13 17:57:33 UTC (rev 5480)
+++ trunk/sources/main.cpp 2018-08-15 12:45:55 UTC (rev 5481)
@@ -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 @@
#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();
}
Modified: trunk/sources/qetapp.cpp
===================================================================
--- trunk/sources/qetapp.cpp 2018-08-13 17:57:33 UTC (rev 5480)
+++ trunk/sources/qetapp.cpp 2018-08-15 12:45:55 UTC (rev 5481)
@@ -60,51 +60,29 @@
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 *)));
+ qApp->setQuitOnLastWindowClosed(false);
+ connect(qApp, &QApplication::lastWindowClosed, this, &QETApp::checkRemainingWindows);
- setQuitOnLastWindowClosed(false);
- connect(this, SIGNAL(lastWindowClosed()), this, SLOT(checkRemainingWindows()));
-
setSplashScreenStep(tr("Chargement... Initialisation du cache des collections d'éléments", "splash screen caption"));
if (!collections_cache_) {
QString cache_path = QETApp::configDir() + "/elements_cache.sqlite";
@@ -112,14 +90,16 @@
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 @@
/**
@return l'instance de la QETApp
*/
-QETApp *QETApp::instance() {
- return(static_cast<QETApp *>(qApp));
+QETApp *QETApp::instance()
+{
+ return m_qetapp;
}
/**
@@ -176,7 +157,7 @@
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 @@
qetTranslator.load("qet_en", languages_path);
}
}
- installTranslator(&qetTranslator);
+ qApp->installTranslator(&qetTranslator);
QString ltr_special_string = tr(
"LTR",
@@ -211,7 +192,7 @@
Switches the application to the provided layout.
*/
void QETApp::switchLayout(Qt::LayoutDirection direction) {
- setLayoutDirection(direction);
+ qApp->setLayoutDirection(direction);
}
/**
@@ -441,26 +422,6 @@
}
/**
- @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 @@
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 @@
*/
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 @@
*/
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 @@
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::quitQET() {
if (closeEveryEditor()) {
- quit();
+ qApp->quit();
}
}
@@ -1158,7 +1126,7 @@
QTimer::singleShot(500, this, SLOT(checkRemainingWindows()));
} else {
if (!diagramEditors().count() && !elementEditors().count()) {
- quit();
+ qApp->quit();
}
}
sleep = !sleep;
@@ -1165,19 +1133,6 @@
}
/**
- 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
@see openProjectFiles openElementFiles
@@ -1371,7 +1326,7 @@
*/
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::aboutQET()
{
- AboutQET aq(activeWindow());
+ AboutQET aq(qApp->activeWindow());
#ifdef Q_OS_MACOS
aq.setWindowFlags(Qt::Sheet);
@@ -1413,7 +1368,7 @@
*/
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 @@
*/
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 @@
if (!message.isEmpty()) {
m_splash_screen -> showMessage(message, Qt::AlignBottom | Qt::AlignLeft);
}
- processEvents();
+ qApp->processEvents();
}
/**
@@ -1514,7 +1469,7 @@
* 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 @@
*/
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"
Modified: trunk/sources/qetapp.h
===================================================================
--- trunk/sources/qetapp.h 2018-08-13 17:57:33 UTC (rev 5480)
+++ trunk/sources/qetapp.h 2018-08-15 12:45:55 UTC (rev 5481)
@@ -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 @@
/**
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 @@
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 @@
// attributes
private:
+ static QETApp *m_qetapp;
QTranslator qtTranslator;
QTranslator qetTranslator;
QSystemTrayIcon *m_qsti;
@@ -207,7 +207,6 @@
void useSystemPalette(bool);
void quitQET();
void checkRemainingWindows();
- void messageReceived(const QString &);
void openFiles(const QETArguments &);
void openProjectFiles(const QStringList &);
void openElementFiles(const QStringList &);
@@ -217,10 +216,8 @@
void openTitleBlockTemplateFiles(const QStringList &);
void configureQET();
void aboutQET();
+ void receiveMessage(int instanceId, QByteArray message);
- private slots:
- void cleanup();
-
private:
template <class T> QList<T *> detectWindows() const;
template <class T> void setMainWindowsVisible(bool);
Deleted: trunk/sources/qetsingleapplication.cpp
===================================================================
--- trunk/sources/qetsingleapplication.cpp 2018-08-13 17:57:33 UTC (rev 5480)
+++ trunk/sources/qetsingleapplication.cpp 2018-08-15 12:45:55 UTC (rev 5481)
@@ -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);
-}
Deleted: trunk/sources/qetsingleapplication.h
===================================================================
--- trunk/sources/qetsingleapplication.h 2018-08-13 17:57:33 UTC (rev 5480)
+++ trunk/sources/qetsingleapplication.h 2018-08-15 12:45:55 UTC (rev 5481)
@@ -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