[calligra/calligra/2.9] krita: Make Krita a QtSingleApplication
Boudewijn Rempt
boud at valdyas.org
Wed Dec 31 12:05:41 UTC 2014
Git commit 5890d194a868e2bea40392aac5735dc4f6a2fb7c by Boudewijn Rempt.
Committed on 30/12/2014 at 15:22.
Pushed by rempt into branch 'calligra/2.9'.
Make Krita a QtSingleApplication
CCMAIL:kimageshop at kde.org
If there is a running instance of Krita, and you open another image with Krita,
or start Krita from the commandline, the files in the arguments are opened in
the first Krita.
This also solves synchronization issues with the tag store, as well as possible
issues with setting shortcuts. (Though not all shortcut issues are solved yet).
BUG:342187 Possibility to open images in one krita app
BUG:318208 With multiple instance of Krita open, preset tags get out of sync
M +5 -13 krita/CMakeLists.txt
M +2 -2 krita/krita.desktop
D +0 -94 krita/kritapart.desktop
M +36 -5 krita/main.cc
M +1 -1 krita/plugins/extensions/dockers/advancedcolorselector/kis_color_selector_ng_docker_widget.cpp
M +4 -3 krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp
M +10 -0 krita/ui/CMakeLists.txt
M +71 -22 krita/ui/KisApplication.cpp
M +8 -8 krita/ui/KisApplication.h
M +10 -0 krita/ui/KisDocumentEntry.cpp
M +4 -0 krita/ui/KisDocumentEntry.h
M +1 -6 krita/ui/KisFilterChain.cpp
M +17 -13 krita/ui/KisMainWindow.cpp
M +6 -13 krita/ui/KisMainWindow.h
M +14 -5 krita/ui/kis_action_manager.cpp
M +15 -31 krita/ui/kis_factory2.cc
M +5 -11 krita/ui/kis_factory2.h
A +10 -0 krita/ui/qtlockedfile/README.txt
A +157 -0 krita/ui/qtlockedfile/qtlockedfile.cpp [License: LGPL (v3)]
A +76 -0 krita/ui/qtlockedfile/qtlockedfile.h [License: LGPL (v3)]
A +13 -0 krita/ui/qtlockedfile/qtlockedfile.pri
A +107 -0 krita/ui/qtlockedfile/qtlockedfile_unix.cpp [License: LGPL (v3)]
A +196 -0 krita/ui/qtlockedfile/qtlockedfile_win.cpp [License: LGPL (v3)]
A +10 -0 krita/ui/qtsingleapplication/README.txt
A +180 -0 krita/ui/qtsingleapplication/qtlocalpeer.cpp [License: LGPL (v3)]
A +62 -0 krita/ui/qtsingleapplication/qtlocalpeer.h [License: LGPL (v3)]
A +195 -0 krita/ui/qtsingleapplication/qtsingleapplication.cpp [License: LGPL (v3)]
A +73 -0 krita/ui/qtsingleapplication/qtsingleapplication.h [License: LGPL (v3)]
A +14 -0 krita/ui/qtsingleapplication/qtsingleapplication.pri
http://commits.kde.org/calligra/5890d194a868e2bea40392aac5735dc4f6a2fb7c
diff --git a/krita/CMakeLists.txt b/krita/CMakeLists.txt
index f392751..aa1cd8e 100644
--- a/krita/CMakeLists.txt
+++ b/krita/CMakeLists.txt
@@ -141,20 +141,14 @@ if (NOT WIN32)
add_subdirectory( benchmarks )
endif (NOT WIN32)
-set(kritapart_PART_SRCS krita_part_init.cc)
-kde4_add_plugin(kritapart ${kritapart_PART_SRCS})
-target_link_libraries(kritapart kritaui)
-install(TARGETS kritapart DESTINATION ${PLUGIN_INSTALL_DIR})
-
-set(krita_KDEINIT_SRCS main.cc)
+set(krita_SRCS main.cc)
if(WIN32 AND USE_BREAKPAD)
- set(krita_KDEINIT_SRCS ${krita_KDEINIT_SRCS} kis_crash_handler.cpp)
+ set(krita_SRCS ${krita_SRCS} kis_crash_handler.cpp)
set(BREAKPAD_LIBS breakpad)
endif(WIN32 AND USE_BREAKPAD)
-kde4_add_app_icon(krita_KDEINIT_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/pics/app/hi*-app-calligrakrita.png")
-kde4_add_kdeinit_executable(krita ${krita_KDEINIT_SRCS})
+kde4_add_app_icon(krita_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/pics/app/hi*-app-calligrakrita.png")
if (Q_WS_MAC)
set_target_properties(krita PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.template)
@@ -162,13 +156,11 @@ if (Q_WS_MAC)
set_target_properties(krita PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Krita")
endif (Q_WS_MAC)
-target_link_libraries(kdeinit_krita kritaui ${BREAKPAD_LIBS})
-target_link_libraries(krita kdeinit_krita kritaui)
+kde4_add_executable(krita ${krita_SRCS})
+target_link_libraries(krita kritaui ${BREAKPAD_LIBS})
install(TARGETS krita ${INSTALL_TARGETS_DEFAULT_ARGS})
-install(TARGETS kdeinit_krita ${INSTALL_TARGETS_DEFAULT_ARGS})
install(PROGRAMS krita.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}/calligra)
-install(FILES kritapart.desktop DESTINATION ${SERVICES_INSTALL_DIR}/calligra)
install(FILES krita.rc DESTINATION ${DATA_INSTALL_DIR}/krita)
install(FILES krita.appdata.xml DESTINATION ${SHARE_INSTALL_PREFIX}/appdata/ )
diff --git a/krita/krita.desktop b/krita/krita.desktop
index 115dd0c..a4b3451 100644
--- a/krita/krita.desktop
+++ b/krita/krita.desktop
@@ -131,9 +131,9 @@ Type=Application
Icon=calligrakrita
Categories=Qt;KDE;Graphics;
X-KDE-ServiceTypes=Calligra/Application
-X-Calligra-DefaultMimeTypes=application/x-krita;application/x-krita-animation
+X-Calligra-DefaultMimeTypes=application/x-krita
X-KDE-NativeMimeType=application/x-krita
-X-KDE-ExtraNativeMimeTypes=application/x-krita-animation
+X-KDE-ExtraNativeMimeTypes=
StartupNotify=true
X-DBUS-StartupType=Multi
X-DBUS-ServiceName=org.krita.krita
diff --git a/krita/kritapart.desktop b/krita/kritapart.desktop
deleted file mode 100644
index 9ed038a..0000000
--- a/krita/kritapart.desktop
+++ /dev/null
@@ -1,94 +0,0 @@
-[Desktop Entry]
-Name=Calligra Painting and Image Editor Component
-Name[bg]=Компонент за рисуване и редактиране на изображения в Calligra
-Name[bs]=Clligra Komponenta za Bojenje i Editovanje Slika
-Name[ca]=Component de dibuix i manipulació d'imatges del Calligra
-Name[ca at valencia]=Component de dibuix i manipulació d'imatges del Calligra
-Name[cs]=Komponenta Calligra pro malování a úpravu fotografií
-Name[da]=Calligra-komponent til tegning og billedredigeringskomponent
-Name[de]=Calligra-Komponente für Malen und Bildbearbeitung
-Name[el]=Συστατικό επεξεργασίας και ζωγραφικής εικόνων του Calligra
-Name[en_GB]=Calligra Painting and Image Editor Component
-Name[es]=Componente de pintura y de edición de imágenes de Calligra
-Name[et]=Calligra joonistamise ja pilditöötluse komponent
-Name[eu]=Calligra-ren pinturaren eta irudi-editorearen osagaia
-Name[fi]=Calligran maalaus- ja kuvankäsittelyosa
-Name[fr]=Composant manipulation d'images et dessin de Calligra
-Name[gl]=Compoñente para Calligra de debuxo e edición de imaxes
-Name[hu]=Calligra rajzoló és képszerkesztő komponens
-Name[it]=Componente per il disegno e la manipolazione di immagini di Calligra
-Name[ja]=Calligra 描画と画像編集コンポーネント
-Name[kk]=Calligra-ның кескінін салу және өңдеу бағдарламасы
-Name[ko]=Calligra 그리기 및 그림 편집기 구성 요소
-Name[nb]=Calligra-komponent for maling og bildemanipulasjon
-Name[nds]=Calligra-Komponent för't Malen un Bildbewerken
-Name[nl]=Calligra-component voor tekenen en afbeeldingsbewerking
-Name[pl]=Składnik malowania i edycji obrazu dla Calligry
-Name[pt]=Componente de Edição e Pintura de Imagens do Calligra
-Name[pt_BR]=Componente de Edição e Pintura de Imagens do Calligra
-Name[ru]=Компонент рисования и редактирования изображений Calligra
-Name[sk]=Calligra modul na úpravu a maľovanie obrázkov
-Name[sl]=Komponenta za slikanje in urejanje slik za Calligro
-Name[sv]=Calligra målnings- och bildredigeringskomponent
-Name[uk]=Компонент Calligra для малювання і редагування зображень
-Name[x-test]=xxCalligra Painting and Image Editor Componentxx
-Name[zh_CN]=Calligra 绘图和图像编辑器组件
-Name[zh_TW]=Calligra 繪圖與影像編輯元件
-X-KDE-Library=kritapart
-MimeType=application/x-krita;image/openraster;
-Type=Service
-X-KDE-ServiceTypes=Calligra/Part
-X-KDE-NativeMimeType=application/x-krita
-GenericName=Image Object
-GenericName[bg]=Графично изображение
-GenericName[br]=Tra skeudenn
-GenericName[bs]=Objekat slike
-GenericName[ca]=Objecte d'imatge
-GenericName[ca at valencia]=Objecte d'imatge
-GenericName[cy]=Gwrthrych Delwedd
-GenericName[da]=Billedobjekt
-GenericName[de]=Bildobjekt
-GenericName[el]=Αντικείμενο εικόνας
-GenericName[en_GB]=Image Object
-GenericName[eo]=Bildobjekto
-GenericName[es]=Objeto de imagen
-GenericName[et]=Pildiobjekt
-GenericName[eu]=Irudi-objektua
-GenericName[fa]=شیء تصویر
-GenericName[fi]=Kuvaobjekti
-GenericName[fr]=Objet image
-GenericName[fy]=Ofbylding
-GenericName[ga]=Réad Íomhá
-GenericName[gl]=Obxecto de imaxe
-GenericName[he]=אובייקט תמונה
-GenericName[hi]=छवि ऑब्जेक्ट
-GenericName[hne]=फोटू आब्जेक्ट
-GenericName[hr]=Objekt slike
-GenericName[hu]=Képobjektum
-GenericName[is]=Myndhluti
-GenericName[it]=Oggetto immagine
-GenericName[ja]=画像オブジェクト
-GenericName[kk]=Кескін
-GenericName[ko]=그림 객체
-GenericName[lv]=Attēla objekts
-GenericName[nb]=Bildeobjekt
-GenericName[nds]=Bildobjekt
-GenericName[ne]=छवि वस्तु
-GenericName[nl]=Afbeelding
-GenericName[pl]=Obiekt obrazu
-GenericName[pt]=Objecto de Imagem
-GenericName[pt_BR]=Objeto de Imagem
-GenericName[ru]=Рисунок
-GenericName[sk]=Objekt obrázok
-GenericName[sl]=Slika
-GenericName[sv]=Bildobjekt
-GenericName[tr]=Resim Nesnesi
-GenericName[uk]=Об’єкт зображення
-GenericName[uz]=Rasm obʼekti
-GenericName[uz at cyrillic]=Расм объекти
-GenericName[wa]=Cayet imådje
-GenericName[x-test]=xxImage Objectxx
-GenericName[zh_CN]=图像对象
-GenericName[zh_TW]=圖片物件
-Icon=calligrakrita
-X-Krita-Version=28
diff --git a/krita/main.cc b/krita/main.cc
index c2121da..ffe91c6 100644
--- a/krita/main.cc
+++ b/krita/main.cc
@@ -54,7 +54,7 @@
#endif
-extern "C" KDE_EXPORT int kdemain(int argc, char **argv)
+extern "C" int main(int argc, char **argv)
{
bool runningInKDE = !qgetenv("KDE_FULL_SESSION").isEmpty();
@@ -63,6 +63,7 @@ extern "C" KDE_EXPORT int kdemain(int argc, char **argv)
qputenv("QT_NO_GLIB", "1");
}
#endif
+
#ifdef USE_BREAKPAD
qputenv("KDE_DEBUG", "1");
KisCrashHandler crashHandler;
@@ -74,6 +75,8 @@ extern "C" KDE_EXPORT int kdemain(int argc, char **argv)
#endif
int state;
+ KisFactory factory;
+ Q_UNUSED(factory); // Not really, it'll self-destruct on exiting main
KAboutData *aboutData = KisFactory::aboutData();
KCmdLineArgs::init(argc, argv, aboutData);
@@ -90,8 +93,29 @@ extern "C" KDE_EXPORT int kdemain(int argc, char **argv)
KCmdLineArgs::addCmdLineOptions(options);
+ // A per-user unique string, without /, because QLocalServer cannot use names with a / in it
+ QString key = "Krita" +
+ QDesktopServices::storageLocation(QDesktopServices::HomeLocation).replace("/", "_");
+ key = key.replace(":", "_").replace("\\","_");
+
+ // initialize qt plugin path (see KComponentDataPrivate::lazyInit)
+ KGlobal::config();
+
// first create the application so we can create a pixmap
- KisApplication app;
+ KisApplication app(key);
+
+ if (app.isRunning()) {
+
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ QByteArray ba;
+ QDataStream ds(&ba, QIODevice::WriteOnly);
+ args->saveAppArgs(ds);
+ ds.device()->close();
+
+ if (app.sendMessage(ba)) {
+ return 0;
+ }
+ }
#if defined Q_OS_WIN
KisTabletSupportWin::init();
@@ -105,9 +129,8 @@ extern "C" KDE_EXPORT int kdemain(int argc, char **argv)
app.setAttribute(Qt::AA_X11InitThreads, true);
#endif
- if (!runningInKDE) {
- app.setAttribute(Qt::AA_DontShowIconsInMenus);
- }
+ // Icons in menus are ugly and distracting
+ app.setAttribute(Qt::AA_DontShowIconsInMenus);
// then create the pixmap from an xpm: we cannot get the
// location of our datadir before we've started our components,
@@ -119,6 +142,14 @@ extern "C" KDE_EXPORT int kdemain(int argc, char **argv)
return 1;
}
+ // Set up remote arguments.
+ QObject::connect(&app, SIGNAL(messageReceived(QByteArray,QObject*)),
+ &app, SLOT(remoteArguments(QByteArray,QObject*)));
+
+ QObject::connect(&app, SIGNAL(fileOpenRequest(QString)),
+ &app, SLOT(fileOpenRequested(QString)));
+
+
state = app.exec();
return state;
diff --git a/krita/plugins/extensions/dockers/advancedcolorselector/kis_color_selector_ng_docker_widget.cpp b/krita/plugins/extensions/dockers/advancedcolorselector/kis_color_selector_ng_docker_widget.cpp
index 8b025db..079826c 100644
--- a/krita/plugins/extensions/dockers/advancedcolorselector/kis_color_selector_ng_docker_widget.cpp
+++ b/krita/plugins/extensions/dockers/advancedcolorselector/kis_color_selector_ng_docker_widget.cpp
@@ -23,7 +23,7 @@
#include <kconfig.h>
#include <kconfiggroup.h>
-#include <kcomponentdata.h>
+
#include <kglobal.h>
#include <kaction.h>
#include <kactioncollection.h>
diff --git a/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp b/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp
index 81532af..3db8b6e 100644
--- a/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp
+++ b/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp
@@ -44,7 +44,6 @@
#include <kis_debug.h>
#include <kmenu.h>
#include <klocale.h>
-#include <kactioncollection.h>
#include <KoIcon.h>
#include <KisDocumentSectionView.h>
@@ -81,7 +80,9 @@
class ButtonAction : public KisAction
{
public:
- ButtonAction(QAbstractButton* button, const KIcon& icon, const QString& text, QObject* parent) : KisAction(icon, text, parent) , m_button(button)
+ ButtonAction(QAbstractButton* button, const KIcon& icon, const QString& text, QObject* parent)
+ : KisAction(icon, text, parent)
+ , m_button(button)
{
connect(m_button, SIGNAL(clicked()), this, SLOT(trigger()));
}
@@ -231,7 +232,7 @@ KisLayerBox::KisLayerBox()
m_selectOpaque = new KisAction(i18n("&Select Opaque"), this);
m_selectOpaque->setActivationFlags(KisAction::ACTIVE_LAYER);
- m_selectOpaque->setObjectName(""); // no name to avoid addition to the action collection
+ m_selectOpaque->setObjectName("select_opaque");
connect(m_selectOpaque, SIGNAL(triggered(bool)), this, SLOT(slotSelectOpaque()));
m_actions.append(m_selectOpaque);
diff --git a/krita/ui/CMakeLists.txt b/krita/ui/CMakeLists.txt
index 7176b70..60b1783 100644
--- a/krita/ui/CMakeLists.txt
+++ b/krita/ui/CMakeLists.txt
@@ -16,6 +16,8 @@ include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/widgets/resources
${CMAKE_CURRENT_SOURCE_DIR}/ora
${CMAKE_SOURCE_DIR}/krita/image/metadata
+ ${CMAKE_SOURCE_DIR}/krita/ui/qtsingleapplication
+ ${CMAKE_SOURCE_DIR}/krita/ui/qtlockedfile
${EXIV2_INCLUDE_DIR}
${OCIO_INCLUDE_DIR})
@@ -317,6 +319,12 @@ set(kritaui_LIB_SRCS
kis_mainwindow_observer.cpp
KisViewManager.cpp
kis_mirror_manager.cpp
+
+
+ qtlockedfile/qtlockedfile.cpp
+
+ qtsingleapplication/qtlocalpeer.cpp
+ qtsingleapplication/qtsingleapplication.cpp
)
if(WIN32)
@@ -324,6 +332,7 @@ if(WIN32)
${kritaui_LIB_SRCS}
input/kis_tablet_event.cpp
input/wintab/kis_tablet_support_win.cpp
+ qtlockedfile/qtlockedfile_win.cpp
)
endif(WIN32)
@@ -332,6 +341,7 @@ if(UNIX)
${kritaui_LIB_SRCS}
input/kis_tablet_event.cpp
input/wintab/kis_tablet_support.cpp
+ qtlockedfile/qtlockedfile_unix.cpp
)
if(NOT APPLE)
set(kritaui_LIB_SRCS
diff --git a/krita/ui/KisApplication.cpp b/krita/ui/KisApplication.cpp
index b9148df..da597e4 100644
--- a/krita/ui/KisApplication.cpp
+++ b/krita/ui/KisApplication.cpp
@@ -31,6 +31,7 @@
#include <KoDpi.h>
#include "KoGlobal.h"
+#include <kcrash.h>
#include <kdeversion.h>
#include <klocale.h>
#include <kcmdlineargs.h>
@@ -136,8 +137,8 @@ public:
-KisApplication::KisApplication()
- : KApplication()
+KisApplication::KisApplication(const QString &key)
+ : QtSingleApplication(key, KCmdLineArgs::qtArgc(), KCmdLineArgs::qtArgv())
, d(new KisApplicationPrivate)
{
KisApplication::KoApp = this;
@@ -148,6 +149,18 @@ KisApplication::KisApplication()
// Initialize all Calligra directories etc.
KoGlobal::initialize();
+ setApplicationName(KGlobal::mainComponent().componentName());
+ setOrganizationDomain(KGlobal::mainComponent().aboutData()->organizationDomain());
+ setApplicationVersion(KGlobal::mainComponent().aboutData()->version());
+
+ // make sure the clipboard is created before setting the window icon (bug 209263)
+ (void) QApplication::clipboard();
+ setWindowIcon(KIcon(KGlobal::mainComponent().aboutData()->programIconName()));
+
+ KCrash::setDrKonqiEnabled(true);
+ KCrash::setApplicationName(applicationName());
+ KCrash::setApplicationPath(QCoreApplication::applicationDirPath());
+
#ifdef Q_OS_MACX
if ( QSysInfo::MacintoshVersion > QSysInfo::MV_10_8 )
{
@@ -158,6 +171,7 @@ KisApplication::KisApplication()
setAttribute(Qt::AA_DontShowIconsInMenus, true);
#endif
+
if (applicationName() == "krita" && qgetenv("KDE_FULL_SESSION").isEmpty()) {
// There are two themes that work for Krita, oxygen and plastique. Try to set plastique first, then oxygen
setStyle("Plastique");
@@ -180,7 +194,7 @@ BOOL isWow64()
//and GetProcAddress to get a pointer to the function if available.
fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(
- GetModuleHandle(TEXT("kernel32")),"IsWow64Process");
+ GetModuleHandle(TEXT("kernel32")),"IsWow64Process");
if(NULL != fnIsWow64Process)
{
@@ -283,18 +297,8 @@ bool KisApplication::start()
&& !exportAs
&& !profileFileName.isEmpty());
- // Figure out _which_ application we actually are
- KisDocumentEntry entry = KisDocumentEntry::queryByMimeType(KIS_MIME_TYPE);
- if (entry.isEmpty()) {
-
- QMessageBox::critical(0, i18nc("@title:window", "Krita: Critical Error"), i18n("Essential application components could not be found.\n"
- "This might be an installation issue.\n"
- "Try restarting or reinstalling."));
- return false;
- }
// Load various global plugins
-
KoShapeRegistry* r = KoShapeRegistry::instance();
r->add(new KisShapeSelectionFactory());
@@ -484,11 +488,9 @@ void KisApplication::setSplashScreen(QWidget *splashScreen)
QStringList KisApplication::mimeFilter(KisImportExportManager::Direction direction) const
{
- KisDocumentEntry entry = KisDocumentEntry::queryByMimeType(KIS_MIME_TYPE);
- KService::Ptr service = entry.service();
return KisImportExportManager::mimeFilter(KIS_MIME_TYPE,
- direction,
- service->property("X-KDE-ExtraNativeMimeTypes", QVariant::StringList).toStringList());
+ direction,
+ KisDocumentEntry::extraNativeMimeTypes());
}
@@ -512,10 +514,57 @@ KisApplication *KisApplication::koApplication()
return KoApp;
}
+void KisApplication::remoteArguments(const QByteArray &message, QObject *socket)
+{
+ Q_UNUSED(socket);
-int KisApplication::checkAutosaveFiles(KisMainWindow *mainWindow)
+ QDataStream ds(message);
+ KCmdLineArgs::loadAppArgs(ds);
+
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ int argsCount = args->count();
+
+ KisMainWindow *mw = qobject_cast<KisMainWindow*>(qApp->activeWindow());
+ if (!mw) {
+ mw = KisPart::instance()->mainWindows().first();
+ }
+
+ if (!mw) {
+ return;
+ }
+
+ if (argsCount > 0) {
+
+ // Loop through arguments
+ for (int argNumber = 0; argNumber < argsCount; argNumber++) {
+ KUrl url = args->url(argNumber);
+ if (url.isValid()) {
+ KisDocument *doc = KisPart::instance()->createDocument();
+
+ if (doc) {
+ mw->openDocumentInternal(url, doc);
+
+ }
+ }
+
+ }
+ }
+
+}
+
+void KisApplication::fileOpenRequested(const QString &url)
{
+ KisDocument *doc = KisPart::instance()->createDocument();
+ KisPart::instance()->addDocument(doc);
+ KisMainWindow *mainWindow = KisPart::instance()->mainWindows().first();
+ if (mainWindow) {
+ mainWindow->openDocumentInternal(url, doc);
+ }
+}
+
+int KisApplication::checkAutosaveFiles(KisMainWindow *mainWindow)
+{
// Check for autosave files from a previous run. There can be several, and
// we want to offer a restore for every one. Including a nice thumbnail!
QStringList autoSaveFiles;
@@ -535,9 +584,9 @@ int KisApplication::checkAutosaveFiles(KisMainWindow *mainWindow)
filters << QString(".%1-%2-%3-autosave%4").arg("krita").arg("*").arg("*").arg(extension);
#ifdef Q_OS_WIN
- QDir dir = QDir::tempPath();
+ QDir dir = QDir::tempPath();
#else
- QDir dir = QDir::home();
+ QDir dir = QDir::home();
#endif
// all autosave files for our application
@@ -545,7 +594,7 @@ int KisApplication::checkAutosaveFiles(KisMainWindow *mainWindow)
QStringList pids;
QString ourPid;
- ourPid.setNum(kapp->applicationPid());
+ ourPid.setNum(qApp->applicationPid());
#ifndef QT_NO_DBUS
// all running instances of our application -- bit hackish, but we cannot get at the dbus name here, for some reason
@@ -610,7 +659,7 @@ int KisApplication::checkAutosaveFiles(KisMainWindow *mainWindow)
mainWindow->openDocumentInternal(url, doc);
}
- if (doc) {
+ if (doc) {
doc->resetURL();
doc->setModified(true);
QFile::remove(url.toLocalFile());
diff --git a/krita/ui/KisApplication.h b/krita/ui/KisApplication.h
index 6df5e2a..a737a29 100644
--- a/krita/ui/KisApplication.h
+++ b/krita/ui/KisApplication.h
@@ -20,7 +20,7 @@
#ifndef KIS_APPLICATION_H
#define KIS_APPLICATION_H
-#include <kapplication.h>
+#include <qtsingleapplication/qtsingleapplication.h>
#include "krita_export.h"
class KisPart;
@@ -46,7 +46,7 @@ class QStringList;
* If the last mainwindow becomes closed, KisApplication automatically
* calls QApplication::quit.
*/
-class KRITAUI_EXPORT KisApplication : public KApplication
+class KRITAUI_EXPORT KisApplication : public QtSingleApplication
{
Q_OBJECT
@@ -55,7 +55,7 @@ public:
* Creates an application object, adds some standard directories and
* initializes kimgio.
*/
- explicit KisApplication();
+ explicit KisApplication(const QString &key);
/**
* Destructor.
@@ -83,11 +83,6 @@ public:
void setSplashScreen(QWidget *splash);
/**
- * Remove the splash dialog
- */
- void removeSplash();
-
- /**
* return a list of mimetypes this application supports.
*/
QStringList mimeFilter(KisImportExportManager::Direction direction) const;
@@ -112,6 +107,11 @@ protected:
// Current application object.
static KisApplication *KoApp;
+public slots:
+
+ void remoteArguments(const QByteArray &message, QObject*socket);
+ void fileOpenRequested(const QString & url);
+
private:
/// @return the number of autosavefiles opened
int checkAutosaveFiles(KisMainWindow *mainWindow);
diff --git a/krita/ui/KisDocumentEntry.cpp b/krita/ui/KisDocumentEntry.cpp
index 662a2f1..5585e96 100644
--- a/krita/ui/KisDocumentEntry.cpp
+++ b/krita/ui/KisDocumentEntry.cpp
@@ -44,6 +44,16 @@ KisDocumentEntry::~KisDocumentEntry()
{
}
+QString KisDocumentEntry::nativeMimeType()
+{
+ return QString::fromLatin1(KIS_MIME_TYPE);
+}
+
+QStringList KisDocumentEntry::extraNativeMimeTypes()
+{
+ return QStringList() << KIS_MIME_TYPE;
+}
+
KService::Ptr KisDocumentEntry::service() const {
return m_service;
diff --git a/krita/ui/KisDocumentEntry.h b/krita/ui/KisDocumentEntry.h
index a84d31c..7c99b7c 100644
--- a/krita/ui/KisDocumentEntry.h
+++ b/krita/ui/KisDocumentEntry.h
@@ -49,6 +49,10 @@ public:
explicit KisDocumentEntry(const KService::Ptr& service);
~KisDocumentEntry();
+ static QString nativeMimeType();
+
+ static QStringList extraNativeMimeTypes();
+
KService::Ptr service() const;
/**
diff --git a/krita/ui/KisFilterChain.cpp b/krita/ui/KisFilterChain.cpp
index d01a618..bb8b490 100644
--- a/krita/ui/KisFilterChain.cpp
+++ b/krita/ui/KisFilterChain.cpp
@@ -521,12 +521,7 @@ KisDocument* KisFilterChain::createDocument(const QString& file)
KisDocument* KisFilterChain::createDocument(const QByteArray& mimeType)
{
- KisDocumentEntry entry = KisDocumentEntry::queryByMimeType(mimeType);
-
- if (entry.isEmpty()) {
- kError(30500) << "Couldn't find a part that can handle mimetype " << mimeType << endl;
- }
-
+ Q_UNUSED(mimeType);
return KisPart::instance()->createDocument();
}
diff --git a/krita/ui/KisMainWindow.cpp b/krita/ui/KisMainWindow.cpp
index 2393f68..94a9c09 100644
--- a/krita/ui/KisMainWindow.cpp
+++ b/krita/ui/KisMainWindow.cpp
@@ -307,7 +307,7 @@ KisMainWindow::KisMainWindow()
d->toolOptionsDocker = qobject_cast<KoToolDocker*>(createDockWidget(&toolDockerFactory));
KoToolBoxFactory toolBoxFactory;
- QDockWidget* toolbox = createDockWidget(&toolBoxFactory);
+ createDockWidget(&toolBoxFactory);
foreach(const QString & docker, KoDockRegistry::instance()->keys()) {
KoDockFactoryBase *factory = KoDockRegistry::instance()->value(docker);
@@ -377,9 +377,8 @@ KisMainWindow::KisMainWindow()
QString doc;
QStringList allFiles = KGlobal::dirs()->findAllResources("data", "krita/krita.rc");
setXMLFile(findMostRecentXMLFile(allFiles, doc));
- setLocalXMLFile(KStandardDirs::locateLocal("data", "krita/krita.rc"));
- guiFactory()->addClient( this );
+ guiFactory()->addClient(this);
// Create and plug toolbar list for Settings menu
QList<QAction *> toolbarList;
@@ -1709,8 +1708,7 @@ QDockWidget* KisMainWindow::createDockWidget(KoDockFactoryBase* factory)
KConfigGroup group(KGlobal::config(), "GUI");
QFont dockWidgetFont = KGlobalSettings::generalFont();
qreal pointSize = group.readEntry("palettefontsize", dockWidgetFont.pointSize() * 0.75);
- pointSize = qMax(pointSize, KGlobalSettings::smallestReadableFont().pointSizeF());
- dockWidgetFont.setPointSizeF(pointSize);
+ dockWidgetFont.setPointSizeF(qMax(pointSize, KGlobalSettings::smallestReadableFont().pointSizeF()));
#ifdef Q_OS_MAC
dockWidget->setAttribute(Qt::WA_MacSmallSize, true);
#endif
@@ -1723,13 +1721,13 @@ QDockWidget* KisMainWindow::createDockWidget(KoDockFactoryBase* factory)
void KisMainWindow::forceDockTabFonts()
{
- QObjectList chis = children();
- for (int i = 0; i < chis.size(); ++i) {
- if (chis.at(i)->inherits("QTabBar")) {
+ foreach(QObject *child, children()) {
+ if (child->inherits("QTabBar")) {
+ KConfigGroup group(KGlobal::config(), "GUI");
QFont dockWidgetFont = KGlobalSettings::generalFont();
- qreal pointSize = KGlobalSettings::smallestReadableFont().pointSizeF();
- dockWidgetFont.setPointSizeF(pointSize);
- ((QTabBar *)chis.at(i))->setFont(dockWidgetFont);
+ qreal pointSize = group.readEntry("palettefontsize", dockWidgetFont.pointSize() * 0.75);
+ dockWidgetFont.setPointSizeF(qMax(pointSize, KGlobalSettings::smallestReadableFont().pointSizeF()));
+ ((QTabBar *)child)->setFont(dockWidgetFont);
}
}
}
@@ -1748,6 +1746,9 @@ QList<KoCanvasObserverBase*> KisMainWindow::canvasObservers()
if (observer) {
observers << observer;
}
+ else {
+ qWarning() << docker << "is not a canvas observer";
+ }
}
return observers;
}
@@ -1808,6 +1809,7 @@ void KisMainWindow::subWindowActivated()
}
updateCaption();
+ d->viewManager->actionManager()->updateGUI();
}
void KisMainWindow::updateWindowMenu()
@@ -1885,6 +1887,7 @@ void KisMainWindow::setActiveSubWindow(QWidget *window)
d->activeSubWindow = subwin;
}
updateWindowMenu();
+ d->viewManager->actionManager()->updateGUI();
}
void KisMainWindow::configChanged()
@@ -1899,6 +1902,7 @@ void KisMainWindow::configChanged()
KConfigGroup group(KGlobal::config(), "theme");
d->themeManager->setCurrentTheme(group.readEntry("Theme", "Krita dark"));
+ d->viewManager->actionManager()->updateGUI();
}
void KisMainWindow::newView(QObject *document)
@@ -1906,18 +1910,18 @@ void KisMainWindow::newView(QObject *document)
KisDocument *doc = qobject_cast<KisDocument*>(document);
KisView *view = KisPart::instance()->createView(doc, this);
addView(view);
- qDebug() << "?>>>>>>>>>>>>>>>>" << KActionCollection::allCollections().size();
+ d->viewManager->actionManager()->updateGUI();
}
void KisMainWindow::newWindow()
{
- qDebug() << "?>>>>>>>>>>>>>>>>" << KActionCollection::allCollections().size();
KisPart::instance()->createMainWindow()->show();
}
void KisMainWindow::closeCurrentWindow()
{
d->mdiArea->currentSubWindow()->close();
+ d->viewManager->actionManager()->updateGUI();
}
void KisMainWindow::showAboutApplication()
diff --git a/krita/ui/KisMainWindow.h b/krita/ui/KisMainWindow.h
index 55c9046..ed4ad58 100644
--- a/krita/ui/KisMainWindow.h
+++ b/krita/ui/KisMainWindow.h
@@ -124,13 +124,6 @@ public:
void setReadWrite(bool readwrite);
- /**
- * Returns the dockwidget specified by the @p factory. If the dock widget doesn't exist yet it's created.
- * Add a "view_palette_action_menu" action to your view menu if you want to use closable dock widgets.
- * @param factory the factory used to create the dock widget if needed
- * @return the dock widget specified by @p factory (may be 0)
- */
- QDockWidget* createDockWidget(KoDockFactoryBase* factory);
/// Return the list of dock widgets belonging to this main window.
QList<QDockWidget*> dockWidgets();
@@ -373,14 +366,14 @@ private:
friend class KisApplication;
+
/**
- * This setting indicates who is calling chooseNewDocument.
- * Usually the app will want to
- * - show the template dialog with 'everything' if InitDocAppStarting, InitDocFileClose or InitDocEmbedded
- * - show the template dialog with 'templates only' if InitDocFileNew
- * - create an empty document with default settings if InitDocEmpty
+ * Returns the dockwidget specified by the @p factory. If the dock widget doesn't exist yet it's created.
+ * Add a "view_palette_action_menu" action to your view menu if you want to use closable dock widgets.
+ * @param factory the factory used to create the dock widget if needed
+ * @return the dock widget specified by @p factory (may be 0)
*/
- enum InitDocFlags { /*InitDocAppStarting, */ InitDocFileNew, InitDocFileClose /*, InitDocEmbedded, InitDocEmpty*/ };
+ QDockWidget* createDockWidget(KoDockFactoryBase* factory);
/**
* Ask user about saving changes to the document upon exit.
diff --git a/krita/ui/kis_action_manager.cpp b/krita/ui/kis_action_manager.cpp
index ae2e5cb..7e1bd5e 100644
--- a/krita/ui/kis_action_manager.cpp
+++ b/krita/ui/kis_action_manager.cpp
@@ -19,6 +19,8 @@
#include "kis_action_manager.h"
#include <QList>
+
+#include <kstandarddirs.h>
#include <kactioncollection.h>
#include "KisPart.h"
@@ -31,6 +33,8 @@
#include "kis_layer.h"
#include "KisDocument.h"
+
+
class KisActionManager::Private {
public:
@@ -42,12 +46,17 @@ public:
QList<KisAction*> actions;
KoGenericRegistry<KisOperationUIFactory*> uiRegistry;
KisOperationRegistry operationRegistry;
+
};
KisActionManager::KisActionManager(KisViewManager* viewManager)
: d(new Private)
{
d->viewManager = viewManager;
+
+ QString kritarc = KStandardDirs::locate("data", "krita/krita.rc");
+ qDebug() << kritarc;
+
}
KisActionManager::~KisActionManager()
@@ -62,12 +71,12 @@ void KisActionManager::setView(QPointer<KisView> imageView)
void KisActionManager::addAction(const QString& name, KisAction* action)
{
- if (!name.isEmpty()) {
- d->viewManager->actionCollection()->addAction(name, action);
- action->setObjectName(name);
- action->setParent(d->viewManager->actionCollection());
- }
+ Q_ASSERT(!name.isEmpty());
+ Q_ASSERT(action);
+ d->viewManager->actionCollection()->addAction(name, action);
+ action->setObjectName(name);
+ action->setParent(d->viewManager->actionCollection());
d->actions.append(action);
action->setActionManager(this);
}
diff --git a/krita/ui/kis_factory2.cc b/krita/ui/kis_factory2.cc
index 269c0d0..2f5ba41 100644
--- a/krita/ui/kis_factory2.cc
+++ b/krita/ui/kis_factory2.cc
@@ -40,10 +40,9 @@
KAboutData* KisFactory::s_aboutData = 0;
-KComponentData* KisFactory::s_instance = 0;
+KComponentData* KisFactory::s_componentData = 0;
-KisFactory::KisFactory(QObject* parent)
- : KPluginFactory(*aboutData(), parent)
+KisFactory::KisFactory()
{
(void)componentData();
}
@@ -52,21 +51,8 @@ KisFactory::~KisFactory()
{
delete s_aboutData;
s_aboutData = 0;
- delete s_instance;
- s_instance = 0;
-}
-
-/**
- * Create the document
- */
-QObject* KisFactory::create( const char* /*iface*/, QWidget* /*parentWidget*/, QObject *parent,
- const QVariantList& args, const QString& keyword )
-{
- Q_UNUSED( parent );
- Q_UNUSED( args );
- Q_UNUSED( keyword );
-
- return KisPart::instance();
+ delete s_componentData;
+ s_componentData = 0;
}
@@ -80,36 +66,34 @@ KAboutData* KisFactory::aboutData()
const KComponentData &KisFactory::componentData()
{
- if (!s_instance) {
- s_instance = new KComponentData(aboutData());
- Q_CHECK_PTR(s_instance);
- s_instance->dirs()->addResourceType("krita_template", "data", "krita/templates");
+ if (!s_componentData) {
+ s_componentData = new KComponentData(aboutData());
+ Q_CHECK_PTR(s_componentData);
+ s_componentData->dirs()->addResourceType("krita_template", "data", "krita/templates");
// for cursors
- s_instance->dirs()->addResourceType("kis_pics", "data", "krita/pics/");
+ s_componentData->dirs()->addResourceType("kis_pics", "data", "krita/pics/");
// for images in the paintop box
- s_instance->dirs()->addResourceType("kis_images", "data", "krita/images/");
+ s_componentData->dirs()->addResourceType("kis_images", "data", "krita/images/");
- s_instance->dirs()->addResourceType("icc_profiles", 0, "krita/profiles/");
+ s_componentData->dirs()->addResourceType("icc_profiles", 0, "krita/profiles/");
- s_instance->dirs()->addResourceType("kis_shaders", "data", "krita/shaders/");
+ s_componentData->dirs()->addResourceType("kis_shaders", "data", "krita/shaders/");
// Tell the iconloader about share/apps/calligra/icons
KIconLoader::global()->addAppDir("calligra");
- KGlobal::locale()->insertCatalog(s_instance->catalogName());
+ KGlobal::locale()->insertCatalog(s_componentData->catalogName());
// install 'instancename'data resource type
- KGlobal::dirs()->addResourceType(QString(s_instance->componentName() + "data").toUtf8(), "data", s_instance->componentName());
+ KGlobal::dirs()->addResourceType(QString(s_componentData->componentName() + "data").toUtf8(), "data", s_componentData->componentName());
}
- return *s_instance;
+ return *s_componentData;
}
const QString KisFactory::componentName()
{
return "krita";
}
-
-#include "kis_factory2.moc"
diff --git a/krita/ui/kis_factory2.h b/krita/ui/kis_factory2.h
index c51b5c8..6873d1e 100644
--- a/krita/ui/kis_factory2.h
+++ b/krita/ui/kis_factory2.h
@@ -23,30 +23,24 @@
#include <QStringList>
-#include <kpluginfactory.h>
-
#include <krita_export.h>
-class KComponentData;
-class KAboutData;
+#include <kcomponentdata.h>
+#include <kaboutdata.h>
-class KRITAUI_EXPORT KisFactory : public KPluginFactory
+class KRITAUI_EXPORT KisFactory
{
- Q_OBJECT
public:
-
- KisFactory(QObject* parent = 0);
+ KisFactory();
~KisFactory();
- virtual QObject* create(const char* iface, QWidget* parentWidget, QObject *parent, const QVariantList& args, const QString& keyword);
-
static KAboutData *aboutData();
static const KComponentData &componentData();
static const QString componentName();
private:
- static KComponentData *s_instance;
+ static KComponentData *s_componentData;
static KAboutData *s_aboutData;
};
diff --git a/krita/ui/qtlockedfile/README.txt b/krita/ui/qtlockedfile/README.txt
new file mode 100644
index 0000000..6fcf2fd
--- /dev/null
+++ b/krita/ui/qtlockedfile/README.txt
@@ -0,0 +1,10 @@
+This is the src directory of the QtLockedFile
+solution integrated over from addons/main/utils/qtlockedfile/src .
+
+namespace.patch was applied to introduce the SharedTools namespace.
+
+It is required by the QtSingleApplication solution.
+
+History:
+
+16.05.2008 Integrated
diff --git a/krita/ui/qtlockedfile/qtlockedfile.cpp b/krita/ui/qtlockedfile/qtlockedfile.cpp
new file mode 100644
index 0000000..cb66662
--- /dev/null
+++ b/krita/ui/qtlockedfile/qtlockedfile.cpp
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://www.qt.io/licensing. For further information
+** use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qtlockedfile.h"
+
+
+
+/*!
+ \class QtLockedFile
+
+ \brief The QtLockedFile class extends QFile with advisory locking functions.
+
+ A file may be locked in read or write mode. Multiple instances of
+ \e QtLockedFile, created in multiple processes running on the same
+ machine, may have a file locked in read mode. Exactly one instance
+ may have it locked in write mode. A read and a write lock cannot
+ exist simultaneously on the same file.
+
+ The file locks are advisory. This means that nothing prevents
+ another process from manipulating a locked file using QFile or
+ file system functions offered by the OS. Serialization is only
+ guaranteed if all processes that access the file use
+ QtLockedFile. Also, while holding a lock on a file, a process
+ must not open the same file again (through any API), or locks
+ can be unexpectedly lost.
+
+ The lock provided by an instance of \e QtLockedFile is released
+ whenever the program terminates. This is true even when the
+ program crashes and no destructors are called.
+*/
+
+/*! \enum QtLockedFile::LockMode
+
+ This enum describes the available lock modes.
+
+ \value ReadLock A read lock.
+ \value WriteLock A write lock.
+ \value NoLock Neither a read lock nor a write lock.
+*/
+
+/*!
+ Constructs an unlocked \e QtLockedFile object. This constructor behaves in the same way
+ as \e QFile::QFile().
+
+ \sa QFile::QFile()
+*/
+QtLockedFile::QtLockedFile()
+ : QFile()
+{
+#ifdef Q_OS_WIN
+ m_semaphore_hnd = 0;
+ m_mutex_hnd = 0;
+#endif
+ m_lock_mode = NoLock;
+}
+
+/*!
+ Constructs an unlocked QtLockedFile object with file \a name. This constructor behaves in
+ the same way as \e QFile::QFile(const QString&).
+
+ \sa QFile::QFile()
+*/
+QtLockedFile::QtLockedFile(const QString &name)
+ : QFile(name)
+{
+#ifdef Q_OS_WIN
+ m_semaphore_hnd = 0;
+ m_mutex_hnd = 0;
+#endif
+ m_lock_mode = NoLock;
+}
+
+/*!
+ Returns \e true if this object has a in read or write lock;
+ otherwise returns \e false.
+
+ \sa lockMode()
+*/
+bool QtLockedFile::isLocked() const
+{
+ return m_lock_mode != NoLock;
+}
+
+/*!
+ Returns the type of lock currently held by this object, or \e QtLockedFile::NoLock.
+
+ \sa isLocked()
+*/
+QtLockedFile::LockMode QtLockedFile::lockMode() const
+{
+ return m_lock_mode;
+}
+
+/*!
+ \fn bool QtLockedFile::lock(LockMode mode, bool block = true)
+
+ Obtains a lock of type \a mode.
+
+ If \a block is true, this
+ function will block until the lock is acquired. If \a block is
+ false, this function returns \e false immediately if the lock cannot
+ be acquired.
+
+ If this object already has a lock of type \a mode, this function returns \e true immediately. If this object has a lock of a different type than \a mode, the lock
+ is first released and then a new lock is obtained.
+
+ This function returns \e true if, after it executes, the file is locked by this object,
+ and \e false otherwise.
+
+ \sa unlock(), isLocked(), lockMode()
+*/
+
+/*!
+ \fn bool QtLockedFile::unlock()
+
+ Releases a lock.
+
+ If the object has no lock, this function returns immediately.
+
+ This function returns \e true if, after it executes, the file is not locked by
+ this object, and \e false otherwise.
+
+ \sa lock(), isLocked(), lockMode()
+*/
+
+/*!
+ \fn QtLockedFile::~QtLockedFile()
+
+ Destroys the \e QtLockedFile object. If any locks were held, they are released.
+*/
diff --git a/krita/ui/qtlockedfile/qtlockedfile.h b/krita/ui/qtlockedfile/qtlockedfile.h
new file mode 100644
index 0000000..1dc345a
--- /dev/null
+++ b/krita/ui/qtlockedfile/qtlockedfile.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://www.qt.io/licensing. For further information
+** use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QTLOCKEDFILE_H
+#define QTLOCKEDFILE_H
+
+#include <QFile>
+
+#if defined(Q_OS_WIN)
+# if !defined(QT_QTLOCKEDFILE_EXPORT) && !defined(QT_QTLOCKEDFILE_IMPORT)
+# define QT_QTLOCKEDFILE_EXPORT
+# elif defined(QT_QTLOCKEDFILE_IMPORT)
+# if defined(QT_QTLOCKEDFILE_EXPORT)
+# undef QT_QTLOCKEDFILE_EXPORT
+# endif
+# define QT_QTLOCKEDFILE_EXPORT __declspec(dllimport)
+# elif defined(QT_QTLOCKEDFILE_EXPORT)
+# undef QT_QTLOCKEDFILE_EXPORT
+# define QT_QTLOCKEDFILE_EXPORT __declspec(dllexport)
+# endif
+#else
+# define QT_QTLOCKEDFILE_EXPORT
+#endif
+
+
+
+class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile
+{
+public:
+ enum LockMode { NoLock = 0, ReadLock, WriteLock };
+
+ QtLockedFile();
+ QtLockedFile(const QString &name);
+ ~QtLockedFile();
+
+ bool lock(LockMode mode, bool block = true);
+ bool unlock();
+ bool isLocked() const;
+ LockMode lockMode() const;
+
+private:
+#ifdef Q_OS_WIN
+ Qt::HANDLE m_semaphore_hnd;
+ Qt::HANDLE m_mutex_hnd;
+#endif
+ LockMode m_lock_mode;
+};
+
+#endif // QTLOCKEDFILE_H
diff --git a/krita/ui/qtlockedfile/qtlockedfile.pri b/krita/ui/qtlockedfile/qtlockedfile.pri
new file mode 100644
index 0000000..46d1f1a
--- /dev/null
+++ b/krita/ui/qtlockedfile/qtlockedfile.pri
@@ -0,0 +1,13 @@
+INCLUDEPATH += $$PWD
+DEPENDPATH += $$PWD
+HEADERS += $$PWD/qtlockedfile.h
+SOURCES += $$PWD/qtlockedfile.cpp
+
+unix:SOURCES += $$PWD/qtlockedfile_unix.cpp
+win32:SOURCES += $$PWD/qtlockedfile_win.cpp
+
+win32:contains(TEMPLATE, lib):contains(CONFIG, shared) {
+ DEFINES += QT_QTLOCKEDFILE_EXPORT=__declspec(dllexport)
+}
+
+
diff --git a/krita/ui/qtlockedfile/qtlockedfile_unix.cpp b/krita/ui/qtlockedfile/qtlockedfile_unix.cpp
new file mode 100644
index 0000000..0d0ba95
--- /dev/null
+++ b/krita/ui/qtlockedfile/qtlockedfile_unix.cpp
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://www.qt.io/licensing. For further information
+** use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qtlockedfile.h"
+
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+
+
+bool QtLockedFile::lock(LockMode mode, bool block)
+{
+ if (!isOpen()) {
+ qWarning("QtLockedFile::lock(): file is not opened");
+ return false;
+ }
+
+ if (mode == NoLock)
+ return unlock();
+
+ if (mode == m_lock_mode)
+ return true;
+
+ if (m_lock_mode != NoLock)
+ unlock();
+
+ struct flock fl;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_type = (mode == ReadLock) ? F_RDLCK : F_WRLCK;
+ int cmd = block ? F_SETLKW : F_SETLK;
+ int ret = fcntl(handle(), cmd, &fl);
+
+ if (ret == -1) {
+ if (errno != EINTR && errno != EAGAIN)
+ qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno));
+ return false;
+ }
+
+
+ m_lock_mode = mode;
+ return true;
+}
+
+
+bool QtLockedFile::unlock()
+{
+ if (!isOpen()) {
+ qWarning("QtLockedFile::unlock(): file is not opened");
+ return false;
+ }
+
+ if (!isLocked())
+ return true;
+
+ struct flock fl;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_type = F_UNLCK;
+ int ret = fcntl(handle(), F_SETLKW, &fl);
+
+ if (ret == -1) {
+ qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno));
+ return false;
+ }
+
+ m_lock_mode = NoLock;
+ remove();
+ return true;
+}
+
+QtLockedFile::~QtLockedFile()
+{
+ if (isOpen())
+ unlock();
+}
diff --git a/krita/ui/qtlockedfile/qtlockedfile_win.cpp b/krita/ui/qtlockedfile/qtlockedfile_win.cpp
new file mode 100644
index 0000000..4f5d8a1
--- /dev/null
+++ b/krita/ui/qtlockedfile/qtlockedfile_win.cpp
@@ -0,0 +1,196 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://www.qt.io/licensing. For further information
+** use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qtlockedfile.h"
+
+#include <qt_windows.h>
+#include <QFileInfo>
+
+
+
+#define SEMAPHORE_PREFIX "QtLockedFile semaphore "
+#define MUTEX_PREFIX "QtLockedFile mutex "
+#define SEMAPHORE_MAX 100
+
+static QString errorCodeToString(DWORD errorCode)
+{
+ QString result;
+ char *data = 0;
+ FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ 0, errorCode, 0,
+ (char*)&data, 0, 0);
+ result = QString::fromLocal8Bit(data);
+ if (data != 0)
+ LocalFree(data);
+
+ if (result.endsWith(QLatin1Char('\n')))
+ result.truncate(result.length() - 1);
+
+ return result;
+}
+
+bool QtLockedFile::lock(LockMode mode, bool block)
+{
+ if (!isOpen()) {
+ qWarning("QtLockedFile::lock(): file is not opened");
+ return false;
+ }
+
+ if (mode == m_lock_mode)
+ return true;
+
+ if (m_lock_mode != 0)
+ unlock();
+
+ if (m_semaphore_hnd == 0) {
+ QFileInfo fi(*this);
+ QString sem_name = QString::fromLatin1(SEMAPHORE_PREFIX)
+ + fi.absoluteFilePath().toLower();
+
+ m_semaphore_hnd = CreateSemaphoreW(0, SEMAPHORE_MAX, SEMAPHORE_MAX,
+ (TCHAR*)sem_name.utf16());
+
+ if (m_semaphore_hnd == 0) {
+ qWarning("QtLockedFile::lock(): CreateSemaphore: %s",
+ errorCodeToString(GetLastError()).toLatin1().constData());
+ return false;
+ }
+ }
+
+ bool gotMutex = false;
+ int decrement;
+ if (mode == ReadLock) {
+ decrement = 1;
+ } else {
+ decrement = SEMAPHORE_MAX;
+ if (m_mutex_hnd == 0) {
+ QFileInfo fi(*this);
+ QString mut_name = QString::fromLatin1(MUTEX_PREFIX)
+ + fi.absoluteFilePath().toLower();
+
+ m_mutex_hnd = CreateMutexW(NULL, FALSE, (TCHAR*)mut_name.utf16());
+
+ if (m_mutex_hnd == 0) {
+ qWarning("QtLockedFile::lock(): CreateMutex: %s",
+ errorCodeToString(GetLastError()).toLatin1().constData());
+ return false;
+ }
+ }
+ DWORD res = WaitForSingleObject(m_mutex_hnd, block ? INFINITE : 0);
+ if (res == WAIT_TIMEOUT)
+ return false;
+ if (res == WAIT_FAILED) {
+ qWarning("QtLockedFile::lock(): WaitForSingleObject (mutex): %s",
+ errorCodeToString(GetLastError()).toLatin1().constData());
+ return false;
+ }
+ gotMutex = true;
+ }
+
+ for (int i = 0; i < decrement; ++i) {
+ DWORD res = WaitForSingleObject(m_semaphore_hnd, block ? INFINITE : 0);
+ if (res == WAIT_TIMEOUT) {
+ if (i) {
+ // A failed nonblocking rw locking. Undo changes to semaphore.
+ if (ReleaseSemaphore(m_semaphore_hnd, i, NULL) == 0) {
+ qWarning("QtLockedFile::unlock(): ReleaseSemaphore: %s",
+ errorCodeToString(GetLastError()).toLatin1().constData());
+ // Fall through
+ }
+ }
+ if (gotMutex)
+ ReleaseMutex(m_mutex_hnd);
+ return false;
+ }
+ if (res != WAIT_OBJECT_0) {
+ if (gotMutex)
+ ReleaseMutex(m_mutex_hnd);
+ qWarning("QtLockedFile::lock(): WaitForSingleObject (semaphore): %s",
+ errorCodeToString(GetLastError()).toLatin1().constData());
+ return false;
+ }
+ }
+
+ m_lock_mode = mode;
+ if (gotMutex)
+ ReleaseMutex(m_mutex_hnd);
+ return true;
+}
+
+bool QtLockedFile::unlock()
+{
+ if (!isOpen()) {
+ qWarning("QtLockedFile::unlock(): file is not opened");
+ return false;
+ }
+
+ if (!isLocked())
+ return true;
+
+ int increment;
+ if (m_lock_mode == ReadLock)
+ increment = 1;
+ else
+ increment = SEMAPHORE_MAX;
+
+ DWORD ret = ReleaseSemaphore(m_semaphore_hnd, increment, 0);
+ if (ret == 0) {
+ qWarning("QtLockedFile::unlock(): ReleaseSemaphore: %s",
+ errorCodeToString(GetLastError()).toLatin1().constData());
+ return false;
+ }
+
+ m_lock_mode = QtLockedFile::NoLock;
+ remove();
+ return true;
+}
+
+QtLockedFile::~QtLockedFile()
+{
+ if (isOpen())
+ unlock();
+ if (m_mutex_hnd != 0) {
+ DWORD ret = CloseHandle(m_mutex_hnd);
+ if (ret == 0) {
+ qWarning("QtLockedFile::~QtLockedFile(): CloseHandle (mutex): %s",
+ errorCodeToString(GetLastError()).toLatin1().constData());
+ }
+ m_mutex_hnd = 0;
+ }
+ if (m_semaphore_hnd != 0) {
+ DWORD ret = CloseHandle(m_semaphore_hnd);
+ if (ret == 0) {
+ qWarning("QtLockedFile::~QtLockedFile(): CloseHandle (semaphore): %s",
+ errorCodeToString(GetLastError()).toLatin1().constData());
+ }
+ m_semaphore_hnd = 0;
+ }
+}
+
diff --git a/krita/ui/qtsingleapplication/README.txt b/krita/ui/qtsingleapplication/README.txt
new file mode 100644
index 0000000..1909a33
--- /dev/null
+++ b/krita/ui/qtsingleapplication/README.txt
@@ -0,0 +1,10 @@
+This is the src directory of the QtSingleApplication solution
+integrated over from addons/main/utils/qtsingleapplication/src .
+
+namespace.patch was applied to introduce the SharedTools namespace.
+
+It additionally requires the QtLockedFile solution.
+
+History:
+
+16.05.2008 Integrated
diff --git a/krita/ui/qtsingleapplication/qtlocalpeer.cpp b/krita/ui/qtsingleapplication/qtlocalpeer.cpp
new file mode 100644
index 0000000..e1080dc
--- /dev/null
+++ b/krita/ui/qtsingleapplication/qtlocalpeer.cpp
@@ -0,0 +1,180 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://www.qt.io/licensing. For further information
+** use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qtlocalpeer.h"
+
+#include <QCoreApplication>
+#include <QDataStream>
+#include <QTime>
+
+#if defined(Q_OS_WIN)
+#include <QLibrary>
+#include <qt_windows.h>
+typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*);
+static PProcessIdToSessionId pProcessIdToSessionId = 0;
+#endif
+
+#if defined(Q_OS_UNIX)
+#include <time.h>
+#include <unistd.h>
+#endif
+
+
+
+static const char ack[] = "ack";
+
+QString QtLocalPeer::appSessionId(const QString &appId)
+{
+ QByteArray idc = appId.toUtf8();
+ quint16 idNum = qChecksum(idc.constData(), idc.size());
+ //### could do: two 16bit checksums over separate halves of id, for a 32bit result - improved uniqeness probability. Every-other-char split would be best.
+
+ QString res = QLatin1String("qtsingleapplication-")
+ + QString::number(idNum, 16);
+#if defined(Q_OS_WIN)
+ if (!pProcessIdToSessionId) {
+ QLibrary lib(QLatin1String("kernel32"));
+ pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId");
+ }
+ if (pProcessIdToSessionId) {
+ DWORD sessionId = 0;
+ pProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
+ res += QLatin1Char('-') + QString::number(sessionId, 16);
+ }
+#else
+ res += QLatin1Char('-') + QString::number(::getuid(), 16);
+#endif
+ return res;
+}
+
+QtLocalPeer::QtLocalPeer(QObject *parent, const QString &appId)
+ : QObject(parent), id(appId)
+{
+ if (id.isEmpty())
+ id = QCoreApplication::applicationFilePath(); //### On win, check if this returns .../argv[0] without casefolding; .\MYAPP == .\myapp on Win
+
+ socketName = appSessionId(id);
+ server = new QLocalServer(this);
+ QString lockName = QDir(QDir::tempPath()).absolutePath()
+ + QLatin1Char('/') + socketName
+ + QLatin1String("-lockfile");
+ lockFile.setFileName(lockName);
+ lockFile.open(QIODevice::ReadWrite);
+}
+
+bool QtLocalPeer::isClient()
+{
+ if (lockFile.isLocked())
+ return false;
+
+ if (!lockFile.lock(QtLockedFile::WriteLock, false))
+ return true;
+
+ if (!QLocalServer::removeServer(socketName))
+ qWarning("QtSingleCoreApplication: could not cleanup socket");
+ bool res = server->listen(socketName);
+ if (!res)
+ qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString()));
+ QObject::connect(server, SIGNAL(newConnection()), SLOT(receiveConnection()));
+ return false;
+}
+
+bool QtLocalPeer::sendMessage(const QByteArray &message, int timeout, bool block)
+{
+ if (!isClient())
+ return false;
+
+ QLocalSocket socket;
+ bool connOk = false;
+ for (int i = 0; i < 2; i++) {
+ // Try twice, in case the other instance is just starting up
+ socket.connectToServer(socketName);
+ connOk = socket.waitForConnected(timeout/2);
+ if (connOk || i)
+ break;
+ int ms = 250;
+#if defined(Q_OS_WIN)
+ Sleep(DWORD(ms));
+#else
+ struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 };
+ nanosleep(&ts, NULL);
+#endif
+ }
+ if (!connOk)
+ return false;
+
+ QByteArray uMsg(message);
+ QDataStream ds(&socket);
+ ds.writeBytes(uMsg.constData(), uMsg.size());
+ bool res = socket.waitForBytesWritten(timeout);
+ res &= socket.waitForReadyRead(timeout); // wait for ack
+ res &= (socket.read(qstrlen(ack)) == ack);
+ if (block) // block until peer disconnects
+ socket.waitForDisconnected(-1);
+ return res;
+}
+
+void QtLocalPeer::receiveConnection()
+{
+ QLocalSocket* socket = server->nextPendingConnection();
+ if (!socket)
+ return;
+
+ // Why doesn't Qt have a blocking stream that takes care of this shait???
+ while (socket->bytesAvailable() < static_cast<int>(sizeof(quint32))) {
+ if (!socket->isValid()) // stale request
+ return;
+ socket->waitForReadyRead(1000);
+ }
+ QDataStream ds(socket);
+ QByteArray uMsg;
+ quint32 remaining;
+ ds >> remaining;
+ uMsg.resize(remaining);
+ int got = 0;
+ char* uMsgBuf = uMsg.data();
+ //qDebug() << "RCV: remaining" << remaining;
+ do {
+ got = ds.readRawData(uMsgBuf, remaining);
+ remaining -= got;
+ uMsgBuf += got;
+ //qDebug() << "RCV: got" << got << "remaining" << remaining;
+ } while (remaining && got >= 0 && socket->waitForReadyRead(2000));
+ //### error check: got<0
+ if (got < 0) {
+ qWarning() << "QtLocalPeer: Message reception failed" << socket->errorString();
+ delete socket;
+ return;
+ }
+ // ### async this
+ socket->write(ack, qstrlen(ack));
+ socket->waitForBytesWritten(1000);
+ emit messageReceived(uMsg, socket); // ##(might take a long time to return)
+}
diff --git a/krita/ui/qtsingleapplication/qtlocalpeer.h b/krita/ui/qtsingleapplication/qtlocalpeer.h
new file mode 100644
index 0000000..5a48afc
--- /dev/null
+++ b/krita/ui/qtsingleapplication/qtlocalpeer.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://www.qt.io/licensing. For further information
+** use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include <qtlockedfile.h>
+
+#include <QLocalServer>
+#include <QLocalSocket>
+#include <QDir>
+
+
+
+class QtLocalPeer : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit QtLocalPeer(QObject *parent = 0, const QString &appId = QString());
+ bool isClient();
+ bool sendMessage(const QByteArray &message, int timeout, bool block);
+ QString applicationId() const
+ { return id; }
+ static QString appSessionId(const QString &appId);
+
+Q_SIGNALS:
+ void messageReceived(const QByteArray &message, QObject *socket);
+
+protected Q_SLOTS:
+ void receiveConnection();
+
+protected:
+ QString id;
+ QString socketName;
+ QLocalServer* server;
+ QtLockedFile lockFile;
+};
diff --git a/krita/ui/qtsingleapplication/qtsingleapplication.cpp b/krita/ui/qtsingleapplication/qtsingleapplication.cpp
new file mode 100644
index 0000000..93b8cac
--- /dev/null
+++ b/krita/ui/qtsingleapplication/qtsingleapplication.cpp
@@ -0,0 +1,195 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://www.qt.io/licensing. For further information
+** use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qtsingleapplication.h"
+#include "qtlocalpeer.h"
+
+#include <qtlockedfile.h>
+
+#include <QDir>
+#include <QFileOpenEvent>
+#include <QSharedMemory>
+#include <QWidget>
+
+static const int instancesSize = 1024;
+
+static QString instancesLockFilename(const QString &appSessionId)
+{
+ const QChar slash(QLatin1Char('/'));
+ QString res = QDir::tempPath();
+ if (!res.endsWith(slash))
+ res += slash;
+ return res + appSessionId + QLatin1String("-instances");
+}
+
+QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv)
+ : QApplication(argc, argv),
+ firstPeer(-1),
+ pidPeer(0)
+{
+ this->appId = appId;
+
+ const QString appSessionId = QtLocalPeer::appSessionId(appId);
+
+ // This shared memory holds a zero-terminated array of active (or crashed) instances
+ instances = new QSharedMemory(appSessionId, this);
+ actWin = 0;
+ block = false;
+
+ // First instance creates the shared memory, later instances attach to it
+ const bool created = instances->create(instancesSize);
+ if (!created) {
+ if (!instances->attach()) {
+ qWarning() << "Failed to initialize instances shared memory: "
+ << instances->errorString();
+ delete instances;
+ instances = 0;
+ return;
+ }
+ }
+
+ // QtLockedFile is used to workaround QTBUG-10364
+ QtLockedFile lockfile(instancesLockFilename(appSessionId));
+
+ lockfile.open(QtLockedFile::ReadWrite);
+ lockfile.lock(QtLockedFile::WriteLock);
+ qint64 *pids = static_cast<qint64 *>(instances->data());
+ if (!created) {
+ // Find the first instance that it still running
+ // The whole list needs to be iterated in order to append to it
+ for (; *pids; ++pids) {
+ if (firstPeer == -1 && isRunning(*pids))
+ firstPeer = *pids;
+ }
+ }
+ // Add current pid to list and terminate it
+ *pids++ = QCoreApplication::applicationPid();
+ *pids = 0;
+ pidPeer = new QtLocalPeer(this, appId + QLatin1Char('-') +
+ QString::number(QCoreApplication::applicationPid()));
+ connect(pidPeer, SIGNAL(messageReceived(QByteArray,QObject*)), SIGNAL(messageReceived(QByteArray,QObject*)));
+ pidPeer->isClient();
+ lockfile.unlock();
+}
+
+QtSingleApplication::~QtSingleApplication()
+{
+ if (!instances)
+ return;
+ const qint64 appPid = QCoreApplication::applicationPid();
+ QtLockedFile lockfile(instancesLockFilename(QtLocalPeer::appSessionId(appId)));
+ lockfile.open(QtLockedFile::ReadWrite);
+ lockfile.lock(QtLockedFile::WriteLock);
+ // Rewrite array, removing current pid and previously crashed ones
+ qint64 *pids = static_cast<qint64 *>(instances->data());
+ qint64 *newpids = pids;
+ for (; *pids; ++pids) {
+ if (*pids != appPid && isRunning(*pids))
+ *newpids++ = *pids;
+ }
+ *newpids = 0;
+ lockfile.unlock();
+}
+
+bool QtSingleApplication::event(QEvent *event)
+{
+ if (event->type() == QEvent::FileOpen) {
+ QFileOpenEvent *foe = static_cast<QFileOpenEvent*>(event);
+ emit fileOpenRequest(foe->file());
+ return true;
+ }
+ return QApplication::event(event);
+}
+
+bool QtSingleApplication::isRunning(qint64 pid)
+{
+ if (pid == -1) {
+ pid = firstPeer;
+ if (pid == -1) {
+ qDebug() << "isRunning" << pid;
+ return false;
+ }
+ }
+
+ QtLocalPeer peer(this, appId + QLatin1Char('-') + QString::number(pid, 10));
+
+ qDebug() << "is not yet running" << pid << peer.isClient();
+
+ return peer.isClient();
+}
+
+bool QtSingleApplication::sendMessage(const QByteArray &message, int timeout, qint64 pid)
+{
+ if (pid == -1) {
+ pid = firstPeer;
+ if (pid == -1)
+ return false;
+ }
+
+ QtLocalPeer peer(this, appId + QLatin1Char('-') + QString::number(pid, 10));
+ return peer.sendMessage(message, timeout, block);
+}
+
+QString QtSingleApplication::applicationId() const
+{
+ return appId;
+}
+
+void QtSingleApplication::setBlock(bool value)
+{
+ block = value;
+}
+
+void QtSingleApplication::setActivationWindow(QWidget *aw, bool activateOnMessage)
+{
+ actWin = aw;
+ if (!pidPeer)
+ return;
+ if (activateOnMessage)
+ connect(pidPeer, SIGNAL(messageReceived(QByteArray,QObject*)), this, SLOT(activateWindow()));
+ else
+ disconnect(pidPeer, SIGNAL(messageReceived(QByteArray,QObject*)), this, SLOT(activateWindow()));
+}
+
+
+QWidget* QtSingleApplication::activationWindow() const
+{
+ return actWin;
+}
+
+
+void QtSingleApplication::activateWindow()
+{
+ if (actWin) {
+ actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized);
+ actWin->raise();
+ actWin->activateWindow();
+ }
+}
diff --git a/krita/ui/qtsingleapplication/qtsingleapplication.h b/krita/ui/qtsingleapplication/qtsingleapplication.h
new file mode 100644
index 0000000..91575ca
--- /dev/null
+++ b/krita/ui/qtsingleapplication/qtsingleapplication.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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 Digia. For licensing terms and
+** conditions see http://www.qt.io/licensing. For further information
+** use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include <QApplication>
+
+QT_FORWARD_DECLARE_CLASS(QSharedMemory)
+
+class QtLocalPeer;
+
+#include <krita_export.h>
+
+class KRITAUI_EXPORT QtSingleApplication : public QApplication
+{
+ Q_OBJECT
+
+public:
+ QtSingleApplication(const QString &id, int &argc, char **argv);
+ ~QtSingleApplication();
+
+ bool isRunning(qint64 pid = -1);
+
+ void setActivationWindow(QWidget* aw, bool activateOnMessage = true);
+ QWidget* activationWindow() const;
+ bool event(QEvent *event);
+
+ QString applicationId() const;
+ void setBlock(bool value);
+
+public Q_SLOTS:
+ bool sendMessage(const QByteArray &message, int timeout = 5000, qint64 pid = -1);
+ void activateWindow();
+
+Q_SIGNALS:
+ void messageReceived(const QByteArray &message, QObject *socket);
+ void fileOpenRequest(const QString &file);
+
+private:
+ QString instancesFileName(const QString &appId);
+
+ qint64 firstPeer;
+ QSharedMemory *instances;
+ QtLocalPeer *pidPeer;
+ QWidget *actWin;
+ QString appId;
+ bool block;
+};
diff --git a/krita/ui/qtsingleapplication/qtsingleapplication.pri b/krita/ui/qtsingleapplication/qtsingleapplication.pri
new file mode 100644
index 0000000..5ddc4b2
--- /dev/null
+++ b/krita/ui/qtsingleapplication/qtsingleapplication.pri
@@ -0,0 +1,14 @@
+INCLUDEPATH += $$PWD
+DEPENDPATH += $$PWD
+HEADERS += $$PWD/qtsingleapplication.h $$PWD/qtlocalpeer.h
+SOURCES += $$PWD/qtsingleapplication.cpp $$PWD/qtlocalpeer.cpp
+
+QT *= network widgets
+
+gotqtlockedfile = $$find(HEADERS, .*qtlockedfile.h)
+isEmpty(gotqtlockedfile):include(../../qtlockedfile/src/qtlockedfile.pri)
+
+
+win32:contains(TEMPLATE, lib):contains(CONFIG, shared) {
+ DEFINES += QT_QTSINGLEAPPLICATION_EXPORT=__declspec(dllexport)
+}
More information about the kimageshop
mailing list