diff --git a/.gitignore b/.gitignore index 6de0441e..46f6fb9f 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,7 @@ Makefile.Sankore* # Build files # ############### +.qmake.stash build install Makefile diff --git a/OpenBoard.pro b/OpenBoard.pro index 2de5b263..ef132b74 100644 --- a/OpenBoard.pro +++ b/OpenBoard.pro @@ -1,7 +1,7 @@ TARGET = "OpenBoard" TEMPLATE = app -CONFIG += c++14 +CONFIG += c++17 CONFIG -= flat CONFIG += debug_and_release \ no_include_pwd @@ -9,9 +9,9 @@ CONFIG += debug_and_release \ VERSION_MAJ = 1 VERSION_MIN = 6 -VERSION_PATCH = 1 -VERSION_TYPE = r # a = alpha, b = beta, rc = release candidate, r = release, other => error -VERSION_BUILD = 0309 +VERSION_PATCH = 2 +VERSION_TYPE = rc # a = alpha, b = beta, rc = release candidate, r = release, other => error +VERSION_BUILD = 0408 VERSION = "$${VERSION_MAJ}.$${VERSION_MIN}.$${VERSION_PATCH}-$${VERSION_TYPE}.$${VERSION_BUILD}" @@ -40,6 +40,7 @@ QT += webkitwidgets QT += multimediawidgets QT += printsupport QT += core +QT += concurrent INCLUDEPATH += src @@ -57,7 +58,8 @@ include(src/podcast/podcast.pri) include(src/tools/tools.pri) include(src/desktop/desktop.pri) include(src/web/web.pri) -include(src/qtsingleapplication/src/qtsingleapplication.pri) +include(src/singleapplication/singleapplication.pri) +DEFINES += QAPPLICATION_CLASS=QApplication DEPENDPATH += src/pdf-merger INCLUDEPATH += src/pdf-merger @@ -177,26 +179,25 @@ macx { equals(QT_MAJOR_VERSION, 5):lessThan(QT_MINOR_VERSION, 14) { LIBS += "-L../OpenBoard-ThirdParty/quazip/lib/macx" "-lquazip" } else { - LIBS += -L/usr/local/opt/quazip/lib -lquazip + LIBS += -L/usr/local/opt/quazip/lib -lquazip1-qt5 } - LIBS += -L/usr/local/opt/ffmpeg/lib + LIBS += -L/opt/local/lib INCLUDEPATH += /usr/local/opt/openssl/include - INCLUDEPATH += /usr/local/opt/ffmpeg/include + INCLUDEPATH += /opt/local/include equals(QT_MAJOR_VERSION, 5):lessThan(QT_MINOR_VERSION, 14) { INCLUDEPATH += ../OpenBoard-ThirdParty/quazip/quazip-0.7.1 } else { INCLUDEPATH += /usr/local/opt/quazip/include/quazip } - LIBS += -L/usr/local/opt/poppler/lib -lpoppler - INCLUDEPATH += /usr/local/opt/poppler/include - INCLUDEPATH += /usr/local/opt/poppler/include/poppler + LIBS += -L/opt/local/lib -lpoppler + INCLUDEPATH += /opt/local/include/poppler CONFIG(release, debug|release):CONFIG += x86_64 CONFIG(debug, debug|release):CONFIG += x86_64 QMAKE_MAC_SDK = macosx - QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.10 + QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.13 QMAKE_CXXFLAGS += -Wno-overloaded-virtual #VERSION_RC_PATH = "$$BUILD_DIR/version_rc" @@ -451,7 +452,7 @@ linux-g++* { #LIBS += -lprofiler LIBS += -lX11 LIBS += -lquazip5 - INCLUDEPATH += "/usr/include/quazip" + INCLUDEPATH += "/usr/include/quazip5" LIBS += -lpoppler INCLUDEPATH += "/usr/include/poppler" diff --git a/README.md b/README.md index 35623702..d90a97d3 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,14 @@ # OpenBoard OpenBoard is an open source cross-platform interactive white board application designed primarily for use in schools. It was originally forked from Open-Sankoré, which was itself based on Uniboard. -Supported platforms are Windows (7+), OS X (10.9+) and Linux (tested on Ubuntu 16.04). - +Supported platforms are Windows (10+), OS X (10.13+) and Linux (Ubuntu 20.04 is the only officially maintained platform, but many others are reported by the community to work). ## Installing -Installers are available for Windows, OS X and Ubuntu on the [wiki](https://github.com/DIP-SEM/OpenBoard/wiki/Downloads). +Installers are available for Windows, OS X and Ubuntu on the [wiki](https://github.com/OpenBoard-org/OpenBoard/wiki/Downloads). ## Building from source -First, obtain the third party libraries from the OpenBoard-ThirdParty repository, and build them (instructions are provided for each library). - -Then, you may use the build (and packaging) scripts which are provided for all three platforms. These take care of compiling OpenBoard, including the translations (for OpenBoard and for Qt), stripping the debug symbols, creating the installers etc. -Minor modification to those scripts may be necessary depending on your configuration, to set the correct Qt path for example. - -Alternatively, you can easily build OpenBoard with qmake and make: - - qmake OpenBoard.pro -spec linux-g++-64 # replace linux-g++-64 by macx or win32 for other platforms - make - -Compilers used are gcc (Linux), clang (OS X) and MSVC 2010 (Windows). Make sure that your version of Qt matches this, as it is not possible e.g to build OpenBoard with clang if Qt was built with gcc. +Please follow instructions available on the [wiki](https://github.com/OpenBoard-org/OpenBoard/wiki/Build-from-source) ## Dependencies -The latest version (1.5) requires Qt 5.5. (While it has been shown to mostly work with Qt 5.2, we cannot guarantee compatibility with Qt versions other than 5.5.) - -### Qt 5.5 on Linux - -Due to a shared library conflict within Qt 5 in some distributions / some Qt versions (the Multimedia and Webkit modules were built against different versions of gstreamer by default), a specific installation of Qt5.5 may be needed for all of OpenBoard's features to work correctly. - -It can either be built from source, with the configure flag `-gstreamer 1.0` (see [here](http://doc.qt.io/qt-5/linux-building.html)), or installed from Stephan Binner's PPAs on Ubuntu. -In the latter case, simply add the repositories and install Qt 5.5.1 like so (example provided for Ubuntu 14.04, aka "Trusty"): - - sudo add-apt-repository ppa:beineri/opt-qt551-trusty - sudo apt-get update - sudo apt-get install qt-latest - -Some distributions, such as Ubuntu 16.04, provide Qt 5.5.1 packages that work perfectly with OpenBoard, so you can simply install Qt from the official repository. +The latest version (1.6.1) can be compiled with the latest open source binaries of Qt 5.15. While it has been shown to work with Qt 5.9 or any other version in between, we recommend that you use the latest version you can. diff --git a/release_scripts/linux/build.sh b/release_scripts/linux/build.sh index 3918fc43..e882f622 100755 --- a/release_scripts/linux/build.sh +++ b/release_scripts/linux/build.sh @@ -17,7 +17,7 @@ initializeVariables() { APPLICATION_NAME="OpenBoard" - STANDARD_QT_USED=false + STANDARD_QT_USED=true # Root directory SCRIPT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -26,7 +26,7 @@ initializeVariables() PRODUCT_PATH="$BUILD_DIR/product" # Qt installation path. This may vary across machines - QT_PATH="/home/dev/Qt/5.15.0/gcc_64" + QT_PATH="/usr/lib/x86_64-linux-gnu/qt5" PLUGINS_PATH="$QT_PATH/plugins" GUI_TRANSLATIONS_DIRECTORY_PATH="/usr/share/qt5/translations" QMAKE_PATH="$QT_PATH/bin/qmake" @@ -39,6 +39,9 @@ initializeVariables() ARCHITECTURE=`uname -m` if [ $ARCHITECTURE == "x86_64" ]; then ARCHITECTURE="amd64" + elif [$ARCHITECTURE == "armv7l" ]; then + $ARCHITECTURE="armhf" + QT_PATH="/usr/lib/arm-linux-gnueabihf/qt5" fi fi } diff --git a/release_scripts/linux/package.sh b/release_scripts/linux/package.sh index c53e3081..2014c3c7 100755 --- a/release_scripts/linux/package.sh +++ b/release_scripts/linux/package.sh @@ -85,13 +85,13 @@ initializeVariables() # Include Qt libraries and plugins in the package, or not # (this is necessary if the target system doesn't provide Qt 5.5.1) - BUNDLE_QT=true + BUNDLE_QT=false # Qt installation path. This may vary across machines - QT_PATH="/home/dev/Qt/5.15.0/gcc_64" + QT_PATH="/usr/lib/x86_64-linux-gnu/qt5" QT_PLUGINS_SOURCE_PATH="$QT_PATH/plugins" GUI_TRANSLATIONS_DIRECTORY_PATH="/usr/share/qt5/translations" - QT_LIBRARY_SOURCE_PATH="$QT_PATH/lib" + QT_LIBRARY_SOURCE_PATH="$QT_PATH/.." NOTIFY_CMD=`which notify-send` ZIP_PATH=`which zip` @@ -180,6 +180,7 @@ chown -R root:root $PACKAGE_DIRECTORY cp -R resources/customizations $PACKAGE_DIRECTORY/ cp resources/linux/openboard-ubz.xml $PACKAGE_DIRECTORY/etc/ +cp resources/linux/application-ubz.png $PACKAGE_DIRECTORY/etc/ if $BUNDLE_QT; then cp -R resources/linux/run.sh $PACKAGE_DIRECTORY/ @@ -214,6 +215,7 @@ if $BUNDLE_QT; then notifyProgress "Copying and stripping Qt libraries" mkdir -p $QT_LIBRARY_DEST_PATH + copyQtLibrary libQt5Concurrent copyQtLibrary libQt5Core copyQtLibrary libQt5DBus copyQtLibrary libQt5Gui @@ -287,6 +289,7 @@ cat > "$BASE_WORKING_DIR/DEBIAN/postinst" << EOF xdg-desktop-menu install --novendor /usr/share/applications/${APPLICATION_CODE}.desktop xdg-mime install --mode system /$APPLICATION_PATH/$APPLICATION_CODE/etc/openboard-ubz.xml xdg-mime default /usr/share/applications/${APPLICATION_CODE}.desktop application/ubz +xdg-icon-resource install --context mimetypes --size 48 /$APPLICATION_PATH/$APPLICATION_CODE/etc/application-ubz.png application-ubz ln -s $SYMLINK_TARGET /usr/bin/$APPLICATION_CODE @@ -400,8 +403,6 @@ echo "MimeType=application/ubz" >> $APPLICATION_SHORTCUT echo "Categories=Education;" >> $APPLICATION_SHORTCUT cp "resources/images/${APPLICATION_NAME}.png" "$PACKAGE_DIRECTORY/${APPLICATION_NAME}.png" - - # ---------------------------------------------------------------------------- # Building the package # ---------------------------------------------------------------------------- diff --git a/release_scripts/osx/release.macx.sh b/release_scripts/osx/release.macx.sh index 376fdb68..456b3e90 100755 --- a/release_scripts/osx/release.macx.sh +++ b/release_scripts/osx/release.macx.sh @@ -19,7 +19,7 @@ PROJECT_ROOT="$SCRIPT_PATH/../.." APPLICATION_NAME="OpenBoard" -BASE_QT_DIR=~/Qt/5.15.0/clang_64 +BASE_QT_DIR=~/Qt/5.15.2/clang_64 # Executables QMAKE=$BASE_QT_DIR/bin/qmake MACDEPLOYQT=$BASE_QT_DIR/bin/macdeployqt @@ -99,7 +99,7 @@ function addImporter { rm MakeFile* rm -rf release rm -rf debug - $QMAKE ${importerName}.pro + $QMAKE ${importerName}.pro -spec macx-clang make -j4 release $MACDEPLOYQT ${importerName}.app cd - @@ -143,9 +143,9 @@ notify "Generating Makefile ..." if [ "$1" == "1010" ]; then - QMAKE_CMD="$QMAKE \"DEFINES+=OS_NEWER_THAN_OR_EQUAL_TO_1010\" $APPLICATION_NAME.pro -spec macx-g++" + QMAKE_CMD="$QMAKE \"DEFINES+=OS_NEWER_THAN_OR_EQUAL_TO_1010\" $APPLICATION_NAME.pro -spec macx-clang" else - QMAKE_CMD="$QMAKE $APPLICATION_NAME.pro -spec macx-g++" + QMAKE_CMD="$QMAKE $APPLICATION_NAME.pro -spec macx-clang" fi $QMAKE_CMD @@ -192,7 +192,7 @@ rm -f "$APP/Contents/Resources/empty.lproj" # set various version infomration in Info.plist $PLISTBUDDY -c "Set :CFBundleVersion $VERSION" "$INFO_PLIST" $PLISTBUDDY -c "Set :CFBundleShortVersionString $VERSION" "$INFO_PLIST" -$PLISTBUDDY -c "Set :CFBundleGetInfoString $APPLICATION_NAME" "$INFO_PLIST" +$PLISTBUDDY -c "Set :CFBundleGetInfoString $VERSION" "$INFO_PLIST" # bundle Qt Frameworks into the app bundle notify "Bulding frameworks ..." @@ -200,32 +200,6 @@ cd "`pwd`/build/macx/release/product/" $MACDEPLOYQT "`pwd`/$APPLICATION_NAME.app" cd - -# make sure libs installed via homebrew 2.0 refer to in-app libs - notify "relinking libs ..." -# libavformat -install_name_tool "$APP/Contents/Frameworks/libavformat.58.dylib" -change /usr/local/Cellar/ffmpeg/4.3.1_1/lib/libavcodec.58.dylib @executable_path/../Frameworks/libavcodec.58.dylib -install_name_tool "$APP/Contents/Frameworks/libavformat.58.dylib" -change /usr/local/Cellar/ffmpeg/4.3.1_1/lib/libswresample.3.dylib @executable_path/../Frameworks/libswresample.3.dylib -install_name_tool "$APP/Contents/Frameworks/libavformat.58.dylib" -change /usr/local/Cellar/ffmpeg/4.3.1_1/lib/libavutil.56.dylib @executable_path/../Frameworks/libavutil.56.dylib - -# libavcodec -install_name_tool "$APP/Contents/Frameworks/libavcodec.58.dylib" -change /usr/local/Cellar/ffmpeg/4.3.1_1/lib/libswresample.3.dylib @executable_path/../Frameworks/libswresample.3.dylib -install_name_tool "$APP/Contents/Frameworks/libavcodec.58.dylib" -change /usr/local/Cellar/ffmpeg/4.3.1_1/lib/libavutil.56.dylib @executable_path/../Frameworks/libavutil.56.dylib - -#libswresample -install_name_tool "$APP/Contents/Frameworks/libswresample.3.dylib" -change /usr/local/Cellar/ffmpeg/4.3.1_1/lib/libavutil.56.dylib @executable_path/../Frameworks/libavutil.56.dylib - -#libswscale -install_name_tool "$APP/Contents/Frameworks/libswscale.5.dylib" -change /usr/local/Cellar/ffmpeg/4.3.1_1/lib/libavutil.56.dylib @executable_path/../Frameworks/libavutil.56.dylib - -# libhogweed -install_name_tool "$APP/Contents/Frameworks/libhogweed.6.dylib" -change /usr/local/Cellar/nettle/3.6/lib/libnettle.8.dylib @executable_path/../Frameworks/libnettle.8.dylib - -# libssl -install_name_tool "$APP/Contents/Frameworks/libssl.1.1.dylib" -change /usr/local/Cellar/openssl@1.1/1.1.1h/lib/libcrypto.1.1.dylib @executable_path/../Frameworks/libcrypto.1.1.dylib - -# libvorbis -install_name_tool "$APP/Contents/Frameworks/libvorbisenc.2.dylib" -change /usr/local/Cellar/libvorbis/1.3.6/lib/libvorbis.0.dylib @executable_path/../Frameworks/libvorbis.0.dylib - notify "Extracting debug information ..." $DSYMUTIL "$APP/Contents/MacOS/$APPLICATION_NAME" -o "$DSYM" $STRIP -S "$APP/Contents/MacOS/$APPLICATION_NAME" diff --git a/resources/OpenBoard.qrc b/resources/OpenBoard.qrc index 9327c9e7..d1a5661f 100644 --- a/resources/OpenBoard.qrc +++ b/resources/OpenBoard.qrc @@ -378,5 +378,12 @@ images/stylusPalette/pen.svg images/stylusPalette/penOn.svg images/stylusPalette/penOnArrow.svg + images/trash-my-documents.png + images/trash-empty.png + images/trash-folder.png + images/trash-delete-document.png + images/trash-delete-folder.png + images/trash-document.png + images/trash-document-page.png diff --git a/resources/etc/OpenBoard.config b/resources/etc/OpenBoard.config index 5d29fb4b..77eea8d1 100644 --- a/resources/etc/OpenBoard.config +++ b/resources/etc/OpenBoard.config @@ -124,6 +124,8 @@ RefreshRateInFramePerSecond=2 [PDF] enableQualityLossToIncreaseZoomPerfs=true +ExportBackgroundGrid=false +ExportBackgroundColor=false Margin=20 PageFormat=A4 Resolution=300 @@ -147,11 +149,8 @@ ReplyPlusMaxKeypads=100 ReplyWWSerialPort=3 [Web] -AddBookmarkURL="http://www.myuniboard.com/bookmarks/save/?url=" -BookmarksPage=http://www.myuniboard.com HistoryLimit=15 Homepage=http://www.openboard.ch -ShowAddBookmarkButton=true ShowPageImediatelyOnMirroredScreen=false UseExternalBrowser=false diff --git a/resources/forms/mainWindow.ui b/resources/forms/mainWindow.ui index 052230ac..a24810b4 100644 --- a/resources/forms/mainWindow.ui +++ b/resources/forms/mainWindow.ui @@ -1497,16 +1497,16 @@ Configure Podcast Recording - + :/images/toolbar/addToolToLibrary.png:/images/toolbar/addToolToLibrary.png - Flash Trap + Capture Web Content - Trap Flash Content + Capture Web Content diff --git a/resources/forms/trapFlash.ui b/resources/forms/trapFlash.ui index 2a7f4a69..00c36a00 100644 --- a/resources/forms/trapFlash.ui +++ b/resources/forms/trapFlash.ui @@ -19,7 +19,7 @@ - Select a flash to trap + Select a content to capture diff --git a/resources/i18n/OpenBoard_ar.ts b/resources/i18n/OpenBoard_ar.ts index 9ad98c6d..3f19c962 100644 --- a/resources/i18n/OpenBoard_ar.ts +++ b/resources/i18n/OpenBoard_ar.ts @@ -697,11 +697,11 @@ Flash Trap - قلاش تراب + قلاش تراب Trap Flash Content - محتوى فلاش تراب + محتوى فلاش تراب Web Trap @@ -839,6 +839,10 @@ Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -896,6 +900,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? هل تريدون حقا إزالة صفحة واحدة من الوثيقة '%0' المختارة؟ + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -907,6 +919,10 @@ Podcast بودكاست + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -1235,6 +1251,10 @@ Title page + + Refreshing Document Thumbnails View (%1/%2) + + UBDocumentManager @@ -1285,6 +1305,10 @@ Page %0 صفحة %0 + + Generating thumbnails for board (%1/%2) + + UBDocumentPublisher @@ -1985,6 +2009,10 @@ Do you want to ignore these errors for this host? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -2148,6 +2176,10 @@ Do you want to ignore these errors for this host? %1 thumbnails generated ... 1 % صور مصغرة مولّدة + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2924,7 +2956,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - اختيار فلاش للامساك به + اختيار فلاش للامساك به about:blank @@ -2938,5 +2970,9 @@ p, li { white-space: pre-wrap; } Create Application إنشاء تطبيق + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_bg.ts b/resources/i18n/OpenBoard_bg.ts index 303f13a3..1628b746 100644 --- a/resources/i18n/OpenBoard_bg.ts +++ b/resources/i18n/OpenBoard_bg.ts @@ -697,11 +697,11 @@ Flash Trap - Инструмент за Flash съдържание + Инструмент за Flash съдържание Trap Flash Content - Съдържание на Flash + Съдържание на Flash Web Trap @@ -839,6 +839,10 @@ Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -896,6 +900,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? Сигурни ли сте ,че искате да премахнете 1 страница от избрания документ '%0'? + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -907,6 +919,10 @@ Podcast Подкаст + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -1227,6 +1243,10 @@ Title page + + Refreshing Document Thumbnails View (%1/%2) + + UBDocumentManager @@ -1277,6 +1297,10 @@ Page %0 Страница %0 + + Generating thumbnails for board (%1/%2) + + UBDocumentReplaceDialog @@ -1964,6 +1988,10 @@ Do you want to ignore these errors for this host? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -2127,6 +2155,10 @@ Do you want to ignore these errors for this host? %1 thumbnails generated ... %1 икони са създадени ... + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2904,7 +2936,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - Избери флаш , който ще използваш + Избери флаш , който ще използваш about:blank @@ -2918,5 +2950,9 @@ p, li { white-space: pre-wrap; } Create Application Създаване на приложение + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_ca.ts b/resources/i18n/OpenBoard_ca.ts index e7b01e8d..e18fcfd4 100644 --- a/resources/i18n/OpenBoard_ca.ts +++ b/resources/i18n/OpenBoard_ca.ts @@ -729,11 +729,11 @@ Flash Trap - Captura d'objectes Flash + Captura d'objectes Flash Trap Flash Content - Captura un objecte Flash + Captura un objecte Flash Web Trap @@ -839,6 +839,10 @@ Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -896,6 +900,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? Esteu segur que voleu eliminar 1 pàgina del document seleccionat '%0'? + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -907,6 +919,10 @@ Podcast Podcast + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -1226,6 +1242,10 @@ Title page + + Refreshing Document Thumbnails View (%1/%2) + + UBDocumentManager @@ -1276,6 +1296,10 @@ Page %0 Pàgina %0 + + Generating thumbnails for board (%1/%2) + + UBDocumentPublisher @@ -1970,6 +1994,10 @@ Voleu ignorar aquests errors per a aquest amfitrió? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -2126,6 +2154,10 @@ Voleu ignorar aquests errors per a aquest amfitrió? %1 thumbnails generated ... S'han generat %1 miniatures... + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2904,7 +2936,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - Seleccioneu l'objecte Flash a capturar + Seleccioneu l'objecte Flash a capturar about:blank @@ -2918,5 +2950,9 @@ p, li { white-space: pre-wrap; } Create Application Crea una aplicació + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_cs.ts b/resources/i18n/OpenBoard_cs.ts index 92187e20..2860b481 100644 --- a/resources/i18n/OpenBoard_cs.ts +++ b/resources/i18n/OpenBoard_cs.ts @@ -374,7 +374,7 @@ Trap Flash Content - Přenést obsah ve Flashi + Přenést obsah ve Flashi Import @@ -562,7 +562,7 @@ Flash Trap - Přenést Flash + Přenést Flash Window Capture @@ -840,6 +840,10 @@ Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -897,6 +901,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? Opravdu chcete odstranit 1 stránku z vybraného dokumentu '%0'? + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -908,6 +920,10 @@ Page Size Velikost stránky + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -1229,6 +1245,10 @@ Title page + + Refreshing Document Thumbnails View (%1/%2) + + UBDocumentManager @@ -1279,6 +1299,10 @@ Page %0 Stránka %0 + + Generating thumbnails for board (%1/%2) + + UBDocumentReplaceDialog @@ -1969,6 +1993,10 @@ Chcete ignorovat tyto chyby na tomto serveru? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -2132,6 +2160,10 @@ Chcete ignorovat tyto chyby na tomto serveru? Generating preview thumbnails ... Vytváří se miniatury obrázků ... + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2910,7 +2942,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - Vyberte flash, který chcete přenést + Vyberte flash, který chcete přenést about:blank @@ -2924,5 +2956,9 @@ p, li { white-space: pre-wrap; } Create Application Vytvořit aplikaci + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_da.ts b/resources/i18n/OpenBoard_da.ts index 60f55f91..35cd0e1c 100644 --- a/resources/i18n/OpenBoard_da.ts +++ b/resources/i18n/OpenBoard_da.ts @@ -697,11 +697,11 @@ Flash Trap - Flash Trap + Flash Trap Trap Flash Content - Opsaml Flash-indhold + Opsaml Flash-indhold Web Trap @@ -839,6 +839,10 @@ Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -896,6 +900,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? Er du sikker på, at du vil fjerne 1 side fra det valgte dokument'%0'? + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -907,6 +919,10 @@ Podcast Podcast + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -1226,6 +1242,10 @@ Title page + + Refreshing Document Thumbnails View (%1/%2) + + UBDocumentManager @@ -1276,6 +1296,10 @@ Page %0 Side %0 + + Generating thumbnails for board (%1/%2) + + UBDocumentPublisher @@ -1970,6 +1994,10 @@ Do you want to ignore these errors for this host? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -2133,6 +2161,10 @@ Do you want to ignore these errors for this host? %1 thumbnails generated ... %1 miniaturebilleder genereret... + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2907,7 +2939,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - Vælg en flash der skal opsamles + Vælg en flash der skal opsamles about:blank @@ -2921,5 +2953,9 @@ p, li { white-space: pre-wrap; } Create Application Opret applikation + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_de.ts b/resources/i18n/OpenBoard_de.ts index 9a67156d..c6305089 100644 --- a/resources/i18n/OpenBoard_de.ts +++ b/resources/i18n/OpenBoard_de.ts @@ -697,11 +697,11 @@ Flash Trap - Einblendung einfangen + Einblendung einfangen Trap Flash Content - Eingeblendeten Inhalt einfangen + Eingeblendeten Inhalt einfangen Web Trap @@ -839,6 +839,10 @@ Draw intermediate grid lines Gitter-Zwischenlinien zeichnen + + Capture Web Content + Webinhalte erfassen + PasswordDialog @@ -896,6 +900,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? Wollen Sie wirklich die ausgewählte Seite des Dokuments '%0' entfernen? + + Loading scene (%1/%2) + Laden der Szene (%1/%2) + + + Moving cached scenes (%1/%2) + Verschieben von zwischengespeicherten Szenen (%1/%2) + UBApplication @@ -907,6 +919,10 @@ Podcast Podcast + + Cannot open your UBX file directly. Please import it in Documents mode instead + Ihre UBX-Datei kann nicht direkt geöffnet werden. Bitte importieren Sie die Datei im Dokumentenmodus + UBApplicationController @@ -1242,6 +1258,10 @@ Empty My Documents Eigene Dokumente leeren + + Refreshing Document Thumbnails View (%1/%2) + Aktualisieren der Vorschauen im Dokumentenmodus (%1/%2) + UBDocumentManager @@ -1292,6 +1312,10 @@ Page %0 Seite %0 + + Generating thumbnails for board (%1/%2) + Erstellen von Voransichten des Tabellenmodus (%1/%2) + UBDocumentPublisher @@ -2028,6 +2052,10 @@ Möchten Sie diese Fehler für diesen Computer ignorieren? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. Openboard hat den Zugang zum Dokumentenarchiv '%1' verloren. Die Anwendung muss leider beendet werden, um Datenkorruption zu vermeiden. Der Verlust kürzlich vorgenommener Änderungen ist möglich. + + Renaming pages (%1/%2) + Aktualisieren der aktuellen Seitennamen (%1/%2) + UBPlatformUtils @@ -2196,6 +2224,10 @@ Möchten Sie diese Fehler für diesen Computer ignorieren? Miniaturansicht der Seite %1 wird geladen + + Loading thumbnail (%1/%2) + Laden der Vorschau (%1/%2) + UBThumbnailTextItem @@ -2978,7 +3010,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - Wählen Sie eine Einblendung, die festgehalten werden soll + Wählen Sie eine Einblendung, die festgehalten werden soll about:blank @@ -2992,5 +3024,9 @@ p, li { white-space: pre-wrap; } Create Application Anwendung erstellen + + Select a content to capture + Einen zu erfassenden Inhalt auswählen + diff --git a/resources/i18n/OpenBoard_el.ts b/resources/i18n/OpenBoard_el.ts index 329a7b9a..4296a070 100644 --- a/resources/i18n/OpenBoard_el.ts +++ b/resources/i18n/OpenBoard_el.ts @@ -697,11 +697,11 @@ Flash Trap - Λήψη αντικειμένου flash + Λήψη αντικειμένου flash Trap Flash Content - Λήψη περιεχομένου ενός αντικειμένου flash + Λήψη περιεχομένου ενός αντικειμένου flash Web Trap @@ -839,6 +839,10 @@ Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -896,6 +900,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? Είστε βέβαιος ότι θέλετε να αφαιρέσετε μια σελίδα από το επιλεγμένο έγγραφο '%0'? + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -907,6 +919,10 @@ Podcast Βίντεο + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -1226,6 +1242,10 @@ Title page + + Refreshing Document Thumbnails View (%1/%2) + + UBDocumentManager @@ -1276,6 +1296,10 @@ Page %0 Σελίδα %0 + + Generating thumbnails for board (%1/%2) + + UBDocumentReplaceDialog @@ -1963,6 +1987,10 @@ Do you want to ignore these errors for this host? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -2126,6 +2154,10 @@ Do you want to ignore these errors for this host? %1 thumbnails generated ... %1 μικρογραφίες δημιουργήθηκαν... + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2904,7 +2936,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - Επιλογή αντικειμένου flash για λήψη + Επιλογή αντικειμένου flash για λήψη about:blank @@ -2918,5 +2950,9 @@ p, li { white-space: pre-wrap; } Create Application Δημιουργία εφαρμογής + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_en.ts b/resources/i18n/OpenBoard_en.ts index 348265ef..ca3377a0 100644 --- a/resources/i18n/OpenBoard_en.ts +++ b/resources/i18n/OpenBoard_en.ts @@ -703,14 +703,6 @@ Configure Podcast Recording - - Flash Trap - - - - Trap Flash Content - - Web Trap @@ -831,6 +823,10 @@ Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -888,6 +884,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -899,6 +903,10 @@ Podcast + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -1166,6 +1174,10 @@ Empty + + Refreshing Document Thumbnails View (%1/%2) + + UBDocumentManager @@ -1216,6 +1228,10 @@ Page %0 + + Generating thumbnails for board (%1/%2) + + UBDocumentReplaceDialog @@ -1845,6 +1861,10 @@ Do you want to ignore these errors for this host? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -1974,6 +1994,10 @@ Do you want to ignore these errors for this host? %1 thumbnails generated ... + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2718,10 +2742,6 @@ p, li { white-space: pre-wrap; } Trap flash - - Select a flash to trap - - about:blank @@ -2734,5 +2754,9 @@ p, li { white-space: pre-wrap; } Create Application + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_en_UK.ts b/resources/i18n/OpenBoard_en_UK.ts index 348265ef..ca3377a0 100644 --- a/resources/i18n/OpenBoard_en_UK.ts +++ b/resources/i18n/OpenBoard_en_UK.ts @@ -703,14 +703,6 @@ Configure Podcast Recording - - Flash Trap - - - - Trap Flash Content - - Web Trap @@ -831,6 +823,10 @@ Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -888,6 +884,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -899,6 +903,10 @@ Podcast + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -1166,6 +1174,10 @@ Empty + + Refreshing Document Thumbnails View (%1/%2) + + UBDocumentManager @@ -1216,6 +1228,10 @@ Page %0 + + Generating thumbnails for board (%1/%2) + + UBDocumentReplaceDialog @@ -1845,6 +1861,10 @@ Do you want to ignore these errors for this host? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -1974,6 +1994,10 @@ Do you want to ignore these errors for this host? %1 thumbnails generated ... + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2718,10 +2742,6 @@ p, li { white-space: pre-wrap; } Trap flash - - Select a flash to trap - - about:blank @@ -2734,5 +2754,9 @@ p, li { white-space: pre-wrap; } Create Application + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_es.ts b/resources/i18n/OpenBoard_es.ts index f3543d1a..8569eca5 100644 --- a/resources/i18n/OpenBoard_es.ts +++ b/resources/i18n/OpenBoard_es.ts @@ -697,11 +697,11 @@ Flash Trap - Captura de elemento Flash + Captura de elemento Flash Trap Flash Content - Capturar contenido Flash + Capturar contenido Flash Web Trap @@ -839,6 +839,10 @@ Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -896,6 +900,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? ¿Está seguro de que quiere eliminar 1 página del documento seleccionado.'%0? + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -907,6 +919,10 @@ Podcast Podcast + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -1238,6 +1254,10 @@ Title page Página de título + + Refreshing Document Thumbnails View (%1/%2) + + UBDocumentManager @@ -1288,6 +1308,10 @@ Page %0 Página %0 + + Generating thumbnails for board (%1/%2) + + UBDocumentReplaceDialog @@ -1979,6 +2003,10 @@ Do you want to ignore these errors for this host? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. OpenBoard perdió el acceso al repositorio de documentos '%1'. Desafortunadamente, la aplicación debe cerrarse para evitar la corrrupción de datos. También se pueden perder los últimos cambios. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -2146,6 +2174,10 @@ Do you want to ignore these errors for this host? loading thumbnail of page %1 cargando miniaturas de la página %1 + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2924,7 +2956,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - Seleccionar un flash para capturar + Seleccionar un flash para capturar about:blank @@ -2938,5 +2970,9 @@ p, li { white-space: pre-wrap; } Create Application Crear aplicación + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_fr.ts b/resources/i18n/OpenBoard_fr.ts index a0735e73..8551977e 100644 --- a/resources/i18n/OpenBoard_fr.ts +++ b/resources/i18n/OpenBoard_fr.ts @@ -181,11 +181,11 @@ Flash Trap - Capturer du contenu Flash + Capturer du contenu Flash Trap Flash Content - Capturer du contenu Flash + Capturer du contenu Flash Web Trap @@ -840,6 +840,10 @@ Draw intermediate grid lines Dessiner des lignes intermédiaires + + Capture Web Content + Capturer du contenu web + PasswordDialog @@ -897,6 +901,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? Voulez-vous vraiment effacer 1 page de ce document '%0'? + + Loading scene (%1/%2) + Chargement de la scène (%1/%2) + + + Moving cached scenes (%1/%2) + Déplacement des scènes mises en cache (%1/%2) + UBApplication @@ -908,6 +920,10 @@ Podcast Podcast + + Cannot open your UBX file directly. Please import it in Documents mode instead + Impossible d'ouvrir un fichier UBX directement. Veuillez l'importer depuis le mode Documents + UBApplicationController @@ -1251,6 +1267,10 @@ Empty My Documents Vider Mes Documents + + Refreshing Document Thumbnails View (%1/%2) + Actualisation des aperçus du mode Documents (%1/%2) + UBDocumentManager @@ -1302,6 +1322,10 @@ Page %0 Page %0 + + Generating thumbnails for board (%1/%2) + Création des aperçus du mode Tableau (%1/%2) + UBDocumentPublisher @@ -2041,6 +2065,10 @@ Voulez-vous ignorer les erreurs pour ce serveur ? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. OpenBoard a perdu l'accès au répertoire des documents '%1'. Malheureusement, l'application sera fermée afin d'éviter la corruption des données. Les dernières modifications pourraient être également perdues. + + Renaming pages (%1/%2) + Actualisation des noms des pages en cours (%1/%2) + UBPlatformUtils @@ -2208,6 +2236,10 @@ Voulez-vous ignorer les erreurs pour ce serveur ? loading thumbnail of page %1 Chargement aperçu page %1 + + Loading thumbnail (%1/%2) + Chargement de l'aperçu (%1/%2) + UBThumbnailTextItem @@ -2995,7 +3027,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - Sélectionner un contenu Flash à capturer + Sélectionner un contenu Flash à capturer about:blank @@ -3009,5 +3041,9 @@ p, li { white-space: pre-wrap; } Create Application Créer une application + + Select a content to capture + Sélectionner un contenu à capturer + diff --git a/resources/i18n/OpenBoard_fr_CH.ts b/resources/i18n/OpenBoard_fr_CH.ts index 2b764e91..408972ca 100644 --- a/resources/i18n/OpenBoard_fr_CH.ts +++ b/resources/i18n/OpenBoard_fr_CH.ts @@ -181,11 +181,11 @@ Flash Trap - Capturer du contenu Flash + Capturer du contenu Flash Trap Flash Content - Capturer du contenu Flash + Capturer du contenu Flash Web Trap @@ -840,6 +840,10 @@ Draw intermediate grid lines Dessiner des lignes intermédiaires + + Capture Web Content + Capturer du contenu web + PasswordDialog @@ -897,6 +901,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? Voulez-vous vraiment effacer 1 page de ce document '%0'? + + Loading scene (%1/%2) + Chargement de la scène (%1/%2) + + + Moving cached scenes (%1/%2) + Déplacement des scènes mises en cache (%1/%2) + UBApplication @@ -908,6 +920,10 @@ Podcast Podcast + + Cannot open your UBX file directly. Please import it in Documents mode instead + Impossible d'ouvrir un fichier UBX directement. Veuillez l'importer depuis le mode Documents + UBApplicationController @@ -1251,6 +1267,10 @@ Empty My Documents Vider Mes Documents + + Refreshing Document Thumbnails View (%1/%2) + Actualisation des aperçus du mode Documents (%1/%2) + UBDocumentManager @@ -1302,6 +1322,10 @@ Page %0 Page %0 + + Generating thumbnails for board (%1/%2) + Création des aperçus du mode Tableau (%1/%2) + UBDocumentPublisher @@ -2041,6 +2065,10 @@ Voulez-vous ignorer les erreurs pour ce serveur ? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. OpenBoard a perdu l'accès au répertoire des documents '%1'. Malheureusement, l'application sera fermée afin d'éviter la corruption des données. Les dernières modifications pourraient être également perdues. + + Renaming pages (%1/%2) + Actualisation des noms des pages en cours (%1/%2) + UBPlatformUtils @@ -2208,6 +2236,10 @@ Voulez-vous ignorer les erreurs pour ce serveur ? loading thumbnail of page %1 Chargement aperçu page %1 + + Loading thumbnail (%1/%2) + Chargement de l'aperçu (%1/%2) + UBThumbnailTextItem @@ -2995,7 +3027,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - Sélectionner un contenu Flash à capturer + Sélectionner un contenu Flash à capturer about:blank @@ -3009,5 +3041,9 @@ p, li { white-space: pre-wrap; } Create Application Créer une application + + Select a content to capture + Sélectionner un contenu à capturer + diff --git a/resources/i18n/OpenBoard_gl.ts b/resources/i18n/OpenBoard_gl.ts index ba0a0751..6d56580c 100644 --- a/resources/i18n/OpenBoard_gl.ts +++ b/resources/i18n/OpenBoard_gl.ts @@ -697,11 +697,11 @@ Flash Trap - Captura de elemento Flash + Captura de elemento Flash Trap Flash Content - Capturar contido Flash + Capturar contido Flash Web Trap @@ -839,6 +839,10 @@ Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -896,6 +900,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? ¿Está seguro de que quere eliminar 1 páxina do documento seleccionado.'%0? + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -907,6 +919,10 @@ Podcast Podcast + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -1238,6 +1254,10 @@ Title page Páxina de título + + Refreshing Document Thumbnails View (%1/%2) + + UBDocumentManager @@ -1288,6 +1308,10 @@ Page %0 Páxina %0 + + Generating thumbnails for board (%1/%2) + + UBDocumentReplaceDialog @@ -1979,6 +2003,10 @@ Do you want to ignore these errors for this host? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. OpenBoard perdeu o acceso ao repositorio de documentos '%1'. Desafortunadamente, a aplicación debe pecharse para evitar a corrrupción de datos. Tamén se poden perder os últimos cambios. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -2146,6 +2174,10 @@ Do you want to ignore these errors for this host? loading thumbnail of page %1 cargando miniaturas da páxina %1 + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2901,7 +2933,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - Seleccionar un flash para capturar + Seleccionar un flash para capturar about:blank @@ -2915,5 +2947,9 @@ p, li { white-space: pre-wrap; } Create Application Crear aplicación + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_hu.ts b/resources/i18n/OpenBoard_hu.ts index a66081cf..9f8bdf4f 100644 --- a/resources/i18n/OpenBoard_hu.ts +++ b/resources/i18n/OpenBoard_hu.ts @@ -50,7 +50,7 @@ IntranetPodcastPublishingDialog Publish Podcast to YouTube - Podcast publikálása a YouTubera + Podcast közzététele a YouTube-on Title @@ -441,7 +441,7 @@ Reload Current Page - Aktuális oldal újratöltése + Jelenlegi oldal újratöltése Load Home Page @@ -641,11 +641,11 @@ Add To New Page - Hozzáadás új oldalhoz + Hozzáadás az új oldalhoz Add Item To New Page - Tartalom hozzáadása új oldalhoz + Elem hozzáadása az új oldalhoz Add To Library @@ -653,7 +653,7 @@ Add Item To Library - Tartalom hozzáadása a könyvtárhoz + Elem hozzáadása a könyvtárhoz Pages @@ -701,11 +701,11 @@ Flash Trap - Flash rögzítése + Flash rögzítése Trap Flash Content - Flash tartalom rögzítése + Flash tartalom rögzítése Web Trap @@ -813,30 +813,34 @@ Small Eraser - + Kis radír Color 1 - + 1. szín Color 2 - + 2. szín Color 3 - + 3. szín Color 4 - + 4. szín Color 5 - + 5. szín Draw intermediate grid lines + Köztes rácsvonalak rajzolása + + + Capture Web Content @@ -886,14 +890,22 @@ Are you sure you want to remove 1 page from the selected document '%0'? - Biztosan el akarsz távolítani 1 oldalt a kiválasztott dokumentumból '%0'? + Biztosan szeretné eltávolítani 1 oldalt a kijelölt „%0”-dokumentumból? Element ID = - + Elem azonosítója = Content is not supported in destination format. + A tartalom nem támogatott célformátumban. + + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) @@ -907,6 +919,10 @@ Podcast Podcast + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -932,7 +948,7 @@ Board drawing... - Tábla rajzolása... + Tábla rajzolása… @@ -943,7 +959,7 @@ Draw intermediate grid lines - + Köztes rácsvonalak rajzolása @@ -990,11 +1006,11 @@ Saving document... - Dokumentum mentése... + Dokumentum mentése… Document has just been saved... - Dokumentum éppen most mentve... + Dokumentum éppen most mentve… Deleting page %1 @@ -1002,7 +1018,7 @@ Color - Szín + Szín @@ -1020,7 +1036,7 @@ UBBoardThumbnailsView Loading page (%1/%2) - Oldalak betöltése (%1/%2) + Oldalak betöltése: %1/%2 @@ -1113,23 +1129,23 @@ Are you sure you want to remove the document '%1'? - Biztosan szeretné eltávolítani a '%1' dokumentumot? + Biztosan szeretné eltávolítani a(z) „%1” dokumentumot? Empty Trash - Kuka kiürítése + Törölt elemek kiürítése Are you sure you want to empty trash? - Biztosan szeretné a kukát üríteni? + Biztosan szeretné kiüríteni a törölt elemeket? Emptying trash - Kuka kiürítése folyamatban + Törölt elemek kiürítése folyamatban van… Emptied trash - Kuka kiürítve + Törölt elemek kiürítve Remove Folder @@ -1137,7 +1153,7 @@ Are you sure you want to remove the folder '%1' and all its content? - Biztosan eltávolítja a(z) '%1' mappát és minden tartalmát? + Biztosan szeretné eltávolítani a(z) „%1” mappát és az összes tartalmát? No document selected! @@ -1149,7 +1165,7 @@ Importing file %1... - %1 fájl importálása... + %1 fájl importálása… Failed to import file ... @@ -1169,7 +1185,7 @@ Trash - Kuka + Törölt elemek Open Document @@ -1187,11 +1203,9 @@ Selection does not contain any image files! A kiválasztásban nincs egyetlen kép sem! - + Are you sure you want to remove %n page(s) from the selected document '%1'? - - Biztosan szeretne eltávolítani %n oldalt a kiválasztott '%1' dokumentumból? - + Biztosan szeretné eltávolítani %n oldalt a kiválasztott „%1” dokumentumból? Folder does not contain any image files @@ -1203,11 +1217,11 @@ The document '%1' has been generated with a newer version of OpenBoard (%2). By opening it, you may lose some information. Do you want to proceed? - A '%1' dokumentum az OpenBoard újabb verziójával (%2) készült. Megnyitásával néhány információ elveszhet. Szeretné folytatni? + A(z) „%1” dokumentum az OpenBoard újabb verziójával (%2) készült. Megnyitásával néhány információ elveszhet. Szeretné folytatni? Are you sure you want to remove all selected documents? - Biztosan szeretne eltávolítani minden kiválasztott dokumentumot? + Biztosan szeretné eltávolítani minden kijelölt dokumentumot? Remove multiple documents @@ -1217,19 +1231,23 @@ duplicated %1 page duplicated %1 pages - + %1 oldal másolata Remove Item - + Elem eltávolítása Are you sure you want to remove the selected item(s) ? - + Biztosan szeretné eltávolítani a kijelölt elem(ek)et? Title page + Címlap + + + Refreshing Document Thumbnails View (%1/%2) @@ -1261,7 +1279,7 @@ Inserting page %1 of %2 - %2 / %1 oldal beszúrása + Oldal beszúrása: %1/%2 Import successful. @@ -1280,6 +1298,10 @@ UBDocumentNavigator Page %0 + %0. oldal + + + Generating thumbnails for board (%1/%2) @@ -1287,32 +1309,34 @@ UBDocumentReplaceDialog Accept - Elfogad + Elfogadás Replace - + Csere Cancel - Mégse + Mégse The name %1 is allready used. Keeping this name will replace the document. Providing a new name will create a new document. - + A(z) %1 név már használatban van. +Ennek a névnek a megtartása helyettesíti a dokumentumot. +Új név megadása új dokumentumot hoz létre. UBDocumentTreeModel My documents - + Saját dokumentumok Trash - Kuka + Törölt elemek %1 pages copied @@ -1325,7 +1349,7 @@ Providing a new name will create a new document. UBDocumentTreeView Copying page %1/%2 - %1/%2 oldal másolása + Oldal másolása: %1/%2 %1 pages copied @@ -1335,11 +1359,11 @@ Providing a new name will create a new document. Remove Item - + Elem eltávolítása Are you sure you want to remove the selected item(s) ? - + Biztosan szeretné eltávolítani a kijelölt elem(ek)et? @@ -1350,13 +1374,11 @@ Providing a new name will create a new document. Copying page %1/%2 - %1/%2 oldal másolása + Oldal másolása: %1/%2 - + %1 pages copied - - %1 oldal másolva - + %1 oldal másolva @@ -1374,7 +1396,7 @@ Providing a new name will create a new document. UBDraggableThumbnail Page %0 - + %0. oldal @@ -1392,7 +1414,7 @@ Providing a new name will create a new document. Exporting document... - Dokumentum exportálása... + Dokumentum exportálása… Export failed @@ -1415,23 +1437,23 @@ Providing a new name will create a new document. UBExportCFF Export to IWB - + Exportálás IWB-formátumként Export as IWB File - + Exportálás IWB-fájlformátumként Exporting document... - Dokumentum exportálása... + Dokumentum exportálása… Export successful. - Exportálás sikeres. + Az exportálás sikeres. Export failed. - Exportálás sikertelen. + Az exportálás sikertelen. @@ -1446,7 +1468,7 @@ Providing a new name will create a new document. Exporting %1 %2 of %3 - %1 exportálása %2/%3 + %1 exportálása: %2/%3 Export to OpenBoard Format @@ -1457,27 +1479,27 @@ Providing a new name will create a new document. UBExportDocumentSetAdaptor Failed to export... - + Exportálás sikertelen… Export as UBX File - + Exportálás UBX-fájlként Exporting document... - Dokumentum exportálása... + Dokumentum exportálása… Export successful. - Exportálás sikeres. + Exportálás sikeres. Export failed. - Exportálás sikertelen. + Exportálás sikertelen. Export to OpenBoard UBX Format - + Exportálás OpenBoard UBX-formátumként @@ -1499,7 +1521,7 @@ Providing a new name will create a new document. Exporting page %1 of %2 - %2 / %1 oldal exportálása + Oldal exportálása: %1/%2 Export to PDF @@ -1518,7 +1540,7 @@ Providing a new name will create a new document. Exporting document... - Dokumentum exportálása... + Dokumentum exportálása… Export successful. @@ -1627,14 +1649,14 @@ Providing a new name will create a new document. Trash - Kuka + Törölt elemek UBFeaturesNewFolderDialog Accept - Elfogad + Elfogadás Cancel @@ -1705,7 +1727,7 @@ Providing a new name will create a new document. UBGraphicsTextItem <Type Text Here> - <Szöveg helye> + <Adja meg a szöveget ide> @@ -1730,7 +1752,7 @@ Providing a new name will create a new document. UBGraphicsWidgetItem Loading ... - Betöltés ... + Betöltés folyamatban van… @@ -1748,30 +1770,30 @@ Providing a new name will create a new document. UBImportCFF Common File Format ( - + Közös fájlformátum ( Importing file %1... - %1 fájl importálása... + %1 fájl importálása… Import of file %1 failed. - %1 fájl importálása sikertelen. + %1 fájl importálása sikertelen. Import successful. - Importálás sikeres. + Importálás sikeres. Import failed. - + Importálás sikertelen. UBImportDocument Importing file %1... - %1 fájl importálása... + %1 fájl importálása… Import successful. @@ -1790,14 +1812,14 @@ Providing a new name will create a new document. UBImportDocumentSetAdaptor Openboard (set of documents) (*.ubx) - + OpenBoard (dokumentumkészlet) (*.ubx) UBImportImage Image Format ( - Kép formátum ( + Kép-formátum ( @@ -1812,25 +1834,25 @@ Providing a new name will create a new document. Importing page %1 of %2 - %2 / %1 oldal importálása + Oldal importálása: %1/%2 UBIntranetPodcastPublisher Error while publishing video to intranet (%1) - Hiba a videó intranetre való publikálása közben (%1) + Hiba történt videó közzétételekor az intraneten (%1) Publishing to Intranet in progress %1 % - Intranetre publikálás folyamatban %1 % + Az intraneten való közzététel folyamatban van %1% UBIntranetPodcastPublishingDialog Publish - Publikálás + Közzététel @@ -1936,14 +1958,18 @@ Figyelmen kívül hagyja ezeket a hibákat ennél a hosztnál? has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. - elveszítette a '%1' dokumentum repository hozzáférését. Sajnálatosan az alkalmazásnak le kellett állnia, hogy elkerülje az adatsérülést. A legutóbbi változtatások elveszhettek. + elveszítette a(z) „%1” dokumentum repository hozzáférését. Sajnálatosan az alkalmazásnak le kellett állnia, hogy elkerülje az adatsérülést. A legutóbbi változtatások elveszhettek. Moving page to trash folder... - Oldal áthelyezése a kukába... + Oldal áthelyezése a törölt elemek mappába… OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + Az OpenBoard elvesztette a hozzáférést a(z) „%1” dokumentumtárához. Sajnos az alkalmazást le kell állítani az adatvesztés elkerülése érdekében. A legújabb változások is elveszhetnek. + + + Renaming pages (%1/%2) @@ -1974,11 +2000,11 @@ Figyelmen kívül hagyja ezeket a hibákat ennél a hosztnál? UBPodcastController Failed to start encoder ... - Sikertelen a kódoló indítása... + Sikertelen a kódoló indítása… No Podcast encoder available ... - Nincs elérhető podcast kódoló ... + Nincs elérhető podcast kódoló… Part %1 @@ -1986,7 +2012,7 @@ Figyelmen kívül hagyja ezeket a hibákat ennél a hosztnál? on your desktop ... - az asztalán ... + az asztalán… in folder %1 @@ -2022,11 +2048,11 @@ Figyelmen kívül hagyja ezeket a hibákat ennél a hosztnál? Publish to Intranet - Publikálás az Intranetre + Közzététel az Intraneten Publish to Youtube - Publikálás a YouTubera + Közzététel a Youtube-on OpenBoard Cast @@ -2048,7 +2074,7 @@ Figyelmen kívül hagyja ezeket a hibákat ennél a hosztnál? UBProxyLoginDlg Proxy Login - Proxy belépés + Proxy bejelentkezés Username: @@ -2063,7 +2089,7 @@ Figyelmen kívül hagyja ezeket a hibákat ennél a hosztnál? UBPublicationDlg Publish document on the web - Dokumentum publikálása a WEBre + Dokumentum közzététele az interneten Title: @@ -2075,7 +2101,7 @@ Figyelmen kívül hagyja ezeket a hibákat ennél a hosztnál? Publish - Publikálás + Közzététel @@ -2103,16 +2129,20 @@ Figyelmen kívül hagyja ezeket a hibákat ennél a hosztnál? UBThumbnailAdaptor Generating preview thumbnails ... - Előképek létrehozása... + Előképek létrehozása… %1 thumbnails generated ... - %1 előkép létrehozva... + %1 előkép létrehozva… loading thumbnail of page %1 %1. oldal előképének betöltése + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2153,7 +2183,7 @@ Figyelmen kívül hagyja ezeket a hibákat ennél a hosztnál? Axes - + Tengelyek @@ -2212,7 +2242,7 @@ Please reboot the application to access the updated documents. Please wait the import process will start soon... - Kérem várjon. Az importálási folyamat hamarosan elindul... + Kérem várjon. Az importálási folyamat hamarosan elindul… Remind me later @@ -2223,7 +2253,7 @@ Please reboot the application to access the updated documents. UBWebPluginWidget Loading... - Betöltés ... + Betöltés folyamatban van… @@ -2256,7 +2286,7 @@ Please reboot the application to access the updated documents. Autos & Vehicles - Autók & Járművek + Autók és Járművek Music @@ -2264,7 +2294,7 @@ Please reboot the application to access the updated documents. Pets & Animals - Házi kedvencek @ Állatok + Háziállatok és Állatok Sports @@ -2272,7 +2302,7 @@ Please reboot the application to access the updated documents. Travel & Events - Utazás & Események + Utazás és Események Gaming @@ -2284,11 +2314,11 @@ Please reboot the application to access the updated documents. People & Blogs - Emberek & Blogok + Emberek és Webes naplók News & Politics - Hírek & Politika + Hírek és Politika Entertainment @@ -2300,15 +2330,15 @@ Please reboot the application to access the updated documents. Howto & Style - Hogyan Csináld & Stílus + Hogyan kell és Stílus Nonprofits & Activism - NonProfit & Aktivisták + Nonprofit szervezetek és Aktivizmus Science & Technology - Tudomány & Technológia + Tudomány és Technológia @@ -2361,7 +2391,7 @@ Please reboot the application to access the updated documents. %1 of %2 (%3/sec) %4 - %1 / %2 (%3/mp) %4 + %1/%2 (%3/mp) %4 ? @@ -2370,7 +2400,7 @@ Please reboot the application to access the updated documents. %1 of %2 - Stopped - %1 / %2 - Megállítva + %1/%2 - Megállítva bytes @@ -2430,11 +2460,11 @@ Please reboot the application to access the updated documents. WBTabBar New &Tab - Új fül &T + Új &Lap Clone Tab - Fül másolása + Lap másolása &Close Tab @@ -2442,15 +2472,15 @@ Please reboot the application to access the updated documents. Close &Other Tabs - Többi fül bezárása &O + &Többi lap bezárása Reload Tab - Fül frissítése + Lap újratöltése Reload All Tabs - Minden fül frissítése + Minden lap újratöltése @@ -2491,7 +2521,7 @@ Please reboot the application to access the updated documents. Add to Current Document - Hozzaadás az aktuális dokumentumhoz + Hozzaadás a jelenlegi dokumentumhoz PDF @@ -2517,14 +2547,14 @@ Please reboot the application to access the updated documents. XPDFRenderer Processing... - + Feldolgozás folyamatban van… YouTubePublishingDialog Publish Podcast to YouTube - Podcast publikálása YouTubera + Podcast közzététele a YouTube-on Title @@ -2645,19 +2675,19 @@ p, li { white-space: pre-wrap; } Creation date - + Létrehozás dátuma Update date - + Frissítés dátuma Alphabetical order - + Rendezés betűrendben Sort Order - + Rendezési sorrend @@ -2744,7 +2774,7 @@ p, li { white-space: pre-wrap; } version : … - verziószám: ... + verziószám: Licences @@ -2828,31 +2858,31 @@ p, li { white-space: pre-wrap; } Swap first and second view displays - + Első és második kijelző megcserélése Documents Mode - + Dokumentum üzemmód Display date column on alphabetical sort - + Dátum oszlop megjelenítése betűrendben Empty trash for documents older than - + A törölt elemek ürítése a következőknél régebbi dokumentumokhoz: days - + nap PDF Rendering - + PDF-megjelenítő Improve zoom execution time (can slightly affect rendering quality) - + Nagyitás végrehajtási idő csökkentése (némileg befolyásolhatja a renderelés minőségét) @@ -2863,7 +2893,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - Flash kiválasztása a rögzítéshez + Flash kiválasztása a rögzítéshez about:blank @@ -2877,5 +2907,9 @@ p, li { white-space: pre-wrap; } Create Application Alkalmazás létrehozása + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_it.ts b/resources/i18n/OpenBoard_it.ts index c134c503..f7ec8b85 100644 --- a/resources/i18n/OpenBoard_it.ts +++ b/resources/i18n/OpenBoard_it.ts @@ -697,11 +697,11 @@ Flash Trap - Cattura Flash + Cattura Flash Trap Flash Content - Cattura contenuto flash + Cattura contenuto flash Web Trap @@ -839,6 +839,10 @@ Draw intermediate grid lines disegna linee di griglia intermedie + + Capture Web Content + Catturare il contenuto del web + PasswordDialog @@ -896,6 +900,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? Sei sicuro di voler rimuovere 1 pagina dal documento selezionato '%0'? + + Loading scene (%1/%2) + Caricamento della scena (%1/%2) + + + Moving cached scenes (%1/%2) + Spostamento delle scene in cache (%1/%2) + UBApplication @@ -907,6 +919,10 @@ Podcast PodCast + + Cannot open your UBX file directly. Please import it in Documents mode instead + Impossibile aprire direttamente un file UBX. Si prega di importarlo dalla modalità Documenti + UBApplicationController @@ -1238,6 +1254,10 @@ Title page Frontespizio + + Refreshing Document Thumbnails View (%1/%2) + Aggiornamento delle anteprime in modalità Documenti (%1/%2) + UBDocumentManager @@ -1288,6 +1308,10 @@ Page %0 Pagina %0 + + Generating thumbnails for board (%1/%2) + Creazione delle anteprime in modalità Lavagna (%1/%2) + UBDocumentPublisher @@ -2020,6 +2044,10 @@ Vuoi ignorare gli errori per questo host? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. OpenBoard ha perso l'accesso al repository documenti "%1". Sfortunatamente l'applicazione deve essere chiusa per evitare di rovinare i dati. Gli ultimi cambiamenti potrebbero andare persi. + + Renaming pages (%1/%2) + Aggiornamento dei nomi delle pagine in corso (%1/%2) + UBPlatformUtils @@ -2187,6 +2215,10 @@ Vuoi ignorare gli errori per questo host? loading thumbnail of page %1 Caricamento miniatura della pagina %1 + + Loading thumbnail (%1/%2) + Caricamento dell'anteprima (%1/%2) + UBThumbnailTextItem @@ -2969,7 +3001,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - Seleziona un'applicazione flash da catturare + Seleziona un'applicazione flash da catturare about:blank @@ -2983,5 +3015,9 @@ p, li { white-space: pre-wrap; } Create Application Crea applicazione + + Select a content to capture + Selezionare il contenuto da catturare + diff --git a/resources/i18n/OpenBoard_iw.ts b/resources/i18n/OpenBoard_iw.ts index c44ddd5b..a94f9f82 100644 --- a/resources/i18n/OpenBoard_iw.ts +++ b/resources/i18n/OpenBoard_iw.ts @@ -697,11 +697,11 @@ Flash Trap - מלכודת פלאש + מלכודת פלאש Trap Flash Content - תוכן מלכודת פלאש + תוכן מלכודת פלאש Web Trap @@ -840,6 +840,10 @@ Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -897,6 +901,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? האם אתה בטוח שברצונך למחוק דף 1 מהמסמך שנבחר %0? + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -908,6 +920,10 @@ Podcast פודקסט + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -1227,6 +1243,10 @@ Title page + + Refreshing Document Thumbnails View (%1/%2) + + UBDocumentManager @@ -1277,6 +1297,10 @@ Page %0 עמוד %0 + + Generating thumbnails for board (%1/%2) + + UBDocumentReplaceDialog @@ -1959,6 +1983,10 @@ Do you want to ignore these errors for this host? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -2122,6 +2150,10 @@ Do you want to ignore these errors for this host? %1 thumbnails generated ... נוצרה תמונה מוקטנת של %1... + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2896,7 +2928,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - בחר פלאש ללכידה + בחר פלאש ללכידה about:blank @@ -2910,5 +2942,9 @@ p, li { white-space: pre-wrap; } Create Application צור יישום + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_ja.ts b/resources/i18n/OpenBoard_ja.ts index 2af7f683..8051862e 100644 --- a/resources/i18n/OpenBoard_ja.ts +++ b/resources/i18n/OpenBoard_ja.ts @@ -697,11 +697,11 @@ Flash Trap - フラッシュトラップ + フラッシュトラップ Trap Flash Content - トラップフラッシュコンテンツ + トラップフラッシュコンテンツ Web Trap @@ -839,6 +839,10 @@ Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -896,6 +900,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -907,6 +919,10 @@ Podcast ポッドキャスト + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -1220,6 +1236,10 @@ Title page + + Refreshing Document Thumbnails View (%1/%2) + + UBDocumentManager @@ -1270,6 +1290,10 @@ Page %0 %0ページ + + Generating thumbnails for board (%1/%2) + + UBDocumentReplaceDialog @@ -1955,6 +1979,10 @@ Do you want to ignore these errors for this host? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -2118,6 +2146,10 @@ Do you want to ignore these errors for this host? %1 thumbnails generated ... %1 サムネイル作成済み ... + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2892,7 +2924,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - フラッシュを選択してトラップする + フラッシュを選択してトラップする about:blank @@ -2906,5 +2938,9 @@ p, li { white-space: pre-wrap; } Create Application アプリケーションを作成 + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_ko.ts b/resources/i18n/OpenBoard_ko.ts index c5b62ec7..3e0cfe29 100644 --- a/resources/i18n/OpenBoard_ko.ts +++ b/resources/i18n/OpenBoard_ko.ts @@ -697,11 +697,11 @@ Flash Trap - 플래시 트랩 + 플래시 트랩 Trap Flash Content - 플래시 콘텐츠 트랩 + 플래시 콘텐츠 트랩 Web Trap @@ -839,6 +839,10 @@ Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -896,6 +900,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? 선택한 문서 '%0'에서 1페이지를 제거하시겠습니까? + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -907,6 +919,10 @@ Podcast 팟캐스트 + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -1224,6 +1240,10 @@ Title page + + Refreshing Document Thumbnails View (%1/%2) + + UBDocumentManager @@ -1274,6 +1294,10 @@ Page %0 %0 페이지 + + Generating thumbnails for board (%1/%2) + + UBDocumentPublisher @@ -1974,6 +1998,10 @@ Do you want to ignore these errors for this host? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -2137,6 +2165,10 @@ Do you want to ignore these errors for this host? %1 thumbnails generated ... %1 썸네일 생성됨 ... + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2915,7 +2947,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - 플래시를 트랩으로 저장 + 플래시를 트랩으로 저장 about:blank @@ -2929,5 +2961,9 @@ p, li { white-space: pre-wrap; } Create Application 응용 프로그램 만들기 + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_mg.ts b/resources/i18n/OpenBoard_mg.ts index 7adefee0..98e8a6db 100644 --- a/resources/i18n/OpenBoard_mg.ts +++ b/resources/i18n/OpenBoard_mg.ts @@ -681,11 +681,11 @@ Flash Trap - Alaina ny Flash + Alaina ny Flash Trap Flash Content - Alaina ny mpiatiny Flash + Alaina ny mpiatiny Flash Web Trap @@ -839,6 +839,10 @@ Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -896,6 +900,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? Tena te hamafa pejy iray avy amin'ilay rakitra '%0' voafidy ve ianao ? + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -907,6 +919,10 @@ Podcast Podcast + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -1226,6 +1242,10 @@ Title page + + Refreshing Document Thumbnails View (%1/%2) + + UBDocumentManager @@ -1276,6 +1296,10 @@ Page %0 Pejy %0 + + Generating thumbnails for board (%1/%2) + + UBDocumentPublisher @@ -1970,6 +1994,10 @@ Tena tsy te hiraharaha an'ireo tsy mety ho an'ilay milina ve ianao?OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -2133,6 +2161,10 @@ Tena tsy te hiraharaha an'ireo tsy mety ho an'ilay milina ve ianao?%1 thumbnails generated ... %1 ny kisarisary no voaforona ... + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2911,7 +2943,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - Safidio ny Flash ho alaina + Safidio ny Flash ho alaina about:blank @@ -2925,5 +2957,9 @@ p, li { white-space: pre-wrap; } Create Application Mamorona rindran'asa + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_nb.ts b/resources/i18n/OpenBoard_nb.ts index 5b4409ae..e03e507c 100644 --- a/resources/i18n/OpenBoard_nb.ts +++ b/resources/i18n/OpenBoard_nb.ts @@ -697,11 +697,11 @@ Flash Trap - Flash trap + Flash trap Trap Flash Content - Trap flash-innhold + Trap flash-innhold Web Trap @@ -839,6 +839,10 @@ Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -896,6 +900,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? Er du sikker på at du vil fjerne side 1 fra det valgte dokumentet '%0'? + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -907,6 +919,10 @@ Podcast Podcast + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -1227,6 +1243,10 @@ Title page + + Refreshing Document Thumbnails View (%1/%2) + + UBDocumentManager @@ -1277,6 +1297,10 @@ Page %0 Side %0 + + Generating thumbnails for board (%1/%2) + + UBDocumentReplaceDialog @@ -1959,6 +1983,10 @@ Do you want to ignore these errors for this host? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -2122,6 +2150,10 @@ Do you want to ignore these errors for this host? %1 thumbnails generated ... %1 miniatyrbilder generert ... + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2901,7 +2933,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - Velg flash som skal tas + Velg flash som skal tas about:blank @@ -2915,5 +2947,9 @@ p, li { white-space: pre-wrap; } Create Application Opprett applikasjon + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_nl.ts b/resources/i18n/OpenBoard_nl.ts index d3e15859..cd013ed2 100644 --- a/resources/i18n/OpenBoard_nl.ts +++ b/resources/i18n/OpenBoard_nl.ts @@ -697,11 +697,11 @@ Flash Trap - Flash Trap + Flash Trap Trap Flash Content - Trap flash inhoud + Trap flash inhoud Web Trap @@ -839,6 +839,10 @@ Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -896,6 +900,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -907,6 +919,10 @@ Podcast Podcast + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -1222,6 +1238,10 @@ Title page + + Refreshing Document Thumbnails View (%1/%2) + + UBDocumentManager @@ -1272,6 +1292,10 @@ Page %0 Pagina %0 + + Generating thumbnails for board (%1/%2) + + UBDocumentPublisher @@ -1960,6 +1984,10 @@ Do you want to ignore these errors for this host? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -2123,6 +2151,10 @@ Do you want to ignore these errors for this host? %1 thumbnails generated ... %1 miniaturen opgeladen... + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2900,7 +2932,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - Te vangen licht selecteren + Te vangen licht selecteren about:blank @@ -2914,5 +2946,9 @@ p, li { white-space: pre-wrap; } Create Application Applicatie maken + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_pl.ts b/resources/i18n/OpenBoard_pl.ts index 5802b1bb..f1bc06d8 100644 --- a/resources/i18n/OpenBoard_pl.ts +++ b/resources/i18n/OpenBoard_pl.ts @@ -481,7 +481,7 @@ Grid Light Background - Jasne rastrowe tło + Jasne tło w kratkę Plain Dark Background @@ -493,7 +493,7 @@ Grid Dark Background - Ciemne rastrowe tło + Ciemne tło w kratkę Podcast @@ -561,7 +561,7 @@ Highlight - Wyróżnienie + Wyróżnienie Ctrl+M @@ -697,16 +697,15 @@ Flash Trap - Pułapka Flash + Pułapka Flash Trap Flash Content - Złap zawartość Flash + Złap zawartość Flash Web Trap - - + Pułapka sieciowa Trap Web Content @@ -797,50 +796,54 @@ Ruled Light Background - + Jasne tło w linie Ruled Dark Background - + Ciemne tło w linie Open Tutorial - + Otwórz samouczek Open the tutorial web page - + Otwórz stronę samouczka Reset grid size - + Zresetuj rozmiar kratki Small Eraser - + Mała gumka Color 1 - + Kolor 1 Color 2 - + Kolor 2 Color 3 - + Kolor 3 Color 4 - + Kolor 4 Color 5 - + Kolor 5 Draw intermediate grid lines + Rysuj drobne linie kratki + + + Capture Web Content @@ -886,7 +889,7 @@ QObject Element ID = - ID elementu = + ID elementu = Content is not supported in destination format. @@ -900,6 +903,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? Czy na pewno chcesz usunąć 1 stronę z wybranego dokumentu „%0”? + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -911,6 +922,10 @@ Podcast Podkast + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -939,11 +954,11 @@ UBBackgroundPalette Grid size - + Rozmiar kratki Draw intermediate grid lines - + Rysuj drobne linie kratki @@ -994,19 +1009,19 @@ Saving document... - + Zapisywanie dokumentu... Document has just been saved... - + Dokument został właśnie zapisany... Deleting page %1 - + Usuwanie strony %1 Color - Kolor + Kolor @@ -1024,7 +1039,7 @@ UBBoardThumbnailsView Loading page (%1/%2) - + Ładowanie strony (%1/%2) @@ -1074,7 +1089,7 @@ Show OpenBoard - + Pokaż OpenBoard @@ -1157,7 +1172,7 @@ Failed to import file ... - Importowanie pliku zakończone niepowodzeniem... + Importowanie pliku zakończone niepowodzeniem... Import all Images from Folder @@ -1210,26 +1225,30 @@ duplicated %1 page duplicated %1 pages - - - - + + zduplikowano %1 stronę + zduplikowano %1 strony + Zduplikowano %1 stron Remove Item - + Usuń element Are you sure you want to remove the selected item(s) ? - + Czy na pewno chcesz usunąć zaznaczone elementy? The document '%1' has been generated with a newer version of OpenBoard (%2). By opening it, you may lose some information. Do you want to proceed? - + Dokument '%1' został utworzony przy użyciu nowszej wersji OpenBoard (%2). Otwierając go możesz stracić niektóre informację. Czy chcesz kontynuować? Title page + Strona tytułowa + + + Refreshing Document Thumbnails View (%1/%2) @@ -1282,6 +1301,10 @@ Page %0 Strona %0 + + Generating thumbnails for board (%1/%2) + + UBDocumentPublisher @@ -1294,32 +1317,34 @@ UBDocumentReplaceDialog Accept - Akceptuj + Akceptuj Cancel - Anuluj + Anuluj Replace - + Zastąp The name %1 is allready used. Keeping this name will replace the document. Providing a new name will create a new document. - + Nazwa %1 jest już używana. +Pozostawienie tej nazwy zastąpi ten dokument. +Zmiana nazwy na nową utworzy nowy dokument. UBDocumentTreeModel Trash - Kosz + Kosz %1 pages copied - + Skopiowano %1 stronę Skopiowano %1 strony Skopiowano %1 stron @@ -1327,14 +1352,14 @@ Providing a new name will create a new document. My documents - + Moje dokumenty UBDocumentTreeView %1 pages copied - + Skopiowano %1 stronę Skopiowano %1 strony Skopiowano %1 stron @@ -1342,15 +1367,15 @@ Providing a new name will create a new document. Remove Item - + Usuń elementy Are you sure you want to remove the selected item(s) ? - + Czy na pewno chcesz usunąć zaznaczone elementy? Copying page %1/%2 - Kopiowanie strony %1/%2 + Kopiowanie strony %1/%2 @@ -1388,14 +1413,14 @@ Providing a new name will create a new document. UBDraggableThumbnail Page %0 - Strona %0 + Strona %0 UBDraggableThumbnailView Page %0 - Strona %0 + Strona %0 @@ -1406,23 +1431,23 @@ Providing a new name will create a new document. Exporting document... - Eksportowanie dokumentu... + Eksportowanie dokumentu... Export successful. - Eksportowanie zakończone powodzeniem. + Eksportowanie zakończone powodzeniem. Export failed - + Eksportowanie zakończone niepowodzeniem Unable to export to the selected location. You do not have the permissions necessary to save the file. - + Nie udało się wyeksportować do wybranej lokacji. Nie masz wystarczających uprawnień do zapisania pliku. Export failed: location not writable - + Eksportowanie nie powiodło się: niemożliwy zapis do lokacji @@ -1472,34 +1497,34 @@ Providing a new name will create a new document. Export to OpenBoard Format - + Wyeksportuj do formatu OpenBoard UBExportDocumentSetAdaptor Exporting document... - Eksportowanie dokumentu... + Eksportowanie dokumentu... Export successful. - Eksportowanie zakończone powodzeniem. + Eksportowanie zakończone powodzeniem. Export failed. - Eksportowanie zakończone niepowodzeniem. + Eksportowanie zakończone niepowodzeniem. Failed to export... - + Nie udało się wyeksportować... Export as UBX File - + Wyeksportuj jako plik UBX Export to OpenBoard UBX Format - + Wyeksportuj jako plik formatu OpenBoard UBX @@ -1691,7 +1716,7 @@ Providing a new name will create a new document. UBFeaturesProgressInfo Loading - Wczytywanie + Wczytywanie @@ -1721,26 +1746,26 @@ Providing a new name will create a new document. Set as background - Ustaw jako tło + Ustaw jako tło UBGraphicsMediaItem Media resource couldn't be resolved - + Nie udało się znaleźć zasobu multimedialnego Unsupported media format - + Niewspierany format zasobu multimedialnego Media playback service not found - + Nie znaleziono usługi odtwarzania Media error: - + Błąd multimediów: @@ -1783,7 +1808,7 @@ Providing a new name will create a new document. Transform as Tool - Przekształć jako narzędzie + Przekształć jako narzędzie @@ -1825,14 +1850,14 @@ Providing a new name will create a new document. OpenBoard (*.ubz) - + OpenBoard (*.ubz) UBImportDocumentSetAdaptor Openboard (set of documents) (*.ubx) - + Openboard (zbiór dokumentów) (*.ubx) @@ -1943,27 +1968,27 @@ Czy chcesz ignorować te błędy dla tego hosta? UBOpenSankoreImporterWidget Cancel - Anuluj + Anuluj Open-Sankore Documents Detected - + Wykryto dokumenty Open-Sankore Open-Sankoré documents are present on your computer. It is possible to import them to OpenBoard by pressing the “Proceed” button to launch the importer application. - + Dokumenty Open-Sankoré są dostępne na twoimkomputerze. Możliwy jest ich import do OpenBoard naciskając przycisk "Kontynuuj" by uruchomić aplikację importującą. Show this panel next time - + Pokaż ten panel następnym razem You can always access the OpenBoard Document Importer through the Preferences panel in the About tab. Warning, if you have already imported your Open-Sankore datas, you might loose your current OpenBoard documents. - + Zawsze możesz zyskać dostęp do aplikacji importującej OpenBoard poprzez panel Ustawień w karcie O programie. Uwaga: jeśli zaimportowałaś już dane Open-Sankore, możesz stracić swoje aktualne dokumenty OpenBoard. Proceed - + Kontynuuj @@ -1978,6 +2003,10 @@ Czy chcesz ignorować te błędy dla tego hosta? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + OpenBoard stracił dostęp do repozytorium dokumentów '%1'. Niestety aplikacja musi zostać zamknięta by uniknąć korupcji danych. Ostatnie zmiany również mogą zostać utracone. + + + Renaming pages (%1/%2) @@ -2064,7 +2093,7 @@ Czy chcesz ignorować te błędy dla tego hosta? OpenBoard Cast - + OpenBoard Cast @@ -2123,7 +2152,7 @@ Czy chcesz ignorować te błędy dla tego hosta? UBStartupHintsPalette Visible next time - + Pokaż następnym razem @@ -2143,12 +2172,16 @@ Czy chcesz ignorować te błędy dla tego hosta? %1 thumbnails generated ... Wygenerowano %1 miniatur... + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem Page %0 - Strona %0 + Strona %0 @@ -2183,7 +2216,7 @@ Czy chcesz ignorować te błędy dla tego hosta? Axes - + Osie @@ -2461,7 +2494,7 @@ Aby uzyskać dostęp do zaktualizowanych dokumentów, należy ponownie uruchomi WBTabBar New &Tab - Nowa zakładka + &Nowa zakładka Clone Tab @@ -2469,11 +2502,11 @@ Aby uzyskać dostęp do zaktualizowanych dokumentów, należy ponownie uruchomi &Close Tab - Zamknij zakładkę + &Zamknij kartę Close &Other Tabs - Zamknij inne zakładki + Zamknij &Inne karty Reload Tab @@ -2534,7 +2567,7 @@ Aby uzyskać dostęp do zaktualizowanych dokumentów, należy ponownie uruchomi Download PDF Document: would you prefer to download the PDF file or add it to the current OpenBoard document? - + Pobierz dokument PDF: chcesz pobrać plik PDF, czy dodać go do aktualnego dokumentu OpenBoard? @@ -2548,7 +2581,7 @@ Aby uzyskać dostęp do zaktualizowanych dokumentów, należy ponownie uruchomi XPDFRenderer Processing... - + Przetwarzanie... @@ -2591,19 +2624,15 @@ p, li { white-space: pre-wrap; } <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Lucida Grande'; font-size:10pt;">Klikając „Wyślij”, poświadczasz, że posiadasz wszelkie prawa do zawartości lub masz zezwolenie właściciela zawartości na jej publiczne udostępnianie w serwisie YouTube, a także, że zawartość jest zgodna z Warunkami korzystania z serwisu YouTube dostępnymi pod adresem </span><a href="http://www.youtube.com/t/terms"><span style=" font-family:'Lucida Grande'; font-size:10pt; text-decoration: underline; color:#0000ff;">http://www.youtube.com/t/terms</span></a></p></body></html> - +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Lucida Grande'; font-size:10pt;">Klikając „Wyślij”, poświadczasz, że posiadasz wszelkie prawa do zawartości lub masz zezwolenie właściciela zawartości na jej publiczne udostępnianie w serwisie YouTube, a także, że zawartość jest zgodna z Warunkami korzystania z serwisu YouTube dostępnymi pod adresem </span><a href="http://www.youtube.com/t/terms"><span style=" font-family:'Lucida Grande'; font-size:10pt; text-decoration: underline; color:#0000ff;">http://www.youtube.com/t/terms</span></a></p></body></html> OpenBoard - - OpenBoard - - + OpenBoard Restore credentials on reboot - + Przywróć dane logowania po restarcie @@ -2642,11 +2671,11 @@ p, li { white-space: pre-wrap; } Show preview circle from - + Pokaż podgląd pędzla od px - + px @@ -2699,23 +2728,23 @@ p, li { white-space: pre-wrap; } documents OpenBoard Documents - + Dokumenty OpenBoard Creation date - + Data utworzenia Update date - + Data modyfikacji Alphabetical order - + Porządek alfabetyczny Sort Order - + Porządek sortowania @@ -2738,7 +2767,7 @@ p, li { white-space: pre-wrap; } Display - Wyświetlaj + Wyświetlanie Internet @@ -2858,63 +2887,63 @@ p, li { white-space: pre-wrap; } On Dark Background - Na ciemnym tle + Na ciemnym tle Opacity - Nieprzezroczystość + Nieprzezroczystość On Light Background - Na jasnym tle + Na jasnym tle Swap first and second view displays - + Zamień wyświetlacze Built-in virtual keyboard button size: - + Rozmiar klawiszy wyświetlanej klawiatury: Use system keyboard (recommended) - + Użyj klawiatury systemowej (zalecane) Grid - + Kratka Open-Sankoré Importer - + Open-Sankoré Importer Check if Open-Sankoré data could be imported at launch - + Sprawdź czy dane Open-Sankoré mogą być zaimportowane przy starcie Documents Mode - + Tryb dokumentów Display date column on alphabetical sort - + Wyświetl kolumnę dat w porządku alfabetycznym Empty trash for documents older than - + Usuń dokumenty z kosza, które są starsze niż days - + dni PDF Rendering - + Renderowanie PDF Improve zoom execution time (can slightly affect rendering quality) - + Usprawnij wykonywanie przybliżania (może nieco pogorszyć jakość renderowania) @@ -2925,7 +2954,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - Wybierz Flash do złapania + Wybierz Flash do złapania about:blank @@ -2939,5 +2968,9 @@ p, li { white-space: pre-wrap; } Create Application Stwórz aplikację + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_pt.ts b/resources/i18n/OpenBoard_pt.ts index 8ba43ccf..c79bf7b2 100644 --- a/resources/i18n/OpenBoard_pt.ts +++ b/resources/i18n/OpenBoard_pt.ts @@ -698,11 +698,11 @@ Flash Trap - Captura de Flash + Captura de Flash Trap Flash Content - Captura de Conteúdo Flash + Captura de Conteúdo Flash Web Trap @@ -841,6 +841,10 @@ Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -898,6 +902,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? Tem a certeza que quer remover 1 página do documento selecionado '%0'? + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -909,6 +921,10 @@ Podcast Podcast + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -1236,6 +1252,10 @@ Title page + + Refreshing Document Thumbnails View (%1/%2) + + UBDocumentManager @@ -1286,6 +1306,10 @@ Page %0 Página %0 + + Generating thumbnails for board (%1/%2) + + UBDocumentReplaceDialog @@ -2002,6 +2026,10 @@ Quer ignorar estes erros, deste servidor? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -2158,6 +2186,10 @@ Quer ignorar estes erros, deste servidor? %1 thumbnails generated ... %1 de miniaturas geradas ... + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2940,7 +2972,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - Selecionar o flash a capturar + Selecionar o flash a capturar about:blank @@ -2954,5 +2986,9 @@ p, li { white-space: pre-wrap; } Create Application Criar Aplicação + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_pt_BR.ts b/resources/i18n/OpenBoard_pt_BR.ts index b5240dd5..342e27e7 100644 --- a/resources/i18n/OpenBoard_pt_BR.ts +++ b/resources/i18n/OpenBoard_pt_BR.ts @@ -1,1716 +1,1803 @@ - + BlackoutWidget Click to Return to Application - Clicar para voltar para a Aplicação + Clique para voltar à Aplicação DownloadDialog Downloads - Transferências + Transferências Clean Up - Limpar + Limpar 0 Items - 0 Itens + 0 Itens DownloadItem Form - Formulário + Formulário Filename - Nome do arquivo + Nome do arquivo Try Again - Tentar novamente + Tentar novamente Stop - Parar + Parar Open - Abrir + Abrir IntranetPodcastPublishingDialog Publish Podcast to YouTube - Publicar Podcast no YouTube + Publicar Podcast no YouTube Title - Título + Título Description - Descrição + Descrição Author - Autor + Autor MainWindow + + OpenBoard + OpenBoard + Board - Quadro + Quadro Web - Web + Web Documents - Documentos + Documentos Stylus - Paleta + Paleta Ctrl+T - Ctrl+T + Ctrl+T Backgrounds - Fundo + Fundo Change Background - Mudar Fundo + Mudar Fundo Undo - Desfazer + Desfazer Ctrl+Z - Ctrl+Z + Ctrl+Z Redo - Refazer + Refazer Ctrl+Y - Ctrl+Y + Ctrl+Y Previous - Anterior + Anterior Previous Page - Página anterior + Página anterior PgUp - Subir na página + Subir na página Next - Próximo + Próximo Next Page - Próxima Página + Próxima Página PgDown - Descer na página + Descer na página Manage Documents - Gerir Documentos + Gerir Documentos Ctrl+D - Ctrl+D + Ctrl+D Web Browsing - Navegar na Web + Navegar na Web Ctrl+W - Ctrl+W + Ctrl+W Line - Linha + Linha Small Line - Traço Fino + Traço Fino Medium Line - Traço Médio + Traço Médio Large Line - Traço Grosso + Traço Grosso Quit - Sair + Sair - Eraser - Borracha + Quit OpenBoard + Fechar OpenBoard - Smalle Eraser - Borracha Pequena + Eraser + Borracha Medium Eraser - Borracha Média + Borracha Média Large Eraser - Borracha Grande - - - Color - Cores + Borracha Grande Back - Recuar + Voltar Left - Esquerda + Esquerda Forward - Avançar + Avançar Right - Direita + Direita Reload - Recarregar + Recarregar Reload Current Page - Recarregar a página atual + Recarregar a página atual Home - Página Inicial + Página Inicial Load Home Page - Carregar a Página Inicial + Carregar a Página Inicial Bookmarks - Favoritos + Favoritos Show Bookmarks - Mostrar os Favoritos + Mostrar os Favoritos Bookmark - Favorito + Favorito Add Bookmark - Adicionar aos Favoritos + Adicionar aos Favoritos Display Board - Mostrar o Quadro + Mostrar o Quadro Ctrl+B - Ctrl+B + Ctrl+B Erase - Apagar + Apagar Erase Content - Apagar Conteúdo + Apagar Conteúdo Preferences - Preferências + Preferências Display Preferences - Mostrar Preferências + Mostrar Preferências Library - Biblioteca + Biblioteca Show Library - Mostrar Biblioteca + Mostrar Biblioteca Ctrl+L - Ctrl+L + Ctrl+L Show Desktop - Ver desktop + Área de Trabalho Show Computer Desktop - Mostrar o ambiente de trabalho do Computador + Mostrar o ambiente de trabalho do Computador Ctrl+Shift+H - Ctrl+Shift+H + Ctrl+Shift+H Bigger - Ampliar + Ampliar Zoom In - Aproximar + Ampliar Ctrl++ - Ctrl++ + Ctrl++ Smaller - Reduzir + Reduzir Zoom Out - Afastar + Afastar Ctrl+- - Ctrl+- + Ctrl+- New Folder - Nova Pasta + Nova Pasta Create a New Folder - Criar uma Nova Pasta + Criar uma Nova Pasta New Document - Novo Documento + Novo Documento Create a New Document - Criar um Novo Documento + Criar um Novo Documento Import - Importar + Importar Import a Document - Importar um Documento + Importar um Documento Export - Exportar + Exportar Export a Document - Exportar um Documento + Exportar um Documento Open in Board - Abrir no Quadro + Abrir no Quadro Open Page in Board - Abrir Página no Quadro + Abrir Página no Quadro Ctrl+O - Ctrl+O + Ctrl+O Duplicate - Duplicar + Duplicar Duplicate Selected Content - Duplicar o Conteúdo Selecionado + Duplicar o Conteúdo Selecionado Delete - Apagar + Apagar Delete Selected Content - Apagar o Conteúdo Selecionado + Apagar o Conteúdo Selecionado Del - Del + Del Add to Working Document - Adicionar ao Documento de Trabalho + Adicionar ao Documento de Trabalho Add Selected Content to Open Document - Adicionar o Conteúdo Selecionado ao Documento Aberto + Adicionar o Conteúdo Selecionado ao Documento Aberto Add - Adicionar + Adicionar Add Content to Document - Adicionar Conteúdo ao Documento + Adicionar Conteúdo ao Documento Rename - Mudar o Nome + Mudar o Nome Rename Content - Mudar o Nome do Conteúdo + Mudar o Nome do Conteúdo Tools - Ferramentas + Ferramentas Display Tools - Mostrar Ferramentas + Mostrar Ferramentas Multi Screen - Múltiplos Monitores + Múltiplos Monitores Wide Size (16/9) - Formato Panorâmico (16/9) + Formato Panorâmico (16/9) Use Document Wide Size (16/9) - Usar documento no Formato Panorâmico (16/9) + Usar documento no Formato Panorâmico (16/9) Regular Size (4/3) - Formato Tradicional (4/3) + Formato Tradicional (4/3) Use Document Regular Size (4/3) - Usar documento no Formato Tradicional (4/3) + Usar documento no Formato Tradicional (4/3) Custom Size - Tamanho Personalizado + Tamanho Personalizado Use Custom Document Size - Usar documento no Tamanho Personalizado + Usar documento no Tamanho Personalizado Stop Loading - Parar de Carregar + Parar de Carregar Stop Loading Web Page - Parar de Carregar a Página Web + Parar de Carregar a Página Web Cut - Cortar + Cortar Copy - Copiar + Copiar Paste - Colar + Colar Sleep - Modo "Espera" + Modo "Espera" Put Presentation to Sleep - Colocar a Apresentaçãao em modo "Espera" + Colocar a Apresentaçãao em modo "Espera" Virtual Keyboard - Teclado Virtual + Teclado Virtual Display Virtual Keyboard - Mostrar o Teclado Virtual + Mostrar o Teclado Virtual Plain Light Background - Fundo Branco e Liso + Fundo Branco e Liso Light - Claro + Claro Grid Light Background - Fundo Branco e Quadriculado + Fundo Claro Quadriculado + + + Ruled Light Background + Caderno com Fundo Claro Plain Dark Background - Fundo Preto e Liso + Fundo Escuro Quadriculado Dark - Escuro + Escuro Grid Dark Background - Fundo Preto e Quadriculado + Fundo Escuro Quadriculado + + + Ruled Dark Background + Caderno com Fundo Escuro Podcast - Podcast + Podcast Record Presentation to Video - Gravar a Apresentação em Vídeo + Gravar a Apresentação em Vídeo Record - Gravar + Gravar Start Screen Recording - Iniciar a Gravação do Vídeo + Iniciar a Gravação do Vídeo Erase Items - Apagar Itens + Apagar Itens Erase All Items - Apagar Todos os Itens + Apagar Todos os Itens Erase Annotations - Apagar Anotações + Apagar Anotações Erase All Annotations - Apagar Todas as Anotações + Apagar Todas as Anotações Clear Page - Limpar a Página + Limpar a Página Clear All Elements - Limpar Todos os Elementos + Limpar Todos os Elementos Pen - Caneta + Caneta Annotate Document - Escrever + Escrever Ctrl+I - Ctrl+I + Ctrl+I Erase Annotation - Apagar + Apagar Anotação Ctrl+E - Ctrl+E + Ctrl+E Marker - Marcador + Marca-Texto Highlight - Marcador + Realçar Ctrl+M - Ctrl+M + Ctrl+M Selector - Seletor + Seletor Select And Modify Objects - Selecionar e Modificar Objetos + Selecionar e Modificar Objetos Ctrl+F - Ctrl+F + Ctrl+F Hand - Mão + Mão Scroll Page - Desloca-se na página + Arrastar a página Laser Pointer - Apontador Laser + Apontador Laser Virtual Laser Pointer - Apontador Laser Virtual + Apontador Laser Virtual Ctrl+G - Ctrl+G + Ctrl+G Draw Lines - Desenhar Linhas + Desenhar Linhas Ctrl+J - Ctrl+J + Ctrl+J Text - Texto + Texto Write Text - Escrever Texto + Escrever Texto Ctrl+K - Ctrl+K + Ctrl+K Capture - Capturar + Capturar Capture Part of the Screen - Capturar uma Parte da Tela + Capturar uma Parte da Tela Add To Current Page - Adicionar à Página Atual + Adicionar à Página Atual Add Item To Current Page - Adicionar Item à Página Atual + Adicionar Item à Página Atual Add To New Page - Adicionar a uma Página Nova + Adicionar a uma Página Nova Add Item To New Page - Adicionar Item a uma Página Nova + Adicionar Item a uma Página Nova Add To Library - Adicionar à Biblioteca + Adicionar à Biblioteca Add Item To Library - Adicionar Item à Biblioteca + Adicionar Item à Biblioteca Pages - Páginas + Páginas Create a New Page - Criar uma Nova Página + Criar uma Nova Página New Page - Nova Página + Nova Página Duplicate Page - Duplicar Página + Duplicar Página Duplicate the Current Page - Duplicar a Página Atual + Duplicar a Página Atual Import Page - Importar Página + Importar Página Import an External Page - Importar uma Página Externa + Importar uma Página Externa Pause - Pausa + Pausa Pause Podcast Recording - Pausar a Gravação do Podcast + Pausar a Gravação do Podcast Podcast Config - Configuração do Podcast + Configuração do Podcast Configure Podcast Recording - Configuração da Gravação de Podcast + Configuração da Gravação de Podcast Flash Trap - Captura de Flash + Captura de Flash Trap Flash Content - Captura de Conteúdo Flash + Captura de Conteúdo Flash Web Trap - Captura de Web + Captura de Web Trap Web Content - Captura de Conteúdo Web + Captura de Conteúdo Web Custom Capture - Captura Personalizada + Captura Personalizada Window Capture - Captura da Janela + Captura da Janela Capture a Window - Capturar uma Janela + Capturar uma Janela Embed Web Content - Conteúdo Web Embutido + Conteúdo Web Embutido Capture Embeddable Web Content - Capturar Conteúdo Web Embutido + Capturar Conteúdo Web Embutido Show on Display - Mostrar no Vídeo + Mostrar no Vídeo Show Main Screen on Display Screen - Mostrar a Tela Principal no Vídeo + Mostrar a Tela Principal no Vídeo Erase all Annotations - Apagar todas as notas + Apagar todas as notas eduMedia - eduMedia + eduMedia Import eduMedia simulation - Importar uma simulação eduMedia + Importar uma simulação eduMedia Check Update - Verificar Atualização - - - Ctrl+H - Ctrl+H - - - OpenBoard - OpenBoard - - - Quit OpenBoard - Sair do OpenBoard + Verificar Atualização Hide OpenBoard - Ocultar o OpenBoard + Ocultar o OpenBoard Hide OpenBoard Application - Ocultar a Aplicação OpenBoard + Ocultar a Aplicação OpenBoard + + + Ctrl+H + Ctrl+H Play Começar(?) - Iniciar + Iniciar Interact with items - Interagir com itens + Interagir com itens Erase Background - Apagar o fundo + Apagar o fundo Remove the backgound - Remover o fundo + Remover o fundo Open Tutorial - Abrir Tutorial + Abrir Tutorial Open the tutorial web page - Abrir a página web de tutorial + Abrir tutorial na web + + + Reset grid size + Redefinir o tamanho da grade + + + Small Eraser + Borracha estreita + + + Color 1 + Cor 1 + + + Color 2 + Cor 2 + + + Color 3 + Cor 3 + + + Color 4 + Cor 4 + + + Color 5 + Cor 5 + + + Draw intermediate grid lines + Desenhar linhas intermediárias PasswordDialog Authentication Required - Autenticação Necessária + Autenticação Necessária Username: - Usuário: + Usuário: Password: - Senha: + Senha: ProxyDialog Proxy Authentication - Autenticação do Proxy + Autenticação do Proxy Connect to Proxy - Conetar ao Proxy + Conetar ao Proxy Username: - Nome de Utilizador: + Nome do Usuário: Password: - Senha: + Senha: Save username and password for future use - Gravar "Nome de Usuário" e "Senha" para futuras utilizações + Gravar "Nome de Usuário" e "Senha" para futuras utilizações QObject Element ID = - ID do Elemento + ID do Elemento Content is not supported in destination format. - O conteúdo não é suportado no formato de destino. + O conteúdo não é suportado no formato de destino. Remove Page - Remover Página + Remover Página Are you sure you want to remove 1 page from the selected document '%0'? - Tem a certeza que quer remover 1 página do documento selecionado '%0'? + Tem certeza que deseja remover 1 página do documento '%0' selecionado? + + + Common + Usual UBApplication Page Size - Tamanho da Página + Tamanho da Página Podcast - Podcast + Podcast UBApplicationController Web - Web + Web Update available - Atualização disponível + Atualização disponível New update available, would you go to the web page ? - Nova atualização disponível. Ir para a página web? + Atualização disponível: deseja ir até a página de download? + + + Update + Atualizar No update available - Nenhuma atualização disponível + Sem atualização disponível + + + UBBackgroundPalette - Update - Atualizar + Grid size + Tamanho da grade + + + Draw intermediate grid lines + Desenhar linhas de grade intermediárias UBBoardController - Downloading content %1 failed - A transferência de conteúdos falhou %1 + Group + Agrupar - Download finished - Transferência Completa + Ungroup + Desagrupar - Unknown tool type %1 - Tipo de ferramenta desconhecido %1 + Saving document... + Salvando documento... - Unknown content type %1 - Tipo de conteúdo desconhecido %1 + Document has just been saved... + O documento acabou de ser salvo... - Add Item - Adicionar Item + Deleting page %1 + Deletando página %1 - All Supported (%1) - Tudo Suportado (%1) + Page %1 deleted + Página %1 deletada - Delete page %1 from document - Apagar a página %1 do documento + Downloading content %1 failed + A transferência de conteúdos falhou %1 - Page %1 deleted - Página %1 apagada + Download finished + Transferência concluída Add file operation failed: file copying error - Falha na operação de adição de arquivo: erro na cópia do arquivo + Falha na operação de adição de arquivo: erro ao copiar arquivo - Group - Agrupar + Unknown tool type %1 + Tipo de ferramenta desconhecido %1 - Ungroup - Desagrupar + Unknown content type %1 + Tipo de conteúdo desconhecido %1 - Saving document... - Gravando documento... + Add Item + Adicionar Item - Document has just been saved... - Documento acaba de ser gravado... + All Supported (%1) + Tudo Suportado (%1) + + + Color + Cor UBBoardPaletteManager + + CapturedImage + Imagem capturada + Error Adding Image to Library - Erro ao Adicionar Imagem à Biblioteca + Erro ao Adicionar Imagem à Biblioteca + + + UBBoardThumbnailsView - CapturedImage - Imagem capturada + Loading page (%1/%2) + Carregando página (%1/%2) UBCachePropertiesWidget Cache Properties - Propriedades da Cache + Propriedades da Cache Color: - Cor: + Cor: Shape: - Forma: + Forma: Size: - Tamanho: + Tamanho: Close - Fechar + Fechar UBDesktopPalette + + Show OpenBoard + Mostrar OpenBoard + Capture Part of the Screen - Capturar Parte da Tela + Capturar Parte da Tela Capture the Screen - Capturar Tela + Capturar Tela Show the stylus palette - Mostrar Paleta + Mostrar Paleta Show Board on Secondary Screen - Mostrar o Quadro num Vídeo Secundário + Mostrar o Quadro numa Tela Secundária Show Desktop on Secondary Screen - Mostrar Ambiente de Trabalho num Vídeo Secundário - - - Show OpenBoard - Mostrar OpenBoard + Mostrar Área de Trabalho numa Tela Secundária UBDocumentController - New Folder - Nova Pasta + Trash + Lixeira - Page %1 - Página %1 + Untitled Documents + Documentos sem nome + + + New Folder + Nova Pasta Add Folder of Images - Adicionar Pasta de Imagens + Adicionar Pasta de Imagens Add Images - Adicionar Imagens + Adicionar Imagens Add Pages from File - Adicionar Páginas de um Arquivo + Adicionar Páginas de um Arquivo + + + duplicated %1 page + duplicated %1 pages + + %1 página duplicada + %1 páginas duplicadas + Duplicating Document %1 - A Duplicar Documento %1 + Copiando Documento %1 Document %1 copied - Documento %1 Copiado - - - Remove Page - Remover Página - - - Remove Document - Remover Documento - - - Are you sure you want to remove the document '%1'? - Tem a certeza que quer remover o documento '%1'? - - - Empty Trash - Vazar Lixo - - - Are you sure you want to empty trash? - Tem a certeza que quer vazar a pasta Lixo? + Documento %1 copiado - Emptying trash - A vazar Lixo + Remove Item + Remover Item - Emptied trash - Pasta Lixo limpa - - - Remove Folder - Apagar Pasta - - - Are you sure you want to remove the folder '%1' and all its content? - Tem a certeza que quer apagar a pasta '%1' e todo o seu conteúdo? - - - No document selected! - Não há documentos selecionados! + Are you sure you want to remove the selected item(s) ? + Tem certeza que deseja excluir os itens selecionados? Open Supported File - Abrir Arquivo Suportado + Abrir Arquivo Suportado Importing file %1... - Importando arquivo %1... + Importando arquivo %1... Failed to import file ... - Falha ao importar arquivo ... + Falha ao importar arquivo ... Import all Images from Folder - Importar todas as Imagens de uma Pasta - - - Delete - Apagar + Importar todas as Imagens de uma Pasta - Empty - Vazio + Folder does not contain any image files + A pasta não contém imagens - Trash - Lixo + Open Document + Abrir Documento - Open Document - Abrir Documento + The document '%1' has been generated with a newer version of OpenBoard (%2). By opening it, you may lose some information. Do you want to proceed? + O documento '%1' foi produzido com uma versão mais nova do OpenBoard (%2). Ao abri-lo, você poderá perder alguma informação. Gostaria de continuar? Add all Images to Document - Adicionar todas as imagens ao Documento + Adicionar todas as imagens ao Documento All Images (%1) - Todas as Imagens (%1) + Todas as Imagens (%1) Selection does not contain any image files! - A seleção não contém arquivos de Imagem! - - - Are you sure you want to remove %n page(s) from the selected document '%1'? - - Tem certeza que quer apagar %n página do documento selecionado '%1'? - Tem certeza que quer apagar %n páginas do documento selecionado '%1'? - - - - Folder does not contain any image files - O diretório não contém imagens + A seleção não contém arquivos de Imagem! - Untitled Documents - Documentos sem nome + Delete + Apagar - The document '%1' has been generated with a newer version of OpenBoard (%2). By opening it, you may lose some information. Do you want to proceed? - O documento '%1' foi produzido com uma versão mais nova do OpenBoard (%2). Ao abri-lo, você poderá perder alguma informação. Gostaria de continuar? + Page %1 + Página %1 - Are you sure you want to remove all selected documents? - Você tem certeza que deseja remover todos os documentos selecionados? + Title page + Título da página - Remove multiple documents - Remover múltiplos documentos + Empty + Vazio UBDocumentManager images - imagens + imagens videos - vídeos + vídeos objects - objetos + objetos widgets - widgets + widgets All supported files (*.%1) - Todos os arquivos suportados (*.%1) - - - File %1 saved - Arquivo %1 gravado + Todos os arquivos suportados (*.%1) Inserting page %1 of %2 - A inserir página %1 de %2 + A inserir página %1 de %2 Import successful. - Importação bem sucedida. + Importado com sucesso.. + + + Importing file %1 + Importando o arquivo %1 Import of file %1 successful. - Importação do arquivo %1 bem sucedida. + Importação do arquivo %1 bem sucedida. - Importing file %1 - Importando o arquivo %1 + File %1 saved + Arquivo %1 gravado UBDocumentNavigator Page %0 - Página %0 + Página %0 - UBDocumentTreeWidget + UBDocumentReplaceDialog - %1 (copy) - %1 (cópia) + Accept + Aceitar - Copying page %1/%2 - Copiando página %1/%2 + Replace + Substituir + + + Cancel + Cancelar + + + The name %1 is allready used. +Keeping this name will replace the document. +Providing a new name will create a new document. + O nome %1 já está em uso. +ATENÇÃO: Ao manter este nome, o documento antigo será sobrescrito pelo novo. +Um outro nome permitirá que você mantenha o documento antigo. + + + + UBDocumentTreeModel + + My documents + Meus documentos + + + Trash + Lixeira + + + %1 pages copied + + %1 página copiada + %1 páginas copiadas + + + + UBDocumentTreeView %1 pages copied - + %1 página copiada %1 páginas copiadas + + Remove Item + Remover itens + + + Are you sure you want to remove the selected item(s) ? + Tem certeza que deseja excluir os itens selecionados? + + + Copying page %1/%2 + Cópia da página %1/%2 em andamento + UBDownloadWidget Downloading files - Baixando arquivos + Transferindo arquivos Cancel - Cancelar + Cancelar - UBExportAdaptor + UBDraggableThumbnail - Warnings during export was appeared - Surgiram alertas durante a exportação + Page %0 + Página %0 - UBExportCFF + UBDraggableThumbnailView - Export to IWB - Exportar para IWB + Page %0 + Página %0 + + + UBExportAdaptor - Export as IWB File - Exportar como arquivo IWB + Exporting document... + A exportar documento... - Exporting document... - A exportar documento... + Export failed + Falha ao exportar + + + Unable to export to the selected location. You do not have the permissions necessary to save the file. + Impossível exportar para a localização selcionada. Você não possui as permissões necessárias para gravar o arquivo. + + + Export failed: location not writable + Exportação falhou: localização não gravável Export successful. - Exportação bem sucedida. + Exportado com sucesso. - Export failed. - Falha na Exportação. + Warnings during export was appeared + Surgiram alertas durante a exportação - UBExportDocument + UBExportCFF - Page - Página + Export to IWB + Exportar como IWB - Export as UBZ File - Exportar como Arquivo UBZ + Export as IWB File + Exportar como arquivo IWB Exporting document... - A exportar documento... + A exportar documento... Export successful. - Exportação bem sucedida. + Exportado com sucesso. - Exporting %1 %2 of %3 - A exportar %1 %2 de %3 + Export failed. + Falha na Exportação. + + + UBExportDocument - Export to OpenBoard Format - Exportar para o formato OpenBoard + Page + Página - Export failed: location not writable - Exportação falhou: localização não gravável + Export as UBZ File + Exportar como Arquivo UBZ - Export failed - Exportação falhou + Exporting %1 %2 of %3 + A exportar %1 %2 de %3 - Unable to export to the selected location. You do not have the permissions necessary to save the file. - Impossível exportar para a localização selcionada. Você não possui as permissões necessárias para gravar o arquivo. + Export to OpenBoard Format + Exportar para o formato OpenBoard - UBExportFullPDF + UBExportDocumentSetAdaptor - Export as PDF File - Exportar como Arquivo PDF + Failed to export... + Falha ao exportar... + + + Export as UBX File + Exportar para formato UBX (*.ubx) Exporting document... - A exportar documento... + Exportando documento... Export successful. - Exportação bem sucedida. + Exportado com sucesso. - Export to PDF - Exportar para PDF + Export failed. + Falha ao exportar. - Export failed: location not writable - Exportação falhou: localização não gravável + Export to OpenBoard UBX Format + Exportar para formato OpenBoard UBX + + + UBExportFullPDF - Export failed - Exportação falhou + Export as PDF File + Exportar como Arquivo PDF - Unable to export to the selected location. You do not have the permissions necessary to save the file. - Impossível exportar para a localização selecionada. Você não possui as permissões necessárias para gravar o arquivo. + Export to PDF + Exportar como PDF UBExportPDF Export as PDF File - Exportar como Arquivo PDF - - - Exporting document... - Exportando documento... - - - Export successful. - Exportação bem sucedida. + Exportar como Arquivo PDF Exporting page %1 of %2 - Exportando página %1 de %2 + Exportando página %1 de %2 Export to PDF - Exportar para PDF + Exportar como PDF UBExportWeb Page - Página + Página Export as Web data - Exportar para formato de Web + Exportar para formato de Web Exporting document... - Exportando documento... + A exportar documento... Export successful. - Exportação bem sucedida. + Exportado com sucesso. Export failed. - Falha na exportação. + Exportação falhou. Export to Web Browser - Exportar para Explorador Web + Exportar para Navegador Web UBFeatureProperties Add to page - Adicionar à página - - - Set as background - Definir como fundo + Adicionar à página Add to library - Adicionar à biblioteca + Adicionar à biblioteca Object informations - Informações do Objecto + Informações do Objeto UBFeaturesActionBar Add to favorites - Adicionar aos favoritos + Adicionar aos favoritos Share - Partilhar + Partilhar Search - Procurar + Procurar + + + Rescan file system + Verificar novamente o sistema de arquivos Delete - Apagar + Apagar Back to folder - Voltar à pasta + Voltar à pasta Remove from favorites - Remover dos favoritos + Remover dos favoritos Create new folder - Criar uma nova pasta - - - Rescan file system - "Procurar arquivo de Sistema"(?) - Voltar a procurar arquivo de Sistema + Criar uma nova pasta UBFeaturesController - - ImportedImage - Imagem Importada - Audios - Áudios + Áudios Movies - Vídeos + Vídeos Pictures - Imagens + Imagens Animations - Animações + Flash Interactivities - Atividades Interativas + Interagir Applications - Aplicações + Utilidades Shapes - Formas + Formas Favorites - Favoritos + Favoritos Web search - Busca na web + Busca Web Trash - Lixo + Lixeira + + + ImportedImage + Imagem Importada UBFeaturesNewFolderDialog Accept - Aceitar + Aceitar Cancel - Cancelar + Cancelar Enter a new folder name - Escrever um novo nome para o diretório + Dê um novo nome para o diretório UBFeaturesProgressInfo Loading - A carregar + A carregar UBGraphicsGroupContainerItemDelegate Locked - Bloqueado + Bloqueado Visible on Extended Screen - Visível em Vídeo Expandido + Visível na Tela Estendida UBGraphicsItemDelegate Locked - Bloqueado + Bloqueado Visible on Extended Screen - Visível em Vídeo Expandido + Visível na Tela Estendida + + + Set as background + Definir como fundo Go to Content Source - Ir para a Fonte do Conteúdo + Ir para a Fonte do Conteúdo UBGraphicsMediaItem Media resource couldn't be resolved - Recurso de mídia não pode ser resolvido + Recurso de mídia não pôde ser resolvido Unsupported media format - Formato de mídia não suportado + Formato de mídia não suportado Media playback service not found - Serviço de reprodução de mídia não encontrado + Serviço de reprodução de mídia não encontrado Media error: - Erro de mídia: + Erro na mídia: UBGraphicsTextItem <Type Text Here> - <Introduzir o Texto Aqui> + <Digite o Texto Aqui> UBGraphicsTextItemDelegate Text Color - Cor do Texto + Cor do Texto Editable - Editável + Editável UBGraphicsW3CWidgetItem Web - Web + Web UBGraphicsWidgetItem Loading ... - Carregando ... + Carregando ... UBGraphicsWidgetItemDelegate Frozen - Congelado + Congelado Transform as Tool - Transformar em Ferramenta + Transformar em Ferramenta UBImportCFF Common File Format ( - Formato de Arquivo Frequente ( + Formato de Arquivo Frequente ( Importing file %1... - Importando arquivos %1... + Importando arquivos %1... Import of file %1 failed. - Falha a importar arquivo %1. + Falha a importar arquivo %1. Import successful. - Importação bem sucedida. + Importado com sucesso.. Import failed. - Falha na importação. + Falha na importação. UBImportDocument + + OpenBoard (*.ubz) + OpenBoard (*.ubz) + Importing file %1... - Importando arquivo %1... + Importando arquivo %1... Import of file %1 failed. - Falha a importar arquivo %1. + Falha a importar arquivo %1. Import successful. - Importação bem sucedida. + Importado com sucesso. + + + UBImportDocumentSetAdaptor - OpenBoard (*.ubz) - OpenBoard (*.ubz) + Openboard (set of documents) (*.ubx) + Openboard (conjunto de documentos) (*.ubx) UBImportImage Image Format ( - Formato da Imagem ( + Formato da Imagem ( UBImportPDF Portable Document Format (*.pdf) - Portable Document Format (*.pdf) + Portable Document Format (*.pdf) PDF import failed. - Falha na importação de PDF. + Falha na importação de PDF. Importing page %1 of %2 - Importando página %1 de %2 + Importando página %1 de %2 UBIntranetPodcastPublisher Error while publishing video to intranet (%1) - Erro durante a publicação do vídeo na intranet (%1) + Erro durante a publicação do vídeo na intranet (%1) Publishing to Intranet in progress %1 % - Publicação na Intranet em progresso %1 % + Publicação na Intranet em progresso %1 % UBIntranetPodcastPublishingDialog Publish - Publicar + Publicar UBKeyboardPalette Enter - Enter + Enter UBMainWindow Yes - Sim + Sim No - Não + Não Ok - Ok + OK UBMessagesDialog Close - Fechar + Fechar UBNetworkAccessManager <qt>Enter username and password for "%1" at %2</qt> - <qt>Introduzir o Nome de Usuário e Senha "%1" em %2</qt> + <qt>Introduzir o Nome de Usuário e Senha "%1" em %2</qt> Failed to log to Proxy - Falha ao entrar no Proxy + Falha ao entrar no Proxy SSL Errors: @@ -1720,641 +1807,799 @@ %2 Do you want to ignore these errors for this host? - Erros SSL: + Erros SSL: %1 %2 -Quer ignorar estes erros, deste servidor? +Quer ignorar estes erros deste servidor? Yes - Sim + Sim No - Não + Não UBOpenSankoreImporterWidget Open-Sankore Documents Detected - Documentos do Open-Sankore Detectado + Documentos do Open-Sankore Detectados + + + Open-Sankoré documents are present on your computer. It is possible to import them to OpenBoard by pressing the “Proceed” button to launch the importer application. + Há documentos do Open-Sankoré em seu computador. É possível importá-los para o OpenBoard pressionando o botão "Prosseguir" para lançar o recurso de importação. Show this panel next time - Mostrar este painel da próxima vez + Mostrar este painel na próxima vez You can always access the OpenBoard Document Importer through the Preferences panel in the About tab. Warning, if you have already imported your Open-Sankore datas, you might loose your current OpenBoard documents. - Você sempre poderá acessar o Importador de Documentos OpenBoard através do painel de Preferências na aba Sobre. Aviso, se você já importou seus dados do Open-Sankoré, você poderá perder seus atuais documentos do OpenBoard. + Você sempre poderá acessar o Importador de Documentos OpenBoard através do painel de Preferências na aba Sobre. Aviso, se você já importou seus dados do Open-Sankoré, você poderá perder seus documentos atuais do OpenBoard. Cancel - Cancelar + Cancelar Proceed - Prosseguir - - - Open-Sankoré documents are present on your computer. It is possible to import them to OpenBoard by pressing the “Proceed” button to launch the importer application. - Documentos do Open-Sankoré estão presentes em seu computador. É possível importá-los para o OpenBoard pressionando o botão "Prosseguir" para lançar a aplicação de importação. + Prosseguir UBPersistenceManager (copy) - (cópia) + (cópia) Document Repository Loss - Repositório de Documentos perdido + Repositório de Documentos perdido - has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. - Acesso ao repositório '%1' foi perdido. Infelizmente a aplicação deverá desligar para avitar a corrupção de dados. As últimas alterações também serão perdidas. + OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + Acesso ao repositório '%1' foi perdido. Infelizmente, deverá encerrar a aplicação para evitar a corrupção de dados. As últimas alterações também serão perdidas. UBPlatformUtils English - Inglês + Inglês Russian - Russo + Russo German - Alemão + Alemão French - Francês + Francês Swiss French - Francês (Suiço) + Francês (Suíço) UBPodcastController + + OpenBoard Cast + OpenBoard Cast + Failed to start encoder ... - Falha ao iniciar o codificador ... + Falha ao iniciar o codificador... No Podcast encoder available ... - Nenhum codificador de Podcast disponível ... + Nenhum codificador de Podcast disponível ... Part %1 - Parte %1 + Parte %1 on your desktop ... - no seu ambiente de trabalho ... + no seu ambiente de trabalho ... in folder %1 - na pasta %1 + na pasta %1 Podcast created %1 - Podcast criado %1 + Podcast criado %1 Podcast recording error (%1) - Erro de gravação do Podcast (%1) + Erro ao gravar Podcast (%1) Default Audio Input - Entrada de áudio padrão + Entrada de áudio padrão No Audio Recording - Sem gravação de áudio + Sem gravação de áudio Small - Pequeno + Pequena Medium - Médio + Média Full - Completo + Cheia Publish to Intranet - Publicar na Intranet + Publicar na Intranet Publish to Youtube - Publicar no Youtube - - - OpenBoard Cast - OpenBoard Cast + Publicar no Youtube UBPreferencesController version: - versão: + versão: Marker is pressure sensitive - O marcador é sensível à pressão + Marca-texto sensível à pressão - - - UBProxyLoginDlg - Proxy Login - Iniciar sessão no Proxy + Key sequence already in use + Combinação de teclas já em uso - Username: - Nome de Utilizador: + Mouse button already in use + Botão do mouse já em uso - Password: - Senha: + Stylus button already in use + Botão de caneta já em uso + + + Accept + preferencesDialog + Aceitar + + + Record + preferencesDialog + Gravar - UBPublicationDlg + UBSettings - Publish document on the web - Publicar documentos na web + My Movies + Meus Vídeos + + + UBShortcutManager - Title: - Título: + Common + Frequente - Description: - Descrição: + Board + Quadro - Publish - Publicar + Stylus Palette + Paleta - - - UBSettings - My Movies - Os meus filmes + Lines and colours + Linhas e cores + + + Background + Fundo + + + Podcast + Podcast + + + First scene + Primeira cena + + + Show first scene + Ver primeira cena + + + Last scene + Última cena + + + Show last scene + Ver última cena + + + Zoom reset + Zoom 1:1 + + + Reset zoom factor + Redefinir fator de zoom + + + Scroll left + Rolar para a esquerda + + + Scroll page left + Rolar página para a esquerda + + + Scroll right + Rolar para a direita + + + Scroll page right + Rolar página para a direita + + + Scroll up + Rolar para cima + + + Scroll page up + Rolar página para cima + + + Scroll down + Rolar para baixo + + + Scroll page down + Rolar página para baixo + + + Built-in (not editable) + Integrado (não editável) + + + Command + Comando + + + Description + Descrição + + + Key Sequence + Sequência de Teclas + + + Mouse Button + Botão do Mouse + + + Tablet Button + Botão da Mesa Digitalizadora + + + Left + MouseButton + Esquerdo + + + Right + MouseButton + Direito + + + Middle + MouseButton + Meio + + + Back + MouseButton + Voltar + + + Forward + MouseButton + Avançar + + + Task + MouseButton + Tarefa + + + Extra + MouseButton + Extra UBStartupHintsPalette Visible next time - Visível na próxima vez + Visível na próxima vez + + + + UBTeacherBarWidget + + + UBThumbnailAdaptor Generating preview thumbnails ... - A gerar pré-visualização de miniaturas ... + Criando miniaturas de visualização... %1 thumbnails generated ... - %1 de miniaturas geradas ... + %1 miniaturas criadas... + + + + UBThumbnailTextItem + + Page %0 + Página %0 UBToolsManager Mask - Cortina + Cortina Ruler - Régua + Régua Compass - Compasso + Compasso Protractor - Transferidor + Transferidor Triangle - Esquadro + Esquadro Magnifier - Lupa + Lupa Cache - Cache + Cache + + + Axes + Eixos UBTrapFlashController Whole page - Página completa + Página completa Web - Web + Web UBUpdateDlg Document updater - Atualizador de documento + Atualizador de documento files require an update. - arquivos requerem uma atualização. + arquivos requerem uma atualização. Backup path: - Localização da Cópia de Segurança: + Localização da Cópia de Segurança: Browse - Procurar + Procurar Update - Atualizar + Atualizar + + + Remind me later + Lembrar-me depois Select a backup folder - Selecione uma pasta para a Cópia de Segurança + Selecione uma pasta para cópia de segurança Please wait the import process will start soon... - Aguarde. O processo de importação vai começar em breve... + Aguarde. O processo de importação iniciará em breve... Files update successful! Please reboot the application to access the updated documents. - Atualização de arquivos bem sucedida! + Atualização de arquivos bem sucedida! Por favor, reinicie o aplicativo para ir aos documentos atualizados. An error occured during the update. The files have not been affected. - Ocorreu um erro durante a atualização. Os arquivos não foram afetados. + Ocorreu um erro durante a atualização. Os arquivos não foram afetados. Files update results - Resultados da atualização de arquivos + Resultados da atualização de arquivos Updating file - Atualizando o arquivo - - - Remind me later - Lembrar-me depois + Atualizando o arquivo UBWebPluginWidget Loading... - Carregando... + Carregando... UBWidgetUniboardAPI %0 called (method=%1, status=%2) - %0 pedido (método=%1, estado=%2) + %0 pedido (método=%1, estado=%2) UBYouTubePublisher YouTube authentication failed. - Erro de autenticação no YouTube. + Erro de autenticação no YouTube. Error while uploading video to YouTube (%1) - Erro no envio do vídeo para o YouTube (%1) + Erro ao enviar o vídeo para o YouTube (%1) Upload to YouTube in progress %1 % - Envio para o YouTube em curso %1 % + Envio para o YouTube em curso %1 % UBYouTubePublishingDialog Upload - Enviar + Enviar Autos & Vehicles - Automóveis + Veículos Music - Música + Música Pets & Animals - Animais + Animais Sports - Esportes + Esportes Travel & Events - Viagens e Eventos + Viagens & Eventos Gaming - Jogos + Jogos Comedy - Humor + Comédia People & Blogs - Pessoas e Blogues + Pessoas & Blogues News & Politics - Notícias e Política + Notícias & Política Entertainment - Entretenimento + Entretenimento Education - Educação + Educação Howto & Style - Guias e Estilo + Tutoriais & Estilo Nonprofits & Activism - Sem fins lucrativos e Ativismo + ONGs & Ativismo Science & Technology - Ciência e Tecnologia + Ciência & Tecnologia UBZoomPalette %1 x - %1 x + %1 x WBClearButton Clear - Limpar + Limpar WBDownloadItem Save File - Gravar arquivo + Gravar arquivo Download canceled: %1 - Download cancelado: %1 + Transferência cancelada: %1 Error opening saved file: %1 - Erro a abrir o arquivo gravado: %1 + Erro ao abrir o arquivo gravado: %1 Error saving: %1 - Erro ao gravar: %1 + Erro ao gravar: %1 Network Error: %1 - Erro na Ligação: %1 + Erro na Ligação: %1 seconds - Segundos + segundos minutes - Minutos + minutos - %4 %5 remaining - - %4 %5 de tempo restante + - %4 %5 de tempo restante %1 of %2 (%3/sec) %4 - %1 de %2 (%3/seg) %4 + %1 de %2 (%3/s) %4 ? unknown file size Tamanho do arquivo desconhecido - ? + ? %1 of %2 - Stopped - %1 de %2 - Parado + %1 de %2 - Parado bytes - bytes + bytes KB - KB + KB MB - MB + MB WBDownloadManager 1 Download - 1 arquivo baixado + 1 arquivo transferido %1 Downloads always >= 2 - Sempre >= 2 - %1 arquivos baixados + %1 arquivos transferidos WBHistoryModel Title - Título + Título Address - Endereço + Endereço WBHistoryTreeModel Earlier Today - Hoje + Hoje %1 items - %1 item(s) + %1 itens WBSearchLineEdit Search - Pesquisar + Pesquisar WBTabBar New &Tab - Novo &Separador + Novo &Separador Clone Tab - Duplicar Separador + Duplicar Separador &Close Tab - &Fechar Separador + &Fechar Separador Close &Other Tabs - Fechar os &Outros Separadores + Fechar os &Outros Separadores Reload Tab - Atualizar Separador + Atualizar Separador Reload All Tabs - Atualizar todos os Separadores + Atualizar todos os Separadores WBTabWidget Recently Closed Tabs - Separadores recentemente fechados + Separadores recentemente fechados (Untitled) - (Sem Título) + (Sem Título) WBToolbarSearch Search - Pesquisar + Pesquisar No Recent Searches - Sem pesquisas recentes + Sem pesquisas recentes Recent Searches - Pesquisas recentes + Pesquisas recentes Clear Recent Searches - Limpar as pesquisas recentes + Limpar as pesquisas recentes WBWebPage + + Download PDF Document: would you prefer to download the PDF file or add it to the current OpenBoard document? + Download de Documento PDF: você prefere transferir o arquivo PDF ou adicioná-lo ao documento OpenBoard atual? + Download - Baixar + Transferir Add to Current Document - Adicionar ao documento atual + Adicionar ao documento atual PDF - PDF + PDF Error loading page: %1 - Erro ao carregar a página: %1 - - - Download PDF Document: would you prefer to download the PDF file or add it to the current OpenBoard document? - Download de Documento PDF: você prefere baixar o arquivo PDF ou adicioná-lo ao atual documento OpenBoard? + Erro ao carregar a página: %1 WBWebView Open in New Tab - Abrir num Novo Separador + Abrir num Novo Separador + + + + XPDFRenderer + + Processing... + Processando... YouTubePublishingDialog Publish Podcast to YouTube - Publicar Podcast no Youtube + Publicar Podcast no YouTube Title - Título + Título Description - Descrição + Descrição Keywords - Palavras chave + Tags (Palavras-Chave) + + + OpenBoard + OpenBoard Category - Categoria + Categoria YouTube Username - Nome de utilizador do Youtube + Nome de Usuário Youtube YouTube Password - Senha do Youtube + Senha no Youtube <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> @@ -2362,264 +2607,362 @@ Por favor, reinicie o aplicativo para ir aos documentos atualizados. - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Lucida Grande'; font-size:10pt;">Clicando em 'Enviar', você certifica que possui todos os direitos sobre o conteúdo ou que está autorizado pelo proprietário a tornar o conteúdo disponível publicamente no YouTube, e que esta de acordo com os Termos de Serviço do YouTube localizado em</span><a href="http://www.youtube.com/t/terms"><span style=" font-family:'Lucida Grande'; font-size:10pt; text-decoration: underline; color:#0000ff;">http://www.youtube.com/t/terms</span></a></p></body></html> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Lucida Grande'; font-size:10pt;">Clicando em 'Enviar', você certifica que possui todos os direitos sobre o conteúdo ou que está autorizado pelo proprietário a tornar o conteúdo disponível publicamente no YouTube, e que està de acordo com os Termos de Serviço do YouTube localizado em </span><a href="http://www.youtube.com/t/terms"><span style=" font-family:'Lucida Grande'; font-size:10pt; text-decoration: underline; color:#0000ff;">http://www.youtube.com/t/terms</span></a></p></body></html> Restore credentials on reboot - Reestabelecer credenciais ao reiniciar - - - OpenBoard - OpenBoard + Reestabelecer credenciais ao reiniciar brushProperties - On Light Background - Num fundo branco + Pen is Pressure Sensitive + Caneta sensível à pressão - On Dark Background - Num fundo preto + Opacity + Opacidade - Opacity - Opacidade + On Light Background + Em fundo claro + + + On Dark Background + Em fundo escuro Line Width - Largura da linha + Largura da Ponta Medium - Médio + Média Strong - Largo + Grossa Fine - Fino + Fina - Pen is Pressure Sensitive - Caneta sensível à pressão + Show preview circle from + Ver cruz a partir de + + + px + px capturePublishingDialog Dialog - Caixa de Diálogo + Caixa de Diálogo Title - Título + Título E-mail - E-mail + E-mail Author - Autor + Autor Description - Descrição + Descrição documents OpenBoard Documents - Documentos OpenBoard + Documentos OpenBoard + + + Creation date + Data de criação + + + Update date + Data de modificação + + + Alphabetical order + Ordem alfabética + + + Sort Order + Inverter Ordem preferencesDialog Preferences - Preferências - - - version : … - versão : ... + Preferências Default Settings - Configuração por defeito + Configurações Padrão Close - Fechar + Fechar Display - Exibir + Exibir - Internet - Internet - - - Show Page with External Browser - Mostrar a página num Browser externo + Multi display + Telas múltiplas - Home Page: - Página de entrada: + Swap control display and view display + Alternar entre telas de controle e de apresentação - Virtual Keyboard - Teclado virtual + Show internal web page content on secondary screen or projector + Mostrar o conteúdo interno da página web numa tela ou projetor secundário - Keyboard button size: - Tamanho dos botões do teclado: + Swap first and second view displays + Trocar as telas Toolbar - Barra de ferramentas + Barra de ferramentas Positioned at the Top (recommended for tablets) - Posicionado no topo (recomendado para tablets) + Posicionado no topo (recomendado para tablets ou mesas digitalizadoras) Positioned at the Bottom (recommended for white boards) - Posicionado em baixo (recomendado para quadros brancos) + Posicionado em baixo (recomendado para quadros brancos) Display Text Under Button - Mostrar o texto por debaixo dos botões + Mostrar legenda abaixo dos botões Stylus Palette - Paleta + Paleta Horizontal - Horizontal + Horizontal Vertical - Vertical + Vertical + + + Mode + Modo + + + Mode to start in: + Iniciar como + + + Board + Quadro + + + Desktop + Área de Trabalho + + + Virtual Keyboard + Teclado Virtual + + + Built-in virtual keyboard button size: + Tamanho das teclas do teclado virtual embarcado: + + + Use system keyboard (recommended) + Usar teclado do sistema (recomendado) + + + Grid + Grade + + + On Light Background + Em fundo claro + + + Opacity + Opacidade + + + On Dark Background + Em fundo escuro Pen - Caneta + Caneta Marker - Marcador + Marca-Texto + + + Network + Rede + + + Internet + Internet + + + Show Page with External Browser + Mostrar a página em navegador externo + + + Home Page: + Página Inicial: + + + Proxy User: + Usuário Proxy: + + + Pass: + Senha: Licences - Licenças + Licenças + + + Credits + Créditos About - Sobre + Sobre Software Update - Atualização de Software + Atualização de Software Check software update at launch - Verificar atualizações de software no arranque + Verificar atualizações ao iniciar - Network - Rede + Open-Sankoré Importer + Importador Open-Sankoré - Multi display - Múltiplos Monitores + Check if Open-Sankoré data could be imported at launch + Verificar se os dados do Open-Sankoré poderão ser importados ao inicializar - Show internal web page content on secondary screen or projector - Mostrar o conteúdo interno da página web num monitor ou projetor secundário + version : … + versão : … - Swap control display and view display - Trocar entre o monitor de controle e apresentação? - Trocar entre o monitor de controle e visualização + Documents Mode + Modo dos Documentos - Mode - Modo + Display date column on alphabetical sort + Mostrar coluna de data em ordem alfabética - Mode to start in: - "Mode to start in:" - Modo iniciar: + Empty trash for documents older than + Esvaziar lixeira para documentos com mais de - Board - Quadro + days + dias - Desktop - Área de Trabalho + PDF Rendering + Renderização de PDF - Proxy User: - Proxy User: + Shortcut + Atalho - Pass: - Senha: + Filter + Filtro - Credits - Créditos + Active keyboard shortcuts without pressing Ctrl key + Ativar atalhos de teclado sem pressionar a tecla Ctrl - Open-Sankoré Importer - Importador Open-Sankoré + Shortcuts + Atalhos - Check if Open-Sankoré data could be imported at launch - Verifique se os dados do Open-Sankoré poderão ser importados na inicialização + Abort + Abortar - Use system keyboard (recommended) - Usar teclado do sistema (recomendado) + Record + Gravar - Built-in virtual keyboard button size: - Tamanho da tecla do teclado virtual embarcado: + Stylus Button + Botão da Caneta + + + Mouse Button + Botão do Mouse + + + Reset + Reiniciar + + + Key Sequence + Sequência de Teclas + + + Improve zoom execution time (can slightly affect rendering quality) + Melhorar o tempo de execução do zoom (pode afetar ligeiramente a qualidade da renderização) trapFlashDialog Trap flash - Capturar flash + Capturar flash Select a flash to trap - Selecionar o flash a capturar + Selecionar o flash a capturar about:blank - Acerca:em branco + Sobre:em branco Application name - Nome da Aplicação + Nome da Aplicação Create Application - Criar Aplicação + Criar Aplicação diff --git a/resources/i18n/OpenBoard_ro.ts b/resources/i18n/OpenBoard_ro.ts index fc63c391..0863a9fc 100644 --- a/resources/i18n/OpenBoard_ro.ts +++ b/resources/i18n/OpenBoard_ro.ts @@ -697,11 +697,11 @@ Flash Trap - Blocare flash + Blocare flash Trap Flash Content - Blocare conţinut flash + Blocare conţinut flash Web Trap @@ -839,6 +839,10 @@ Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -896,6 +900,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? Sunteți sigur că doriți să eliminați 1 pagină din documentul selectat '%0'? + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -907,6 +919,10 @@ Podcast Podcast + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -1226,6 +1242,10 @@ Title page + + Refreshing Document Thumbnails View (%1/%2) + + UBDocumentManager @@ -1276,6 +1296,10 @@ Page %0 Pagina %0 + + Generating thumbnails for board (%1/%2) + + UBDocumentPublisher @@ -1970,6 +1994,10 @@ Doriţi să ignoraţi aceste erori pentru acest host? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -2133,6 +2161,10 @@ Doriţi să ignoraţi aceste erori pentru acest host? %1 thumbnails generated ... %1 miniaturi generate ... + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2911,7 +2943,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - Selectaţi un flash pentru blocare + Selectaţi un flash pentru blocare about:blank @@ -2925,5 +2957,9 @@ p, li { white-space: pre-wrap; } Create Application Creare aplicaţie + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_ru.ts b/resources/i18n/OpenBoard_ru.ts index ece64aea..fade77ea 100644 --- a/resources/i18n/OpenBoard_ru.ts +++ b/resources/i18n/OpenBoard_ru.ts @@ -697,11 +697,11 @@ Flash Trap - Захват флэш + Захват флэш Trap Flash Content - Захват флэш-содержимого + Захват флэш-содержимого Web Trap @@ -839,6 +839,10 @@ Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -896,6 +900,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? Вы уверены, что хотите удалить 1 страницу из документа '%0'? + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -907,6 +919,10 @@ Podcast Подкаст + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -1226,6 +1242,10 @@ Title page Заглавная страница + + Refreshing Document Thumbnails View (%1/%2) + + UBDocumentManager @@ -1276,6 +1296,10 @@ Page %0 Страница %0 + + Generating thumbnails for board (%1/%2) + + UBDocumentPublisher @@ -1972,6 +1996,10 @@ Do you want to ignore these errors for this host? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. Потерян доступ к репозиторию документов «%1». Приложение должно быть закрыто, чтобы избежать повреждения данных. Последние изменения также могут быть утеряны. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -2135,6 +2163,10 @@ Do you want to ignore these errors for this host? %1 thumbnails generated ... сгенерированы эскизы %1... + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2913,7 +2945,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - Выбрать флеш-содержимое для захвата + Выбрать флеш-содержимое для захвата about:blank @@ -2927,5 +2959,9 @@ p, li { white-space: pre-wrap; } Create Application Создать приложение + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_sk.ts b/resources/i18n/OpenBoard_sk.ts index c2abbc34..fa627759 100644 --- a/resources/i18n/OpenBoard_sk.ts +++ b/resources/i18n/OpenBoard_sk.ts @@ -351,7 +351,7 @@ Trap Flash Content - Označiť obsah vo Flashi + Označiť obsah vo Flashi Import @@ -539,7 +539,7 @@ Flash Trap - Označiť Flash + Označiť Flash Window Capture @@ -843,6 +843,10 @@ Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -900,6 +904,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? Určite chcete odstrániť 1 stránku z vybraného dokumentu '%0'? + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -911,6 +923,10 @@ Page Size Veľkosť stránky + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -1240,6 +1256,10 @@ Title page + + Refreshing Document Thumbnails View (%1/%2) + + UBDocumentManager @@ -1290,6 +1310,10 @@ Page %0 Stránka %0 + + Generating thumbnails for board (%1/%2) + + UBDocumentReplaceDialog @@ -2008,6 +2032,10 @@ Chcete ignorovať tieto chyby na tomto serveri? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -2173,6 +2201,10 @@ Chcete ignorovať tieto chyby na tomto serveri? Generating preview thumbnails ... Vytvárajú sa ukážky miniatúr... + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2952,7 +2984,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - Vyberte flash, ktorý chcete označiť + Vyberte flash, ktorý chcete označiť about:blank @@ -2966,5 +2998,9 @@ p, li { white-space: pre-wrap; } Create Application Vytvoriť aplikáciu + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_sv.ts b/resources/i18n/OpenBoard_sv.ts index eed47fcb..f00c99ff 100644 --- a/resources/i18n/OpenBoard_sv.ts +++ b/resources/i18n/OpenBoard_sv.ts @@ -697,11 +697,11 @@ Flash Trap - Lagra flash + Lagra flash Trap Flash Content - Lagra flash innehål + Lagra flash innehål Web Trap @@ -839,6 +839,10 @@ Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -896,6 +900,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? Är du säker på att du vill ta bort 1 sida från det valda dokumentet '%0'? + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -907,6 +919,10 @@ Podcast Podcast + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -1227,6 +1243,10 @@ Title page + + Refreshing Document Thumbnails View (%1/%2) + + UBDocumentManager @@ -1277,6 +1297,10 @@ Page %0 Sida %0 + + Generating thumbnails for board (%1/%2) + + UBDocumentPublisher @@ -1971,6 +1995,10 @@ Vill du ignorera felen för den här värden? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -2134,6 +2162,10 @@ Vill du ignorera felen för den här värden? %1 thumbnails generated ... %1 miniatybil genererad ... + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2912,7 +2944,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - Välj en flash att lagra + Välj en flash att lagra about:blank @@ -2926,5 +2958,9 @@ p, li { white-space: pre-wrap; } Create Application Skapa applikation + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_tr.ts b/resources/i18n/OpenBoard_tr.ts index efe8bbfb..d5ed2c55 100644 --- a/resources/i18n/OpenBoard_tr.ts +++ b/resources/i18n/OpenBoard_tr.ts @@ -701,11 +701,11 @@ Flash Trap - Flash Tuzağı + Flash Tuzağı Trap Flash Content - Flash İçeriğini Yakala + Flash İçeriğini Yakala Web Trap @@ -839,6 +839,10 @@ Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -896,6 +900,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? Seçili olan '%0' adlı dökümandan 1 sayfayı kaldırmak üzeresiniz. Bu işlemi yapmak istediğinizden eminmisiniz? + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -907,6 +919,10 @@ Podcast Ekran Kaydı + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -1224,6 +1240,10 @@ Title page + + Refreshing Document Thumbnails View (%1/%2) + + UBDocumentManager @@ -1274,6 +1294,10 @@ Page %0 Sayfa %0 + + Generating thumbnails for board (%1/%2) + + UBDocumentReplaceDialog @@ -1958,6 +1982,10 @@ Bu host için yukarıdaki hatalar yok sayılsın mı? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -2121,6 +2149,10 @@ Bu host için yukarıdaki hatalar yok sayılsın mı? %1 thumbnails generated ... %1 adet önizleme resmi oluşturuldu ... + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2899,7 +2931,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - Tuzağa almak için bir flash öğesi seçin + Tuzağa almak için bir flash öğesi seçin about:blank @@ -2913,5 +2945,9 @@ p, li { white-space: pre-wrap; } Create Application Uygulama Oluştur + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_uk.ts b/resources/i18n/OpenBoard_uk.ts index 8a9513e8..e39b6101 100644 --- a/resources/i18n/OpenBoard_uk.ts +++ b/resources/i18n/OpenBoard_uk.ts @@ -697,11 +697,11 @@ Flash Trap - Захоплення флеш + Захоплення флеш Trap Flash Content - Захоплення флеш-вмісту + Захоплення флеш-вмісту Web Trap @@ -839,6 +839,10 @@ Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -896,6 +900,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? Ви впевнені, що хочете видалити 1 сторінку з документа '%0'? + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -907,6 +919,10 @@ Podcast Подкаст + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -1234,6 +1250,10 @@ Title page + + Refreshing Document Thumbnails View (%1/%2) + + UBDocumentManager @@ -1284,6 +1304,10 @@ Page %0 Сторінка %0 + + Generating thumbnails for board (%1/%2) + + UBDocumentReplaceDialog @@ -1979,6 +2003,10 @@ Do you want to ignore these errors for this host? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -2142,6 +2170,10 @@ Do you want to ignore these errors for this host? %1 thumbnails generated ... згенеровані ескізи %1... + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2897,7 +2929,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - Вибрати флеш-вміст для захоплення + Вибрати флеш-вміст для захоплення about:blank @@ -2911,5 +2943,9 @@ p, li { white-space: pre-wrap; } Create Application Створити програму + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_zh.ts b/resources/i18n/OpenBoard_zh.ts index 2621530a..453b04db 100644 --- a/resources/i18n/OpenBoard_zh.ts +++ b/resources/i18n/OpenBoard_zh.ts @@ -697,11 +697,11 @@ Flash Trap - 截取动画 + 截取动画 Trap Flash Content - 截取动画内容 + 截取动画内容 Web Trap @@ -839,6 +839,10 @@ Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -896,6 +900,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? 确定要删除选中文件“%0”中的1页? + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -907,6 +919,10 @@ Podcast 播客 + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -1224,6 +1240,10 @@ Title page + + Refreshing Document Thumbnails View (%1/%2) + + UBDocumentManager @@ -1274,6 +1294,10 @@ Page %0 页面 %0 + + Generating thumbnails for board (%1/%2) + + UBDocumentReplaceDialog @@ -1958,6 +1982,10 @@ Do you want to ignore these errors for this host? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -2121,6 +2149,10 @@ Do you want to ignore these errors for this host? %1 thumbnails generated ... 已为%1生成缩略图…… + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2899,7 +2931,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - 选择要截取的动画 + 选择要截取的动画 about:blank @@ -2913,5 +2945,9 @@ p, li { white-space: pre-wrap; } Create Application 创建应用程序 + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_zh_CN.ts b/resources/i18n/OpenBoard_zh_CN.ts index 2621530a..453b04db 100644 --- a/resources/i18n/OpenBoard_zh_CN.ts +++ b/resources/i18n/OpenBoard_zh_CN.ts @@ -697,11 +697,11 @@ Flash Trap - 截取动画 + 截取动画 Trap Flash Content - 截取动画内容 + 截取动画内容 Web Trap @@ -839,6 +839,10 @@ Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -896,6 +900,14 @@ Are you sure you want to remove 1 page from the selected document '%0'? 确定要删除选中文件“%0”中的1页? + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + + UBApplication @@ -907,6 +919,10 @@ Podcast 播客 + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -1224,6 +1240,10 @@ Title page + + Refreshing Document Thumbnails View (%1/%2) + + UBDocumentManager @@ -1274,6 +1294,10 @@ Page %0 页面 %0 + + Generating thumbnails for board (%1/%2) + + UBDocumentReplaceDialog @@ -1958,6 +1982,10 @@ Do you want to ignore these errors for this host? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -2121,6 +2149,10 @@ Do you want to ignore these errors for this host? %1 thumbnails generated ... 已为%1生成缩略图…… + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem @@ -2899,7 +2931,7 @@ p, li { white-space: pre-wrap; } Select a flash to trap - 选择要截取的动画 + 选择要截取的动画 about:blank @@ -2913,5 +2945,9 @@ p, li { white-space: pre-wrap; } Create Application 创建应用程序 + + Select a content to capture + + diff --git a/resources/i18n/OpenBoard_zh_TW.ts b/resources/i18n/OpenBoard_zh_TW.ts index 859aece4..336adbca 100644 --- a/resources/i18n/OpenBoard_zh_TW.ts +++ b/resources/i18n/OpenBoard_zh_TW.ts @@ -50,7 +50,7 @@ IntranetPodcastPublishingDialog Publish Podcast to YouTube - 發佈Podcast至YouTube + 發佈 Podcast 至 YouTube Title @@ -333,7 +333,7 @@ Pen - 電子筆 + Annotate Document @@ -365,7 +365,7 @@ Selector - 選擇器 + 選擇工具 Select And Modify Objects @@ -413,7 +413,7 @@ Import eduMedia simulation - 匯入eduMedia模擬程序 + 匯入 eduMedia 模擬程序 Check Update @@ -673,19 +673,19 @@ Podcast Config - Podcast設定 + Podcast 設定 Configure Podcast Recording - Podcast錄製設定 + Podcast 錄製設定 Flash Trap - Flash動畫擷取 + Flash 動畫擷取 Trap Flash Content - 擷取Flash動畫內容 + 擷取 Flash 動畫內容 Web Trap @@ -745,11 +745,11 @@ Wide Size (16/9) - 寬螢幕顯示(16/9) + 寬螢幕顯示 (16/9) Regular Size (4/3) - 一般螢幕顯示(4/3) + 一般螢幕顯示 (4/3) Custom Size @@ -765,15 +765,15 @@ Quit OpenBoard - 退出OpenBoard + 退出 OpenBoard Hide OpenBoard - 隱藏OpenBoard + 隱藏 OpenBoard Hide OpenBoard Application - 隱藏OpenBoard應用程式 + 隱藏 OpenBoard 應用程式 Play @@ -801,44 +801,48 @@ Open Tutorial - + 開啟新手教學 Open the tutorial web page - + 打開教學網頁 Reset grid size - + 重設網格大小 Small Eraser - + 小橡皮擦 Color 1 - + 顏色 1 Color 2 - + 顏色 2 Color 3 - + 顏色 3 Color 4 - + 顏色 4 Color 5 - + 顏色 5 Draw intermediate grid lines + + Capture Web Content + + PasswordDialog @@ -848,30 +852,30 @@ Username: - 帳號: + 帳號: Password: - 密碼: + 密碼: ProxyDialog Proxy Authentication - Proxy驗證 + Proxy 驗證 Connect to Proxy - 連接至Proxy + 連接至 Proxy Username: - 帳號: + 帳號: Password: - 密碼: + 密碼: Save username and password for future use @@ -894,7 +898,15 @@ Are you sure you want to remove 1 page from the selected document '%0'? - 確定要移除所選文件 '%0' 的一個頁面 ? + 確定要移除所選文件 '%0' 的一個頁面? + + + Loading scene (%1/%2) + + + + Moving cached scenes (%1/%2) + @@ -907,6 +919,10 @@ Podcast Podcast + + Cannot open your UBX file directly. Please import it in Documents mode instead + + UBApplicationController @@ -916,7 +932,7 @@ New update available, would you go to the web page ? - 可更新,要上網頁嗎? + 有可用更新,要前往網頁下載嗎? No update available @@ -924,7 +940,7 @@ Update available - 目前可更新 + 有可用更新 Update @@ -935,7 +951,7 @@ UBBackgroundPalette Grid size - + 網格大小 Draw intermediate grid lines @@ -954,7 +970,7 @@ Unknown tool type %1 - 陌生工具型態 %1 + 未知工具類型 %1 Add Item @@ -962,11 +978,11 @@ All Supported (%1) - 全部已支援(%1) + 全部已支援 (%1) Unknown content type %1 - 陌生內容型態 %1 + 未知內容類型 %1 Delete page %1 from document @@ -978,7 +994,7 @@ Add file operation failed: file copying error - 新增檔案失敗:檔案複製有錯誤 + 新增檔案失敗:檔案複製錯誤 Group @@ -990,26 +1006,26 @@ Saving document... - + 正在儲存檔案... Document has just been saved... - + 檔案已被儲存... Deleting page %1 - + 正在刪除第 %1 頁 Color - 顏色 + 顏色 UBBoardPaletteManager Error Adding Image to Library - 錯誤新增圖像至圖書館 + 新增圖像至圖書館時發生錯誤 CapturedImage @@ -1020,26 +1036,26 @@ UBBoardThumbnailsView Loading page (%1/%2) - + 正在載入第 %1 頁,共 %2 頁 UBCachePropertiesWidget Cache Properties - Cache屬性 + Cache 屬性 Color: - 顏色: + 顏色: Shape: - 形狀: + 形狀: Size: - 大小: + 大小: Close @@ -1070,7 +1086,7 @@ Show OpenBoard - + 顯示 OpenBoard @@ -1085,7 +1101,7 @@ Add Folder of Images - 新增圖像檔案夾 + 新增圖像資料夾 Add Images @@ -1210,18 +1226,22 @@ Remove Item - + 刪除項目 Are you sure you want to remove the selected item(s) ? - + 您確定要刪除所選項目嗎? The document '%1' has been generated with a newer version of OpenBoard (%2). By opening it, you may lose some information. Do you want to proceed? - + 檔案 '%1' 是由新版的 OpenBoard (%2) 建立的。開啟後可能會遺失資料。您確定要繼續嗎? Title page + 標題頁 + + + Refreshing Document Thumbnails View (%1/%2) @@ -1274,6 +1294,10 @@ Page %0 第 %0 頁 + + Generating thumbnails for board (%1/%2) + + UBDocumentPublisher @@ -1286,15 +1310,15 @@ UBDocumentReplaceDialog Accept - 接受 + 接受 Cancel - 取消 + 取消 Replace - + 取代 The name %1 is allready used. @@ -1307,38 +1331,38 @@ Providing a new name will create a new document. UBDocumentTreeModel Trash - 回收桶 + 回收桶 %1 pages copied - + 已複製 %1 個頁面 My documents - + 我的檔案 UBDocumentTreeView %1 pages copied - + 已複製 %1 個頁面 Remove Item - + 刪除項目 Are you sure you want to remove the selected item(s) ? - + 您確定要刪除所選項目嗎? Copying page %1/%2 - 頁面 %1/%2 複製中 + 頁面 %1/%2 複製中 @@ -1391,38 +1415,38 @@ Providing a new name will create a new document. Exporting document... - + 匯出檔案中... Export failed - + 匯出失敗 Unable to export to the selected location. You do not have the permissions necessary to save the file. - + 無法匯出至指定位置。您沒有儲存檔案的權限。 Export failed: location not writable - + 匯出失敗:路徑無法被寫入 Export successful. - + 匯出成功。 UBExportCFF Export to IWB - 匯出至IWB + 匯出至 IWB Export as IWB File - 以IWB格式匯出 + 以 IWB 格式匯出 Exporting document... - 匯出文件... + 匯出檔案中... Export successful. @@ -1457,41 +1481,41 @@ Providing a new name will create a new document. Export to OpenBoard Format - + 以 OpenBoard 格式匯出 UBExportDocumentSetAdaptor Export failed. - 匯出失敗。 + 匯出失敗。 Failed to export... - + 匯出失敗... Export as UBX File - + 以 UBX 檔案匯出 Exporting document... - + 匯出檔案中... Export successful. - + 匯出成功。 Export to OpenBoard UBX Format - + 以 OpenBoard UBX 格式匯出 UBExportFullPDF Export as PDF File - 以PDF格式匯出 + 以 PDF 格式匯出 Exporting document... @@ -1499,7 +1523,7 @@ Providing a new name will create a new document. Export to PDF - 匯出成PDF + 匯出成 PDF Export successful. @@ -1510,7 +1534,7 @@ Providing a new name will create a new document. UBExportPDF Export as PDF File - 以PDF格式匯出 + 以 PDF 格式匯出 Exporting page %1 of %2 @@ -1526,7 +1550,7 @@ Providing a new name will create a new document. Export to PDF - 匯出成PDF + 匯出成 PDF @@ -1553,7 +1577,7 @@ Providing a new name will create a new document. Export to Web Browser - 匯出成網頁瀏覽 + 匯出至瀏覽器 @@ -1669,7 +1693,7 @@ Providing a new name will create a new document. Enter a new folder name - 鍵入檔案名稱 + 輸入檔案名稱 @@ -1706,7 +1730,7 @@ Providing a new name will create a new document. Set as background - 設定成背景 + 設定成背景 @@ -1717,7 +1741,7 @@ Providing a new name will create a new document. Unsupported media format - + 不支援的媒體格式 Media playback service not found @@ -1725,14 +1749,14 @@ Providing a new name will create a new document. Media error: - + 媒體錯誤: UBGraphicsTextItem <Type Text Here> - <此處鍵入文字> + <此處輸入文字> @@ -1810,7 +1834,7 @@ Providing a new name will create a new document. OpenBoard (*.ubz) - + OpenBoard (*.ubz) @@ -1824,18 +1848,18 @@ Providing a new name will create a new document. UBImportImage Image Format ( - 圖像格式( + 圖像格式 ( UBImportPDF Portable Document Format (*.pdf) - Portable Document Format (*.pdf) + 可攜式檔案格式 (*.pdf) PDF import failed. - PDF匯入失敗。 + PDF 匯入失敗。 Importing page %1 of %2 @@ -1864,7 +1888,7 @@ Providing a new name will create a new document. UBKeyboardPalette Enter - 鍵入 + 輸入 @@ -1897,7 +1921,7 @@ Providing a new name will create a new document. Failed to log to Proxy - 登入Proxy失敗 + 登入 Proxy 失敗 Yes @@ -1922,7 +1946,7 @@ Do you want to ignore these errors for this host? UBOpenSankoreImporterWidget Cancel - 取消 + 取消 Open-Sankore Documents Detected @@ -1942,7 +1966,7 @@ Do you want to ignore these errors for this host? Proceed - + 繼續 @@ -1959,6 +1983,10 @@ Do you want to ignore these errors for this host? OpenBoard has lost access to the document repository '%1'. Unfortunately the application must shut down to avoid data corruption. Latest changes may be lost as well. + + Renaming pages (%1/%2) + + UBPlatformUtils @@ -1980,22 +2008,22 @@ Do you want to ignore these errors for this host? Swiss French - 法文(瑞士) + 法文 (瑞士) UBPodcastController Failed to start encoder ... - 編碼器(encoder)啟動失敗... + 編碼器啟動失敗... No Podcast encoder available ... - 沒有可用的Podcast編碼器(encoder)... + 沒有可用的Podcast編碼器... Part %1 - Part %1 + 第 %1 部分 on your desktop ... @@ -2011,7 +2039,7 @@ Do you want to ignore these errors for this host? Podcast recording error (%1) - Podcast錄製有錯誤 (%1) + Podcast 錄製有錯誤 (%1) Default Audio Input @@ -2039,7 +2067,7 @@ Do you want to ignore these errors for this host? Publish to Youtube - 發佈至YouTube + 發佈至 YouTube OpenBoard Cast @@ -2050,7 +2078,7 @@ Do you want to ignore these errors for this host? UBPreferencesController version: - 版本: + 版本: Marker is pressure sensitive @@ -2061,15 +2089,15 @@ Do you want to ignore these errors for this host? UBProxyLoginDlg Proxy Login - Proxy登入 + Proxy 登入 Username: - 帳號: + 帳號: Password: - 密碼: + 密碼: @@ -2080,11 +2108,11 @@ Do you want to ignore these errors for this host? Title: - 標題: + 標題: Description: - 描述: + 描述: Publish @@ -2115,12 +2143,16 @@ Do you want to ignore these errors for this host? %1 thumbnails generated ... 已產生縮圖 %1... + + Loading thumbnail (%1/%2) + + UBThumbnailTextItem Page %0 - 第 %0 頁 + 第 %0 頁 @@ -2151,7 +2183,7 @@ Do you want to ignore these errors for this host? Cache - Cache + 快取 Axes @@ -2199,7 +2231,7 @@ Do you want to ignore these errors for this host? Files update successful! Please reboot the application to access the updated documents. 成功更新檔案! -請重新啟動程式使用該檔案。 +請重新啟動程式以使用該檔案。 An error occured during the update. The files have not been affected. @@ -2215,11 +2247,11 @@ Please reboot the application to access the updated documents. Please wait the import process will start soon... - 即將開始匯入,請稍待... + 即將開始匯入,請稍候... Remind me later - 稍候題提醒我 + 稍候提醒我 @@ -2240,15 +2272,15 @@ Please reboot the application to access the updated documents. UBYouTubePublisher YouTube authentication failed. - YouTube驗證失敗。 + YouTube 驗證失敗。 Error while uploading video to YouTube (%1) - 影片上傳至YouTube (%1)過程中有錯誤 + 影片上傳至 YouTube (%1)過程中有錯誤 Upload to YouTube in progress %1 % - 上傳至YouTube中 %1 % + 上傳至 YouTube 中 %1 % @@ -2385,7 +2417,7 @@ Please reboot the application to access the updated documents. ? unknown file size - ? + ? @@ -2520,14 +2552,14 @@ Please reboot the application to access the updated documents. XPDFRenderer Processing... - + 處理中... YouTubePublishingDialog Publish Podcast to YouTube - 發佈Podcast至YouTube + 發佈 Podcast 至 YouTube Title @@ -2547,11 +2579,11 @@ Please reboot the application to access the updated documents. YouTube Username - YouTube帳號 + YouTube 帳號 YouTube Password - YouTube密碼 + YouTube 密碼 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> @@ -2567,7 +2599,7 @@ p, li { white-space: pre-wrap; } OpenBoard - OpenBoard + OpenBoard Restore credentials on reboot @@ -2614,7 +2646,7 @@ p, li { white-space: pre-wrap; } px - + 像素 @@ -2671,19 +2703,19 @@ p, li { white-space: pre-wrap; } Creation date - + 建立日期 Update date - + 更新日期 Alphabetical order - + 按字母排序 Sort Order - + 排序方式 @@ -2750,7 +2782,7 @@ p, li { white-space: pre-wrap; } Check software update at launch - 啟用時檢查更新 + 啟動時檢查更新 Internet @@ -2758,7 +2790,7 @@ p, li { white-space: pre-wrap; } Home Page: - 主頁: + 主頁: Toolbar @@ -2774,7 +2806,7 @@ p, li { white-space: pre-wrap; } version : … - 版本: … + 版本: … Licences @@ -2802,7 +2834,7 @@ p, li { white-space: pre-wrap; } Mode to start in: - 啟動模式: + 啟動模式: Board @@ -2814,11 +2846,11 @@ p, li { white-space: pre-wrap; } Proxy User: - Proxy User: + Proxy 使用者: Pass: - 密碼: + 密碼: Credits @@ -2826,15 +2858,15 @@ p, li { white-space: pre-wrap; } On Dark Background - 深色背景 + 深色背景 Opacity - 透明度 + 透明度 On Light Background - 淡色背景 + 淡色背景 Swap first and second view displays @@ -2842,15 +2874,15 @@ p, li { white-space: pre-wrap; } Built-in virtual keyboard button size: - + 內建虛擬鍵盤按鍵大小: Use system keyboard (recommended) - + 使用系統鍵盤 (推薦) Grid - + 網格 Open-Sankoré Importer @@ -2874,7 +2906,7 @@ p, li { white-space: pre-wrap; } days - + PDF Rendering @@ -2889,11 +2921,11 @@ p, li { white-space: pre-wrap; } trapFlashDialog Trap flash - 擷取flash + 擷取 flash Select a flash to trap - 選擇要擷取的flash動畫 + 選擇要擷取的 flash 動畫 about:blank @@ -2907,5 +2939,9 @@ p, li { white-space: pre-wrap; } Create Application 建立應用程式 + + Select a content to capture + + diff --git a/resources/images/favorites.png b/resources/images/favorites.png index a3275659..30c8dd04 100644 Binary files a/resources/images/favorites.png and b/resources/images/favorites.png differ diff --git a/resources/images/folder.png b/resources/images/folder.png index cff0b7f4..1f307946 100644 Binary files a/resources/images/folder.png and b/resources/images/folder.png differ diff --git a/resources/images/libpalette/social.png b/resources/images/libpalette/social.png index 9f1c6dcb..1322f27c 100644 Binary files a/resources/images/libpalette/social.png and b/resources/images/libpalette/social.png differ diff --git a/resources/images/stylusPalette/eraserArrow.png b/resources/images/stylusPalette/eraserArrow.png index 24f4bf99..e1c81098 100644 Binary files a/resources/images/stylusPalette/eraserArrow.png and b/resources/images/stylusPalette/eraserArrow.png differ diff --git a/resources/images/stylusPalette/eraserOnArrow.png b/resources/images/stylusPalette/eraserOnArrow.png index ee4953cb..3d1ec0ea 100644 Binary files a/resources/images/stylusPalette/eraserOnArrow.png and b/resources/images/stylusPalette/eraserOnArrow.png differ diff --git a/resources/images/stylusPalette/markerArrow.png b/resources/images/stylusPalette/markerArrow.png index c8b621bc..ef1a2281 100644 Binary files a/resources/images/stylusPalette/markerArrow.png and b/resources/images/stylusPalette/markerArrow.png differ diff --git a/resources/images/stylusPalette/markerOnArrow.png b/resources/images/stylusPalette/markerOnArrow.png index 36d1d2a8..e200d020 100644 Binary files a/resources/images/stylusPalette/markerOnArrow.png and b/resources/images/stylusPalette/markerOnArrow.png differ diff --git a/resources/images/stylusPalette/penOnArrow.png b/resources/images/stylusPalette/penOnArrow.png index 23aa2f9b..674ac013 100644 Binary files a/resources/images/stylusPalette/penOnArrow.png and b/resources/images/stylusPalette/penOnArrow.png differ diff --git a/resources/images/toolPalette/axesTool.png b/resources/images/toolPalette/axesTool.png index 05008c8c..78be7b06 100644 Binary files a/resources/images/toolPalette/axesTool.png and b/resources/images/toolPalette/axesTool.png differ diff --git a/resources/images/tools.png b/resources/images/tools.png index 5f40247e..04009965 100644 Binary files a/resources/images/tools.png and b/resources/images/tools.png differ diff --git a/resources/images/trash-delete-document.png b/resources/images/trash-delete-document.png new file mode 100644 index 00000000..05f9c1e1 Binary files /dev/null and b/resources/images/trash-delete-document.png differ diff --git a/resources/images/trash-delete-folder.png b/resources/images/trash-delete-folder.png new file mode 100644 index 00000000..ae920eaf Binary files /dev/null and b/resources/images/trash-delete-folder.png differ diff --git a/resources/images/trash-document-page.png b/resources/images/trash-document-page.png new file mode 100644 index 00000000..19fc4089 Binary files /dev/null and b/resources/images/trash-document-page.png differ diff --git a/resources/images/trash-document.png b/resources/images/trash-document.png new file mode 100644 index 00000000..c3fda592 Binary files /dev/null and b/resources/images/trash-document.png differ diff --git a/resources/images/trash-empty.png b/resources/images/trash-empty.png new file mode 100644 index 00000000..94447b07 Binary files /dev/null and b/resources/images/trash-empty.png differ diff --git a/resources/images/trash-folder.png b/resources/images/trash-folder.png new file mode 100644 index 00000000..9cc89e97 Binary files /dev/null and b/resources/images/trash-folder.png differ diff --git a/resources/images/trash-my-documents.png b/resources/images/trash-my-documents.png new file mode 100644 index 00000000..a602798c Binary files /dev/null and b/resources/images/trash-my-documents.png differ diff --git a/resources/library/applications/WebBrowser.wgt/index.html b/resources/library/applications/WebBrowser.wgt/index.html index a1ca93a7..8c77b1fe 100644 --- a/resources/library/applications/WebBrowser.wgt/index.html +++ b/resources/library/applications/WebBrowser.wgt/index.html @@ -138,12 +138,41 @@ if($("#textbox").val().length > 0){ loadingState = false; var url = $("#textbox").val(); - var urlStart = url.split("://"); - - if(urlStart[0]!="http"){ - url = "http://" + url; - }; - + if(!url.startsWith("http")) + { + url = "https://" + url; + $("#textbox").val(url); + } + + //non-exhaustive. add new names here as it is requested + var x_frame_options_secured_sites = [ + "google.", + "youtube.", + "stackoverflow.", + "facebook.", + "github.", + "twitter.", + "amazon.", + "linkedin.", + "ebay." + ]; + + var iframe_denied = x_frame_options_secured_sites.some(function(element, indice, array) + { + return url.includes(element); + }); + + if (iframe_denied) + { + $("#container").addClass("error"); + $("#web-content").hide(); + $("#container-shadow").hide(); + $("#content").hide(); + $("#arrow").hide(); + $("#notifications").html(sankoreLang[lang].error_xframe_options); + return false; + } + if(checkURLs(references, url)){ if(currentHistory == references.length) references[currentHistory++] = url; @@ -158,7 +187,8 @@ $("#arrow").hide(); $("#embeded-content").hide(); $("#web-content").hide(); - $('#web-content').attr('src',url); + $('#web-content').contents().find("body").html(""); + $('#web-content').attr('src', url); checkcontent(); checkLoading(); @@ -225,11 +255,12 @@ }; if(resizerIndex > 80){ resizerIndex = 0; - console.log("error on loading page"); - $("#back-button").trigger("click"); - $("#textbox").val($("#textbox").val().replace("http://", "")); - $("#textbox").val("http://www.metacrawler.com/search/web?&q=" + $("#textbox").val().replace("http://", "")+"&ql="); - $("#search-button").trigger("click"); + $("#container").addClass("error"); + $("#web-content").hide(); + $("#container-shadow").hide(); + $("#content").hide(); + $("#arrow").hide(); + $("#notifications").html(sankoreLang[lang].error_loading_page); }else{ resizer = setTimeout(function(){checkLoading()}, 100); resizerIndex++; @@ -280,6 +311,7 @@
+
arrow-top
diff --git a/resources/library/applications/WebBrowser.wgt/scripts/languages.js b/resources/library/applications/WebBrowser.wgt/scripts/languages.js index 396dca88..6411fbee 100644 --- a/resources/library/applications/WebBrowser.wgt/scripts/languages.js +++ b/resources/library/applications/WebBrowser.wgt/scripts/languages.js @@ -6,7 +6,9 @@ "prev_page":"Previous page", "next_page":"Next page", "open":"Open the site", - "alert":"Cannot open a page! Maybe it's because of a security policy or a wrong url. Also check your internet connection." + "alert":"Cannot open a page! Maybe it's because of a security policy or a wrong url. Also check your internet connection.", + "error_loading_page": "An error has occured during page's loading", + "error_xframe_options" : "This site does not allow its content to be embed from another domain" }, "ru":{ "previous":"Пред.", @@ -15,7 +17,9 @@ "prev_page":"Пред. страница", "next_page":"След. страница", "open":"Перейти", - "alert":"Невозможно отобразить страницу! Возможно это из-за политики безопасности сайта или неверного адреса.Также стоит проверить подключение к интернету." + "alert":"Невозможно отобразить страницу! Возможно это из-за политики безопасности сайта или неверного адреса.Также стоит проверить подключение к интернету.", + "error_loading_page": "An error has occured during page's loading", + "error_xframe_options" : "This site does not allow its content to be embed from another domain" }, "fr":{ "previous":"Précédente", @@ -24,7 +28,9 @@ "prev_page":"Page précédente", "next_page":"Page suivante", "open":"Ouvrez le site", - "alert":"Impossible d'ouvrir une page! Peut-être c'est à cause d'une politique de sécurité ou une URL erronée. Vérifiez aussi votre connexion internet." + "alert":"Impossible d'ouvrir une page! Peut-être c'est à cause d'une politique de sécurité ou une URL erronée. Vérifiez aussi votre connexion internet.", + "error_loading_page": "Une erreur est survenue durant le chargement de la page", + "error_xframe_options" : "Ce site n'autorise pas l'intégration de son contenu à partir d'un autre domaine" }, "sk":{ "previous":"Predošlá", @@ -33,7 +39,9 @@ "prev_page":"Predošlá stránka", "next_page":"Ďalšia stránka", "open":"Otvoriť stránku", - "alert":"Stránka sa nedá otvoriť! Možno je to kvôli spôsobu zabezpečenia alebo nesprávnej internetovej adrese. Skontrolujte aj svoje internetové pripojenie." + "alert":"Stránka sa nedá otvoriť! Možno je to kvôli spôsobu zabezpečenia alebo nesprávnej internetovej adrese. Skontrolujte aj svoje internetové pripojenie.", + "error_loading_page": "An error has occured during page's loading", + "error_xframe_options" : "This site does not allow its content to be embed from another domain" } }; diff --git a/resources/linux/application-ubz.png b/resources/linux/application-ubz.png new file mode 100644 index 00000000..6eff7417 Binary files /dev/null and b/resources/linux/application-ubz.png differ diff --git a/resources/linux/application-ubz.svg b/resources/linux/application-ubz.svg new file mode 100644 index 00000000..0320a2c8 --- /dev/null +++ b/resources/linux/application-ubz.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/linux/run.sh b/resources/linux/run.sh index a179fb4b..c3b1fe85 100644 --- a/resources/linux/run.sh +++ b/resources/linux/run.sh @@ -25,4 +25,4 @@ DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" # Add custom libraries to LD_LIBRARY_PATH # TODO: Remomve the need for this -env LD_LIBRARY_PATH=$DIR/qtlib:$LD_LIBRARY_PATH QT_PLUGIN_PATH=$DIR/plugins $DIR/OpenBoard +env LD_LIBRARY_PATH=$DIR/qtlib:$LD_LIBRARY_PATH QT_PLUGIN_PATH=$DIR/plugins $DIR/OpenBoard "$@" diff --git a/resources/macx/Info.plist b/resources/macx/Info.plist index db9f96f2..5987de23 100644 --- a/resources/macx/Info.plist +++ b/resources/macx/Info.plist @@ -68,6 +68,8 @@ sparkle_public_key.pem NSMicrophoneUsageDescription Accès au microphone requis + NSAppleEventsUsageDescription + Accès au clavier virtuel requis UTExportedTypeDeclarations diff --git a/resources/style/treeview-branch-closed.png b/resources/style/treeview-branch-closed.png index be022111..2c566533 100644 Binary files a/resources/style/treeview-branch-closed.png and b/resources/style/treeview-branch-closed.png differ diff --git a/resources/style/treeview-branch-open.png b/resources/style/treeview-branch-open.png index 320b81e5..58fa3062 100644 Binary files a/resources/style/treeview-branch-open.png and b/resources/style/treeview-branch-open.png differ diff --git a/src/adaptors/UBExportPDF.cpp b/src/adaptors/UBExportPDF.cpp index 177f716d..683716bb 100644 --- a/src/adaptors/UBExportPDF.cpp +++ b/src/adaptors/UBExportPDF.cpp @@ -106,7 +106,17 @@ bool UBExportPDF::persistsDocument(UBDocumentProxy* pDocumentProxy, const QStrin // set background to white, no crossing for PDF output bool isDark = scene->isDarkBackground(); UBPageBackground pageBackground = scene->pageBackground(); - scene->setBackground(false, UBPageBackground::plain); + + bool exportDark = isDark && UBSettings::settings()->exportBackgroundColor->get().toBool(); + + if (UBSettings::settings()->exportBackgroundGrid->get().toBool()) + { + scene->setBackground(exportDark, pageBackground); + } + else + { + scene->setBackground(exportDark, UBPageBackground::plain); + } // pageSize is the output PDF page size; it is set to equal the scene's boundary size; if the contents // of the scene overflow from the boundaries, they will be scaled down. diff --git a/src/adaptors/UBMetadataDcSubsetAdaptor.cpp b/src/adaptors/UBMetadataDcSubsetAdaptor.cpp index 9cbe55eb..54ce6d51 100644 --- a/src/adaptors/UBMetadataDcSubsetAdaptor.cpp +++ b/src/adaptors/UBMetadataDcSubsetAdaptor.cpp @@ -125,8 +125,6 @@ void UBMetadataDcSubsetAdaptor::persist(UBDocumentProxy* proxy) // introduced in UB 4.4 xmlWriter.writeTextElement(UBSettings::uniboardDocumentNamespaceUri, "updated-at", UBStringUtils::toUtcIsoDateTime(QDateTime::currentDateTimeUtc())); - xmlWriter.writeTextElement(UBSettings::uniboardDocumentNamespaceUri, "page-count", QString::number(proxy->pageCount())); - xmlWriter.writeEndElement(); //dc:Description xmlWriter.writeEndElement(); //RDF @@ -226,11 +224,6 @@ QMap UBMetadataDcSubsetAdaptor::load(QString pPath) metadata.insert(UBSettings::documentUpdatedAt, xml.readElementText()); updatedAtFound = true; } - else if (xml.name() == "page-count" - && xml.namespaceUri() == UBSettings::uniboardDocumentNamespaceUri) - { - metadata.insert(UBSettings::documentPageCount, xml.readElementText()); - } metadata.insert(UBSettings::documentVersion, docVersion); } diff --git a/src/adaptors/UBSvgSubsetAdaptor.cpp b/src/adaptors/UBSvgSubsetAdaptor.cpp index 62a4e524..1fde7639 100644 --- a/src/adaptors/UBSvgSubsetAdaptor.cpp +++ b/src/adaptors/UBSvgSubsetAdaptor.cpp @@ -29,6 +29,7 @@ #include "UBSvgSubsetAdaptor.h" +#include #include #include #include @@ -239,6 +240,7 @@ QString UBSvgSubsetAdaptor::uniboardDocumentNamespaceUriFromVersion(int mFileVer UBGraphicsScene* UBSvgSubsetAdaptor::loadScene(UBDocumentProxy* proxy, const int pageIndex) { + UBApplication::showMessage(QObject::tr("Loading scene (%1/%2)").arg(pageIndex+1).arg(proxy->pageCount())); QString fileName = proxy->persistencePath() + UBFileSystemUtils::digitFileFormat("/page%1.svg", pageIndex); qDebug() << fileName; QFile file(fileName); @@ -355,7 +357,7 @@ UBGraphicsScene* UBSvgSubsetAdaptor::UBSvgSubsetReader::loadScene(UBDocumentProx time.start(); mScene = 0; UBGraphicsWidgetItem *currentWidget = 0; - bool pageDpiSpecified = true; + //bool pageDpiSpecified = true; saveSceneAfterLoading = false; mFileVersion = 40100; // default to 4.1.0 @@ -443,7 +445,7 @@ UBGraphicsScene* UBSvgSubsetAdaptor::UBSvgSubsetReader::loadScene(UBDocumentProx else if (proxy->pageDpi() == 0) { proxy->setPageDpi((UBApplication::desktop()->physicalDpiX() + UBApplication::desktop()->physicalDpiY())/2); - pageDpiSpecified = false; + //pageDpiSpecified = false; } bool darkBackground = false; @@ -909,9 +911,9 @@ UBGraphicsScene* UBSvgSubsetAdaptor::UBSvgSubsetReader::loadScene(UBDocumentProx if (textDelegate) { - QDesktopWidget* desktop = UBApplication::desktop(); - qreal currentDpi = (desktop->physicalDpiX() + desktop->physicalDpiY()) / 2; - qreal textSizeMultiplier = qreal(proxy->pageDpi())/currentDpi; + //QDesktopWidget* desktop = UBApplication::desktop(); + //qreal currentDpi = (desktop->physicalDpiX() + desktop->physicalDpiY()) / 2; + //qreal textSizeMultiplier = qreal(proxy->pageDpi())/currentDpi; //textDelegate->scaleTextSize(textSizeMultiplier); } @@ -2390,8 +2392,9 @@ void UBSvgSubsetAdaptor::UBSvgSubsetWriter::graphicsItemToSvg(QGraphicsItem* ite mXmlWriter.writeAttribute("x", "0"); mXmlWriter.writeAttribute("y", "0"); - mXmlWriter.writeAttribute("width", QString("%1").arg(item->boundingRect().width())); - mXmlWriter.writeAttribute("height", QString("%1").arg(item->boundingRect().height())); + QRectF rect = item->boundingRect() - QMarginsF(0.5, 0.5, 0.5, 0.5); + mXmlWriter.writeAttribute("width", QString("%1").arg(rect.width())); + mXmlWriter.writeAttribute("height", QString("%1").arg(rect.height())); mXmlWriter.writeAttribute("transform", toSvgTransform(item->sceneMatrix())); @@ -2487,8 +2490,9 @@ void UBSvgSubsetAdaptor::UBSvgSubsetWriter::graphicsWidgetToSvg(UBGraphicsWidget mXmlWriter.writeStartElement(nsXHtml, "iframe"); mXmlWriter.writeAttribute("style", "border: none"); - mXmlWriter.writeAttribute("width", QString("%1").arg(item->boundingRect().width())); - mXmlWriter.writeAttribute("height", QString("%1").arg(item->boundingRect().height())); + QRectF rect = item->boundingRect() - QMarginsF(0.5, 0.5, 0.5, 0.5); + mXmlWriter.writeAttribute("width", QString("%1").arg(rect.width())); + mXmlWriter.writeAttribute("height", QString("%1").arg(rect.height())); QString startFileUrl; if (item->mainHtmlFileName().startsWith("http://")) @@ -2817,10 +2821,11 @@ void UBSvgSubsetAdaptor::UBSvgSubsetWriter::curtainItemToSvg(UBGraphicsCurtainIt */ mXmlWriter.writeStartElement(UBSettings::uniboardDocumentNamespaceUri, "curtain"); - mXmlWriter.writeAttribute("x", QString("%1").arg(curtainItem->boundingRect().center().x())); - mXmlWriter.writeAttribute("y", QString("%1").arg(curtainItem->boundingRect().center().y())); - mXmlWriter.writeAttribute("width", QString("%1").arg(curtainItem->boundingRect().width())); - mXmlWriter.writeAttribute("height", QString("%1").arg(curtainItem->boundingRect().height())); + QRectF rect = curtainItem->boundingRect() - QMarginsF(0.5, 0.5, 0.5, 0.5); + mXmlWriter.writeAttribute("x", QString("%1").arg(rect.center().x())); + mXmlWriter.writeAttribute("y", QString("%1").arg(rect.center().y())); + mXmlWriter.writeAttribute("width", QString("%1").arg(rect.width())); + mXmlWriter.writeAttribute("height", QString("%1").arg(rect.height())); mXmlWriter.writeAttribute("transform", toSvgTransform(curtainItem->sceneMatrix())); //graphicsItemToSvg(curtainItem); @@ -2877,10 +2882,11 @@ void UBSvgSubsetAdaptor::UBSvgSubsetWriter::rulerToSvg(UBGraphicsRuler* item) */ mXmlWriter.writeStartElement(UBSettings::uniboardDocumentNamespaceUri, "ruler"); - mXmlWriter.writeAttribute("x", QString("%1").arg(item->boundingRect().x())); - mXmlWriter.writeAttribute("y", QString("%1").arg(item->boundingRect().y())); - mXmlWriter.writeAttribute("width", QString("%1").arg(item->boundingRect().width())); - mXmlWriter.writeAttribute("height", QString("%1").arg(item->boundingRect().height())); + QRectF rect = item->boundingRect() - QMarginsF(0.5, 0.5, 0.5, 0.5); + mXmlWriter.writeAttribute("x", QString("%1").arg(rect.x())); + mXmlWriter.writeAttribute("y", QString("%1").arg(rect.y())); + mXmlWriter.writeAttribute("width", QString("%1").arg(rect.width())); + mXmlWriter.writeAttribute("height", QString("%1").arg(rect.height())); mXmlWriter.writeAttribute("transform", toSvgTransform(item->sceneMatrix())); QString zs; @@ -3005,10 +3011,11 @@ void UBSvgSubsetAdaptor::UBSvgSubsetWriter::compassToSvg(UBGraphicsCompass* item */ mXmlWriter.writeStartElement(UBSettings::uniboardDocumentNamespaceUri, "compass"); - mXmlWriter.writeAttribute("x", QString("%1").arg(item->boundingRect().x())); - mXmlWriter.writeAttribute("y", QString("%1").arg(item->boundingRect().y())); - mXmlWriter.writeAttribute("width", QString("%1").arg(item->boundingRect().width())); - mXmlWriter.writeAttribute("height", QString("%1").arg(item->boundingRect().height())); + QRectF rect = item->boundingRect() - QMarginsF(0.5, 0.5, 0.5, 0.5); + mXmlWriter.writeAttribute("x", QString("%1").arg(rect.x())); + mXmlWriter.writeAttribute("y", QString("%1").arg(rect.y())); + mXmlWriter.writeAttribute("width", QString("%1").arg(rect.width())); + mXmlWriter.writeAttribute("height", QString("%1").arg(rect.height())); mXmlWriter.writeAttribute("transform", toSvgTransform(item->sceneMatrix())); QString zs; @@ -3139,10 +3146,11 @@ void UBSvgSubsetAdaptor::UBSvgSubsetWriter::triangleToSvg(UBGraphicsTriangle *it */ mXmlWriter.writeStartElement(UBSettings::uniboardDocumentNamespaceUri, "triangle"); - mXmlWriter.writeAttribute("x", QString("%1").arg(item->boundingRect().x())); - mXmlWriter.writeAttribute("y", QString("%1").arg(item->boundingRect().y())); - mXmlWriter.writeAttribute("width", QString("%1").arg(item->boundingRect().width())); - mXmlWriter.writeAttribute("height", QString("%1").arg(item->boundingRect().height())); + QRectF rect = item->boundingRect() - QMarginsF(0.5, 0.5, 0.5, 0.5); + mXmlWriter.writeAttribute("x", QString("%1").arg(rect.x())); + mXmlWriter.writeAttribute("y", QString("%1").arg(rect.y())); + mXmlWriter.writeAttribute("width", QString("%1").arg(rect.width())); + mXmlWriter.writeAttribute("height", QString("%1").arg(rect.height())); mXmlWriter.writeAttribute("transform", toSvgTransform(item->sceneMatrix())); mXmlWriter.writeAttribute("orientation", UBGraphicsTriangle::orientationToStr(item->getOrientation())); diff --git a/src/adaptors/UBThumbnailAdaptor.cpp b/src/adaptors/UBThumbnailAdaptor.cpp index b73f9e57..f01cc27b 100644 --- a/src/adaptors/UBThumbnailAdaptor.cpp +++ b/src/adaptors/UBThumbnailAdaptor.cpp @@ -83,8 +83,9 @@ void UBThumbnailAdaptor::generateMissingThumbnails(UBDocumentProxy* proxy) } } -const QPixmap* UBThumbnailAdaptor::get(UBDocumentProxy* proxy, int pageIndex) +QPixmap UBThumbnailAdaptor::get(UBDocumentProxy* proxy, int pageIndex) { + UBApplication::showMessage(tr("Loading thumbnail (%1/%2)").arg(pageIndex+1).arg(proxy->pageCount())); QString fileName = proxy->persistencePath() + UBFileSystemUtils::digitFileFormat("/page%1.thumbnail.jpg", pageIndex); QFile file(fileName); @@ -93,30 +94,21 @@ const QPixmap* UBThumbnailAdaptor::get(UBDocumentProxy* proxy, int pageIndex) generateMissingThumbnails(proxy); } - QPixmap* pix = new QPixmap(); + QPixmap pix; if (file.exists()) { - //Warning. Works only with modified Qt -#ifdef Q_OS_LINUX - pix->load(fileName, 0, Qt::AutoColor); -#else - pix->load(fileName, 0, Qt::AutoColor); -#endif + pix.load(fileName, 0, Qt::AutoColor); } return pix; } -void UBThumbnailAdaptor::load(UBDocumentProxy* proxy, QList& list) +void UBThumbnailAdaptor::load(UBDocumentProxy* proxy, QList>& list) { - generateMissingThumbnails(proxy); - - foreach(const QPixmap* pm, list){ - delete pm; - pm = NULL; - } list.clear(); for(int i=0; ipageCount(); i++) - list.append(get(proxy, i)); + { + list.append(std::make_shared(get(proxy, i))); + } } void UBThumbnailAdaptor::persistScene(UBDocumentProxy* proxy, UBGraphicsScene* pScene, int pageIndex, bool overrideModified) diff --git a/src/adaptors/UBThumbnailAdaptor.h b/src/adaptors/UBThumbnailAdaptor.h index 8c36b724..47f8be17 100644 --- a/src/adaptors/UBThumbnailAdaptor.h +++ b/src/adaptors/UBThumbnailAdaptor.h @@ -45,8 +45,8 @@ public: static void persistScene(UBDocumentProxy* proxy, UBGraphicsScene* pScene, int pageIndex, bool overrideModified = false); - static const QPixmap* get(UBDocumentProxy* proxy, int index); - static void load(UBDocumentProxy* proxy, QList& list); + static QPixmap get(UBDocumentProxy* proxy, int index); + static void load(UBDocumentProxy* proxy, QList>& list); private: static void generateMissingThumbnails(UBDocumentProxy* proxy); diff --git a/src/board/UBBoardController.cpp b/src/board/UBBoardController.cpp index 0ee35ae5..1a02262d 100644 --- a/src/board/UBBoardController.cpp +++ b/src/board/UBBoardController.cpp @@ -346,6 +346,8 @@ void UBBoardController::setupToolbar() , lineWidthChoice, SLOT(setCurrentIndex(int))); lineWidthChoice->displayText(QVariant(settings->appToolBarDisplayText->get().toBool())); + lineWidthChoice->setCurrentIndex(settings->penWidthIndex()); + lineWidthActions.at(settings->penWidthIndex())->setChecked(true); mMainWindow->boardToolBar->insertWidget(mMainWindow->actionBackgrounds, lineWidthChoice); @@ -367,6 +369,7 @@ void UBBoardController::setupToolbar() eraserWidthChoice->displayText(QVariant(settings->appToolBarDisplayText->get().toBool())); eraserWidthChoice->setCurrentIndex(settings->eraserWidthIndex()); + eraserWidthActions.at(settings->eraserWidthIndex())->setChecked(true); mMainWindow->boardToolBar->insertSeparator(mMainWindow->actionBackgrounds); @@ -521,6 +524,11 @@ void UBBoardController::addScene() persistCurrentScene(false,true); UBDocumentContainer::addPage(mActiveSceneIndex + 1); + if (UBApplication::documentController->selectedDocument() == selectedDocument()) + { + UBApplication::documentController->insertThumbPage(mActiveSceneIndex+1); + UBApplication::documentController->reloadThumbnails(); + } selectedDocument()->setMetaData(UBSettings::documentUpdatedAt, UBStringUtils::toUtcIsoDateTime(QDateTime::currentDateTime())); @@ -580,11 +588,13 @@ void UBBoardController::duplicateScene(int nIndex) QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); persistCurrentScene(false,true); - QList scIndexes; - scIndexes << nIndex; - duplicatePages(scIndexes); + duplicatePage(nIndex); insertThumbPage(nIndex); - emit documentThumbnailsUpdated(this); + if (UBApplication::documentController->selectedDocument() == selectedDocument()) + { + UBApplication::documentController->insertThumbPage(nIndex); + UBApplication::documentController->reloadThumbnails(); + } emit addThumbnailRequired(this, nIndex + 1); selectedDocument()->setMetaData(UBSettings::documentUpdatedAt, UBStringUtils::toUtcIsoDateTime(QDateTime::currentDateTime())); @@ -725,11 +735,19 @@ UBGraphicsItem *UBBoardController::duplicateItem(UBItem *item) case UBMimeType::UNKNOWN: { + QGraphicsItem *copiedItem = dynamic_cast(item); QGraphicsItem *gitem = dynamic_cast(item->deepCopy()); if (gitem) { mActiveScene->addItem(gitem); + if (copiedItem) + { + if (mActiveScene->tools().contains(copiedItem)) + { + mActiveScene->registerTool(gitem); + } + } gitem->setPos(itemPos); mLastCreatedItem = gitem; @@ -779,6 +797,10 @@ void UBBoardController::deleteScene(int nIndex) QList scIndexes; scIndexes << nIndex; deletePages(scIndexes); + if (UBApplication::documentController->selectedDocument() == selectedDocument()) + { + UBApplication::documentController->deleteThumbPage(nIndex); + } selectedDocument()->setMetaData(UBSettings::documentUpdatedAt, UBStringUtils::toUtcIsoDateTime(QDateTime::currentDateTime())); UBMetadataDcSubsetAdaptor::persist(selectedDocument()); @@ -865,7 +887,6 @@ void UBBoardController::showKeyboard(bool show) UBPlatformUtils::showOSK(show); else mPaletteManager->showVirtualKeyboard(show); - } @@ -984,7 +1005,7 @@ void UBBoardController::previousScene() persistViewPositionOnCurrentScene(); persistCurrentScene(); setActiveDocumentScene(mActiveSceneIndex - 1); - mControlView->centerOn(mActiveScene->lastCenter()); + centerOn(mActiveScene->lastCenter()); QApplication::restoreOverrideCursor(); } @@ -1000,7 +1021,7 @@ void UBBoardController::nextScene() persistViewPositionOnCurrentScene(); persistCurrentScene(); setActiveDocumentScene(mActiveSceneIndex + 1); - mControlView->centerOn(mActiveScene->lastCenter()); + centerOn(mActiveScene->lastCenter()); QApplication::restoreOverrideCursor(); } @@ -1016,7 +1037,7 @@ void UBBoardController::firstScene() persistViewPositionOnCurrentScene(); persistCurrentScene(); setActiveDocumentScene(0); - mControlView->centerOn(mActiveScene->lastCenter()); + centerOn(mActiveScene->lastCenter()); QApplication::restoreOverrideCursor(); } @@ -1032,7 +1053,7 @@ void UBBoardController::lastScene() persistViewPositionOnCurrentScene(); persistCurrentScene(); setActiveDocumentScene(selectedDocument()->pageCount() - 1); - mControlView->centerOn(mActiveScene->lastCenter()); + centerOn(mActiveScene->lastCenter()); QApplication::restoreOverrideCursor(); } @@ -1418,12 +1439,16 @@ UBItem *UBBoardController::downloadFinished(bool pSuccess, QUrl sourceUrl, QUrl QStringList fileNames; fileNames << pdfFile.fileName(); result = UBDocumentManager::documentManager()->addFilesToDocument(selectedDocument(), fileNames); - emit documentThumbnailsUpdated(this); + reloadThumbnails(); pdfFile.close(); } } - if (result){ + if (result) + { + if (UBApplication::documentController->selectedDocument() == selectedDocument()) + UBApplication::documentController->reloadThumbnails(); + selectedDocument()->setMetaData(UBSettings::documentUpdatedAt, UBStringUtils::toUtcIsoDateTime(QDateTime::currentDateTime())); } } @@ -1618,7 +1643,12 @@ void UBBoardController::moveSceneToIndex(int source, int target) { persistCurrentScene(false,true); - UBDocumentContainer::movePageToIndex(source, target); + UBPersistenceManager::persistenceManager()->moveSceneToIndex(selectedDocument(), source, target); + UBDocumentContainer::moveThumbPage(source, target); + if (UBApplication::documentController->selectedDocument() == selectedDocument()) + { + UBApplication::documentController->moveThumbPage(source, target); + } selectedDocument()->setMetaData(UBSettings::documentUpdatedAt, UBStringUtils::toUtcIsoDateTime(QDateTime::currentDateTime())); UBPersistenceManager::persistenceManager()->persistDocumentMetadata(selectedDocument()); @@ -1632,11 +1662,11 @@ void UBBoardController::moveSceneToIndex(int source, int target) } } -void UBBoardController::findUniquesItems(const QUndoCommand *parent, QSet &itms) +void UBBoardController::findUniquesItems(const QUndoCommand *parent, QSet &items) { if (parent->childCount()) { for (int i = 0; i < parent->childCount(); i++) { - findUniquesItems(parent->child(i), itms); + findUniquesItems(parent->child(i), items); } } @@ -1657,16 +1687,26 @@ void UBBoardController::findUniquesItems(const QUndoCommand *parent, QSetparentItem() && UBGraphicsGroupContainerItem::Type == item->parentItem()->type())) - itms.insert(item); + if (!items.contains(item) && + !(item->parentItem() && UBGraphicsGroupContainerItem::Type == item->parentItem()->type()) && + !items.contains(item->parentItem()) + ) + { + items.insert(item); + } } QSetIterator itRemoved(cmd->GetRemovedList()); while (itRemoved.hasNext()) { QGraphicsItem* item = itRemoved.next(); - if( !itms.contains(item) && !(item->parentItem() && UBGraphicsGroupContainerItem::Type == item->parentItem()->type())) - itms.insert(item); + if (!items.contains(item) && + !(item->parentItem() && UBGraphicsGroupContainerItem::Type == item->parentItem()->type()) && + !items.contains(item->parentItem()) + ) + { + items.insert(item); + } } } @@ -1867,7 +1907,7 @@ void UBBoardController::documentSceneChanged(UBDocumentProxy* pDocumentProxy, in if(selectedDocument() == pDocumentProxy) { setActiveDocumentScene(mActiveSceneIndex); - updatePage(pIndex); + updateThumbPage(pIndex); } } @@ -1906,7 +1946,12 @@ void UBBoardController::closing() mIsClosing = true; lastWindowClosed(); ClearUndoStack(); - showKeyboard(false); +#ifdef Q_OS_OSX + if (!UBPlatformUtils::errorOpeningVirtualKeyboard) + showKeyboard(false); +#else + showKeyboard(false); +#endif } void UBBoardController::lastWindowClosed() @@ -2010,8 +2055,12 @@ void UBBoardController::persistCurrentScene(bool isAnAutomaticBackup, bool force && (mActiveSceneIndex >= 0) && mActiveSceneIndex != mMovingSceneIndex && (mActiveScene->isModified())) { - UBPersistenceManager::persistenceManager()->persistDocumentScene(selectedDocument(), mActiveScene, mActiveSceneIndex); - updatePage(mActiveSceneIndex); + UBPersistenceManager::persistenceManager()->persistDocumentScene(selectedDocument(), mActiveScene, mActiveSceneIndex, isAnAutomaticBackup); + updateThumbPage(mActiveSceneIndex); + if (UBApplication::documentController->selectedDocument() == selectedDocument()) + { + UBApplication::documentController->updateThumbPage(mActiveSceneIndex); + } } } @@ -2142,7 +2191,14 @@ void UBBoardController::stylusToolChanged(int tool) if(eTool != UBStylusTool::Selector && eTool != UBStylusTool::Text) { if(mPaletteManager->mKeyboardPalette->m_isVisible) + { +#ifdef Q_OS_OSX + if (!UBPlatformUtils::errorOpeningVirtualKeyboard) + UBApplication::mainWindow->actionVirtualKeyboard->activate(QAction::Trigger); +#else UBApplication::mainWindow->actionVirtualKeyboard->activate(QAction::Trigger); +#endif + } } } diff --git a/src/board/UBBoardController.h b/src/board/UBBoardController.h index 360e2e71..2f95ed88 100644 --- a/src/board/UBBoardController.h +++ b/src/board/UBBoardController.h @@ -177,7 +177,7 @@ class UBBoardController : public UBDocumentContainer void notifyPageChanged(); void displayMetaData(QMap metadatas); - void findUniquesItems(const QUndoCommand *parent, QSet &itms); + void findUniquesItems(const QUndoCommand *parent, QSet &items); void ClearUndoStack(); void setActiveDocumentScene(UBDocumentProxy* pDocumentProxy, int pSceneIndex = 0, bool forceReload = false, bool onImport = false); diff --git a/src/board/UBBoardPaletteManager.cpp b/src/board/UBBoardPaletteManager.cpp index 720fbe9f..31605dbd 100644 --- a/src/board/UBBoardPaletteManager.cpp +++ b/src/board/UBBoardPaletteManager.cpp @@ -512,16 +512,18 @@ void UBBoardPaletteManager::containerResized() mKeyboardPalette->adjustSizeAndPosition(); } - if(mLeftPalette) +// NOTE @letsfindaway Fixed, but don't see any reason for this. +// Probably remove. + if(mLeftPalette && mLeftPalette->width() > 0) { mLeftPalette->resize(mLeftPalette->width()-1, mContainer->height()); - mLeftPalette->resize(mLeftPalette->width(), mContainer->height()); + mLeftPalette->resize(mLeftPalette->width()+1, mContainer->height()); } - if(mRightPalette) + if(mRightPalette && mRightPalette->width() > 0) { mRightPalette->resize(mRightPalette->width()-1, mContainer->height()); - mRightPalette->resize(mRightPalette->width(), mContainer->height()); + mRightPalette->resize(mRightPalette->width()+1, mContainer->height()); } } diff --git a/src/board/UBBoardView.cpp b/src/board/UBBoardView.cpp index 5ba68364..f60cbd19 100644 --- a/src/board/UBBoardView.cpp +++ b/src/board/UBBoardView.cpp @@ -486,8 +486,14 @@ void UBBoardView::handleItemsSelection(QGraphicsItem *item) if (item) { // item has group as first parent - it is any item or UBGraphicsStrokesGroup. - if(item->parentItem() && UBGraphicsGroupContainerItem::Type == getMovingItem()->parentItem()->type()) - return; + if (getMovingItem()) + { + if (getMovingItem()->parentItem()) + { + if(item->parentItem() && UBGraphicsGroupContainerItem::Type == getMovingItem()->parentItem()->type()) + return; + } + } // delegate buttons shouldn't selected if (DelegateButton::Type == item->type()) @@ -510,6 +516,7 @@ void UBBoardView::handleItemsSelection(QGraphicsItem *item) if ((UBGraphicsItemType::UserTypesCount > item->type()) && (item->type() > QGraphicsItem::UserType)) { scene()->deselectAllItemsExcept(item); + scene()->updateSelectionFrame(); } } } @@ -653,11 +660,17 @@ bool UBBoardView::itemShouldBeMoved(QGraphicsItem *item) if (!(mMouseButtonIsPressed || mTabletStylusIsPressed)) return false; - if (getMovingItem()->data(UBGraphicsItemData::ItemLocked).toBool()) - return false; + if (getMovingItem()) + { + if (getMovingItem()->data(UBGraphicsItemData::ItemLocked).toBool()) + return false; - if (getMovingItem()->parentItem() && UBGraphicsGroupContainerItem::Type == getMovingItem()->parentItem()->type() && !getMovingItem()->isSelected() && getMovingItem()->parentItem()->isSelected()) - return false; + if (getMovingItem()->parentItem()) + { + if (UBGraphicsGroupContainerItem::Type == getMovingItem()->parentItem()->type() && !getMovingItem()->isSelected() && getMovingItem()->parentItem()->isSelected()) + return false; + } + } UBStylusTool::Enum currentTool = (UBStylusTool::Enum)UBDrawingController::drawingController()->stylusTool(); @@ -684,7 +697,6 @@ bool UBBoardView::itemShouldBeMoved(QGraphicsItem *item) case UBGraphicsAudioItem::Type: return true; case UBGraphicsStrokesGroup::Type: - return false; case UBGraphicsTextItem::Type: if (currentTool == UBStylusTool::Play) return true; @@ -831,20 +843,29 @@ void UBBoardView::handleItemMouseMove(QMouseEvent *event) QPointF posAfterMove; if (getMovingItem()) + { posBeforeMove = getMovingItem()->pos(); - - QGraphicsView::mouseMoveEvent (event); - - if (getMovingItem()) + QGraphicsView::mouseMoveEvent (event); posAfterMove = getMovingItem()->pos(); + } + else + { + if (!mMouseButtonIsPressed) + { + QGraphicsView::mouseMoveEvent(event); + } + } mWidgetMoved = ((posAfterMove-posBeforeMove).manhattanLength() != 0); // a cludge for terminate moving of w3c widgets. // in some cases w3c widgets catches mouse move and doesn't sends that events to web page, // at simple - in google map widget - mouse move events doesn't comes to web page from rectangle of wearch bar on bottom right corner of widget. - if (getMovingItem() && mWidgetMoved && UBGraphicsW3CWidgetItem::Type == getMovingItem()->type()) - getMovingItem()->setPos(posBeforeMove); + if (getMovingItem()) + { + if (mWidgetMoved && UBGraphicsW3CWidgetItem::Type == getMovingItem()->type()) + getMovingItem()->setPos(posBeforeMove); + } } } @@ -1270,51 +1291,58 @@ void UBBoardView::mouseReleaseEvent (QMouseEvent *event) setMovingItem(nullptr); } else - if (getMovingItem() && (!isCppTool(getMovingItem()) || UBGraphicsCurtainItem::Type == getMovingItem()->type())) + { + if (getMovingItem()) { - if (suspendedMousePressEvent) - { - QGraphicsView::mousePressEvent(suspendedMousePressEvent); // suspendedMousePressEvent is deleted by old Qt event loop - setMovingItem(NULL); - delete suspendedMousePressEvent; - suspendedMousePressEvent = NULL; - bReleaseIsNeed = true; - } - else + if (!isCppTool(getMovingItem()) || UBGraphicsCurtainItem::Type == getMovingItem()->type()) { - if (isUBItem(getMovingItem()) && - DelegateButton::Type != getMovingItem()->type() && - UBGraphicsDelegateFrame::Type != getMovingItem()->type() && - UBGraphicsCache::Type != getMovingItem()->type() && - QGraphicsWebView::Type != getMovingItem()->type() && // for W3C widgets as Tools. - !(!isMultipleSelectionEnabled() && getMovingItem()->parentItem() && UBGraphicsWidgetItem::Type == getMovingItem()->type() && UBGraphicsGroupContainerItem::Type == getMovingItem()->parentItem()->type())) + if (suspendedMousePressEvent) { - bReleaseIsNeed = false; - if (getMovingItem()->isSelected() && isMultipleSelectionEnabled()) - getMovingItem()->setSelected(false); - else - if (getMovingItem()->parentItem() && getMovingItem()->parentItem()->isSelected() && isMultipleSelectionEnabled()) - getMovingItem()->parentItem()->setSelected(false); + QGraphicsView::mousePressEvent(suspendedMousePressEvent); // suspendedMousePressEvent is deleted by old Qt event loop + setMovingItem(NULL); + delete suspendedMousePressEvent; + suspendedMousePressEvent = NULL; + bReleaseIsNeed = true; + } + else + { + if (isUBItem(getMovingItem()) && + DelegateButton::Type != getMovingItem()->type() && + UBGraphicsDelegateFrame::Type != getMovingItem()->type() && + UBGraphicsCache::Type != getMovingItem()->type() && + QGraphicsWebView::Type != getMovingItem()->type() && // for W3C widgets as Tools. + !(!isMultipleSelectionEnabled() && getMovingItem()->parentItem() && UBGraphicsWidgetItem::Type == getMovingItem()->type() && UBGraphicsGroupContainerItem::Type == getMovingItem()->parentItem()->type())) + { + bReleaseIsNeed = false; + if (getMovingItem()->isSelected() && isMultipleSelectionEnabled()) + getMovingItem()->setSelected(false); else - { - if (getMovingItem()->isSelected()) - bReleaseIsNeed = true; - - UBGraphicsTextItem* textItem = dynamic_cast(getMovingItem()); - UBGraphicsMediaItem* movieItem = dynamic_cast(getMovingItem()); - if(textItem) - textItem->setSelected(true); - else if(movieItem) - movieItem->setSelected(true); + if (getMovingItem()->parentItem() && getMovingItem()->parentItem()->isSelected() && isMultipleSelectionEnabled()) + getMovingItem()->parentItem()->setSelected(false); else - getMovingItem()->setSelected(true); - } + { + if (getMovingItem()->isSelected()) + bReleaseIsNeed = true; + + UBGraphicsTextItem* textItem = dynamic_cast(getMovingItem()); + UBGraphicsMediaItem* movieItem = dynamic_cast(getMovingItem()); + if(textItem) + textItem->setSelected(true); + else if(movieItem) + movieItem->setSelected(true); + else + getMovingItem()->setSelected(true); + } + } } } + else + bReleaseIsNeed = true; } else bReleaseIsNeed = true; + } if (bReleaseIsNeed) { @@ -1402,9 +1430,13 @@ void UBBoardView::mouseReleaseEvent (QMouseEvent *event) return; } - if (mWidgetMoved) { - getMovingItem()->setSelected(false); - setMovingItem(NULL); + if (mWidgetMoved) + { + if (getMovingItem()) + { + getMovingItem()->setSelected(false); + setMovingItem(NULL); + } mWidgetMoved = false; } else { diff --git a/src/core/UBApplication.cpp b/src/core/UBApplication.cpp index b189c3b8..b7d3e0c0 100644 --- a/src/core/UBApplication.cpp +++ b/src/core/UBApplication.cpp @@ -81,6 +81,8 @@ const QString UBApplication::mimeTypeUniboardPage = QString("application/vnd.mne const QString UBApplication::mimeTypeUniboardPageItem = QString("application/vnd.mnemis-uniboard-page-item"); const QString UBApplication::mimeTypeUniboardPageThumbnail = QString("application/vnd.mnemis-uniboard-thumbnail"); +QString UBApplication::fileToOpen = ""; + #if defined(Q_OS_OSX) || defined(Q_OS_LINUX) bool bIsMinimized = false; #endif @@ -88,7 +90,7 @@ bool bIsMinimized = false; QObject* UBApplication::staticMemoryCleaner = 0; -UBApplication::UBApplication(const QString &id, int &argc, char **argv) : QtSingleApplication(id, argc, argv) +UBApplication::UBApplication(const QString &id, int &argc, char **argv) : SingleApplication(argc, argv) , mPreferencesController(NULL) , mApplicationTranslator(NULL) , mQtGuiTranslator(NULL) @@ -309,6 +311,14 @@ int UBApplication::exec(const QString& pFileToImport) boardController->paletteManager()->rightPalette()); + if (!UBApplication::fileToOpen.isEmpty()) + { + if (!UBApplication::fileToOpen.endsWith("ubx")) + applicationController->importFile(UBApplication::fileToOpen); + else + applicationController->showMessage(tr("Cannot open your UBX file directly. Please import it in Documents mode instead"), false); + } + connect(applicationController, SIGNAL(mainModeChanged(UBApplicationController::MainMode)), boardController->paletteManager(), SLOT(slot_changeMainMode(UBApplicationController::MainMode))); @@ -348,7 +358,12 @@ int UBApplication::exec(const QString& pFileToImport) boardController->setupLayout(); if (pFileToImport.length() > 0) - UBApplication::applicationController->importFile(pFileToImport); + { + if (!pFileToImport.endsWith("ubx")) + applicationController->importFile(pFileToImport); + else + applicationController->showMessage(tr("Cannot open your UBX file directly. Please import it in Documents mode instead"), false); + } if (UBSettings::settings()->appStartMode->get().toInt()) applicationController->showDesktop(); @@ -575,7 +590,21 @@ bool UBApplication::eventFilter(QObject *obj, QEvent *event) UBPlatformUtils::setFrontProcess(); - applicationController->importFile(fileToOpenEvent->file()); + if (applicationController) + { + if (!fileToOpenEvent->file().endsWith("ubx")) + applicationController->importFile(fileToOpenEvent->file()); + else + applicationController->showMessage(tr("Cannot open your UBX file directly. Please import it in Documents mode instead"), false); + } + else + { + //startup : progressdialog.exec() is called and fileOpenEvent is consumed too early + // we store the file and will import it when the documents tree is ready + + UBApplication::fileToOpen = fileToOpenEvent->file(); + return true; + } } if (event->type() == QEvent::TabletLeaveProximity) diff --git a/src/core/UBApplication.h b/src/core/UBApplication.h index b821ce4a..5473804c 100644 --- a/src/core/UBApplication.h +++ b/src/core/UBApplication.h @@ -35,7 +35,7 @@ #include #include -#include "qtsingleapplication.h" +#include "singleapplication/singleapplication.h" namespace Ui { @@ -54,7 +54,7 @@ class UBApplicationController; class UBDocumentController; class UBMainWindow; -class UBApplication : public QtSingleApplication +class UBApplication : public SingleApplication { Q_OBJECT @@ -86,6 +86,8 @@ class UBApplication : public QtSingleApplication static const QString mimeTypeUniboardPageItem; static const QString mimeTypeUniboardPageThumbnail; + static QString fileToOpen; + static void showMessage(const QString& message, bool showSpinningWheel = false); static void setDisabled(bool disable); diff --git a/src/core/UBApplicationController.cpp b/src/core/UBApplicationController.cpp index ffe0f639..cf03215b 100644 --- a/src/core/UBApplicationController.cpp +++ b/src/core/UBApplicationController.cpp @@ -351,11 +351,11 @@ void UBApplicationController::showBoard() if (mMainMode == Document) { - int selectedSceneIndex = UBApplication::documentController->getSelectedItemIndex(); - if (selectedSceneIndex != -1) - { - UBApplication::boardController->setActiveDocumentScene(UBApplication::documentController->selectedDocument(), selectedSceneIndex, true); - } +// int selectedSceneIndex = UBApplication::documentController->getSelectedItemIndex(); +// if (selectedSceneIndex != -1) +// { +// UBApplication::boardController->setActiveDocumentScene(UBApplication::documentController->selectedDocument(), selectedSceneIndex); +// } } mMainMode = Board; @@ -435,12 +435,12 @@ void UBApplicationController::showDocument() { if (UBApplication::boardController->activeScene()->isModified()) UBApplication::boardController->persistCurrentScene(); + UBApplication::boardController->hide(); } if (UBApplication::documentController) { - emit UBApplication::documentController->reorderDocumentsRequested(); UBApplication::documentController->show(); } @@ -464,6 +464,7 @@ void UBApplicationController::showDesktop(bool dontSwitchFrontProcess) if (mMirror) { QRect rect = qApp->desktop()->screenGeometry(desktopWidgetIndex); + rect.moveTo(0, 0); mMirror->setSourceRect(rect); } diff --git a/src/core/UBDisplayManager.cpp b/src/core/UBDisplayManager.cpp index cd9db4ec..4256379d 100644 --- a/src/core/UBDisplayManager.cpp +++ b/src/core/UBDisplayManager.cpp @@ -180,7 +180,7 @@ void UBDisplayManager::setDisplayWidget(QWidget* pDisplayWidget) } mDisplayWidget = pDisplayWidget; mDisplayWidget->setGeometry(mDesktop->screenGeometry(mDisplayScreenIndex)); - if (UBSettings::settings()->appUseMultiscreen->get().toBool()) + if (numScreens() > 1 && UBSettings::settings()->appUseMultiscreen->get().toBool()) UBPlatformUtils::showFullScreen(mDisplayWidget); } } @@ -236,7 +236,7 @@ void UBDisplayManager::positionScreens() if (mDisplayWidget && mDisplayScreenIndex > -1) { - mDisplayWidget->hide(); + mDisplayWidget->showNormal(); mDisplayWidget->setGeometry(mDesktop->screenGeometry(mDisplayScreenIndex)); UBPlatformUtils::showFullScreen(mDisplayWidget); } diff --git a/src/core/UBDocumentManager.cpp b/src/core/UBDocumentManager.cpp index ac331d94..f451e519 100644 --- a/src/core/UBDocumentManager.cpp +++ b/src/core/UBDocumentManager.cpp @@ -286,7 +286,6 @@ int UBDocumentManager::addFilesToDocument(UBDocumentProxy* document, QStringList UBGraphicsScene* scene = UBPersistenceManager::persistenceManager()->createDocumentSceneAt(document, pageIndex); importAdaptor->placeImportedItemToScene(scene, page); UBPersistenceManager::persistenceManager()->persistDocumentScene(document, scene, pageIndex); - UBApplication::boardController->insertThumbPage(pageIndex); } UBPersistenceManager::persistenceManager()->persistDocumentMetadata(document); diff --git a/src/core/UBPersistenceManager.cpp b/src/core/UBPersistenceManager.cpp index 90a160c4..e163668e 100644 --- a/src/core/UBPersistenceManager.cpp +++ b/src/core/UBPersistenceManager.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include "frameworks/UBPlatformUtils.h" #include "frameworks/UBFileSystemUtils.h" @@ -98,7 +99,6 @@ UBPersistenceManager::UBPersistenceManager(QObject *pParent) mDocumentTreeStructureModel = new UBDocumentTreeModel(this); createDocumentProxiesStructure(); - emit proxyListChanged(); } @@ -123,6 +123,50 @@ UBPersistenceManager::~UBPersistenceManager() { } +void UBPersistenceManager::createDocumentProxiesStructure(const QFileInfoList &contentInfoList, bool interactive) +{ + // Create a QFutureWatcher and connect signals and slots. + QFutureWatcher futureWatcher; + QObject::connect(&futureWatcher, &QFutureWatcher::finished, &mProgress, &QProgressDialog::reset); + QObject::connect(&futureWatcher, &QFutureWatcher::progressRangeChanged, &mProgress, &QProgressDialog::setRange); + QObject::connect(&futureWatcher, &QFutureWatcher::progressValueChanged, &mProgress, &QProgressDialog::setValue); + + // Start the computation. + std::function createDocumentProxyLambda = [=](QFileInfo contentInfo) { + return createDocumentProxyStructure(contentInfo); + }; + + QFuture proxiesFuture = QtConcurrent::mapped(contentInfoList, createDocumentProxyLambda); + futureWatcher.setFuture(proxiesFuture); + + // Display the dialog and start the event loop. + mProgress.exec(); + + futureWatcher.waitForFinished(); + + QList proxies = futureWatcher.future().results(); + + for (auto&& proxy : qAsConst(proxies)) + { + if (proxy) + { + QString docGroupName = proxy->metaData(UBSettings::documentGroupName).toString(); + QModelIndex parentIndex = mDocumentTreeStructureModel->goTo(docGroupName); + if (parentIndex.isValid()) + { + if (!interactive) + mDocumentTreeStructureModel->addDocument(proxy, parentIndex); + else + processInteractiveReplacementDialog(proxy); + } + else + { + qDebug() << "something went wrong"; + } + } + } +} + void UBPersistenceManager::createDocumentProxiesStructure(bool interactive) { mDocumentRepositoryPath = UBSettings::userDocumentDirectory(); @@ -130,8 +174,13 @@ void UBPersistenceManager::createDocumentProxiesStructure(bool interactive) QDir rootDir(mDocumentRepositoryPath); rootDir.mkpath(rootDir.path()); - QFileInfoList contentList = rootDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Time | QDir::Reversed); - createDocumentProxiesStructure(contentList, interactive); + QFileInfoList contentInfoList = rootDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Time | QDir::Reversed); + + mProgress.setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::CustomizeWindowHint); + mProgress.setLabelText(QString("retrieving all your documents (found %1)").arg(contentInfoList.size())); + mProgress.setCancelButton(nullptr); + + createDocumentProxiesStructure(contentInfoList, interactive); if (QFileInfo(mFoldersXmlStorageName).exists()) { QDomDocument xmlDom; @@ -158,52 +207,36 @@ void UBPersistenceManager::createDocumentProxiesStructure(bool interactive) } } -void UBPersistenceManager::createDocumentProxiesStructure(const QFileInfoList &contentInfo, bool interactive) +UBDocumentProxy* UBPersistenceManager::createDocumentProxyStructure(QFileInfo& contentInfo) { - foreach(QFileInfo path, contentInfo) - { - QString fullPath = path.absoluteFilePath(); + QString fullPath = contentInfo.absoluteFilePath(); + QDir dir(fullPath); + if (dir.entryList(QDir::Files | QDir::NoDotAndDotDot).size() > 0) + { QMap metadatas = UBMetadataDcSubsetAdaptor::load(fullPath); - QString docGroupName = metadatas.value(UBSettings::documentGroupName, QString()).toString(); QString docName = metadatas.value(UBSettings::documentName, QString()).toString(); if (docName.isEmpty()) { qDebug() << "Group name and document name are empty in UBPersistenceManager::createDocumentProxiesStructure()"; - continue; - } - - QModelIndex parentIndex = mDocumentTreeStructureModel->goTo(docGroupName); - if (!parentIndex.isValid()) { - return; + return nullptr; } - UBDocumentProxy* docProxy = new UBDocumentProxy(fullPath, metadatas); // managed in UBDocumentTreeNode + UBDocumentProxy* docProxy = new UBDocumentProxy(fullPath); // managed in UBDocumentTreeNode foreach(QString key, metadatas.keys()) { docProxy->setMetaData(key, metadatas.value(key)); } - if (metadatas.contains(UBSettings::documentPageCount)) - { - int pageCount = metadatas.value(UBSettings::documentPageCount).toInt(); - if (pageCount == 0) - pageCount = sceneCount(docProxy); + docProxy->setPageCount(sceneCount(docProxy)); - docProxy->setPageCount(pageCount); - } - else - { - int pageCount = sceneCount(docProxy); - docProxy->setPageCount(pageCount); - } + docProxy->moveToThread(UBApplication::instance()->thread()); - if (!interactive) - mDocumentTreeStructureModel->addDocument(docProxy, parentIndex); - else - processInteractiveReplacementDialog(docProxy); + return docProxy; } -} + + return nullptr; +}; QDialog::DialogCode UBPersistenceManager::processInteractiveReplacementDialog(UBDocumentProxy *pProxy) { @@ -246,7 +279,10 @@ QDialog::DialogCode UBPersistenceManager::processInteractiveReplacementDialog(UB if (mDocumentTreeStructureModel->currentIndex() == replaceIndex) { - UBApplication::documentController->selectDocument(pProxy, true, true); + if (pProxy->pageCount() > 0) + { + UBApplication::documentController->selectDocument(pProxy, true, true); + } } if (replaceProxy) { @@ -256,7 +292,12 @@ QDialog::DialogCode UBPersistenceManager::processInteractiveReplacementDialog(UB mDocumentTreeStructureModel->removeRow(i, parentIndex); } } - pProxy->setMetaData(UBSettings::documentName, resultName); + + if (docName != resultName) + { + pProxy->setMetaData(UBSettings::documentName, resultName); + UBMetadataDcSubsetAdaptor::persist(pProxy); + } mDocumentTreeStructureModel->addDocument(pProxy, parentIndex); } replaceDialog->setParent(0); @@ -625,11 +666,6 @@ void UBPersistenceManager::deleteDocumentScenes(UBDocumentProxy* proxy, const QL if (compactedIndexes.size() == 0) return; - foreach(int index, compactedIndexes) - { - emit documentSceneWillBeDeleted(proxy, index); - } - QString sourceName = proxy->metaData(UBSettings::documentName).toString(); UBDocumentProxy *trashDocProxy = createDocument(UBSettings::trashedDocumentGroupNamePrefix/* + sourceGroupName*/, sourceName, false); @@ -650,8 +686,7 @@ void UBPersistenceManager::deleteDocumentScenes(UBDocumentProxy* proxy, const QL d.mkpath(d.absolutePath()); QFile::copy(source, target); } - - insertDocumentSceneAt(trashDocProxy, scene, trashDocProxy->pageCount()); + insertDocumentSceneAt(trashDocProxy, scene, trashDocProxy->pageCount(), true, true); } } @@ -817,7 +852,7 @@ void UBPersistenceManager::copyDocumentScene(UBDocumentProxy *from, int fromInde Q_ASSERT(QFileInfo(thumbTmp).exists()); Q_ASSERT(QFileInfo(thumbTo).exists()); - const QPixmap *pix = new QPixmap(thumbTmp); + auto pix = std::make_shared(thumbTmp); UBDocumentController *ctrl = UBApplication::documentController; ctrl->addPixmapAt(pix, toIndex); ctrl->TreeViewSelectionChanged(ctrl->firstSelectedTreeIndex(), QModelIndex()); @@ -828,10 +863,12 @@ void UBPersistenceManager::copyDocumentScene(UBDocumentProxy *from, int fromInde UBGraphicsScene* UBPersistenceManager::createDocumentSceneAt(UBDocumentProxy* proxy, int index, bool useUndoRedoStack) { - int count = sceneCount(proxy); + int count = proxy->pageCount(); for(int i = count - 1; i >= index; i--) + { renamePage(proxy, i , i + 1); + } mSceneCache.shiftUpScenes(proxy, index, count -1); @@ -852,7 +889,7 @@ UBGraphicsScene* UBPersistenceManager::createDocumentSceneAt(UBDocumentProxy* pr } -void UBPersistenceManager::insertDocumentSceneAt(UBDocumentProxy* proxy, UBGraphicsScene* scene, int index, bool persist) +void UBPersistenceManager::insertDocumentSceneAt(UBDocumentProxy* proxy, UBGraphicsScene* scene, int index, bool persist, bool deleting) { scene->setDocument(proxy); @@ -873,7 +910,8 @@ void UBPersistenceManager::insertDocumentSceneAt(UBDocumentProxy* proxy, UBGraph persistDocumentScene(proxy, scene, index); } - emit documentSceneCreated(proxy, index); + if (!deleting) + emit documentSceneCreated(proxy, index); } @@ -939,11 +977,12 @@ void UBPersistenceManager::reassignDocProxy(UBDocumentProxy *newDocument, UBDocu return mSceneCache.reassignDocProxy(newDocument, oldDocument); } -void UBPersistenceManager::persistDocumentScene(UBDocumentProxy* pDocumentProxy, UBGraphicsScene* pScene, const int pSceneIndex) +void UBPersistenceManager::persistDocumentScene(UBDocumentProxy* pDocumentProxy, UBGraphicsScene* pScene, const int pSceneIndex, bool isAnAutomaticBackup) { checkIfDocumentRepositoryExists(); - pScene->deselectAllItems(); + if (!isAnAutomaticBackup) + pScene->deselectAllItems(); generatePathIfNeeded(pDocumentProxy); @@ -978,6 +1017,7 @@ UBDocumentProxy* UBPersistenceManager::persistDocumentMetadata(UBDocumentProxy* void UBPersistenceManager::renamePage(UBDocumentProxy* pDocumentProxy, const int sourceIndex, const int targetIndex) { + UBApplication::showMessage(tr("Renaming pages (%1/%2)").arg(sourceIndex).arg(pDocumentProxy->pageCount())); QFile svg(pDocumentProxy->persistencePath() + UBFileSystemUtils::digitFileFormat("/page%1.svg", sourceIndex)); svg.rename(pDocumentProxy->persistencePath() + UBFileSystemUtils::digitFileFormat("/page%1.svg", targetIndex)); diff --git a/src/core/UBPersistenceManager.h b/src/core/UBPersistenceManager.h index 2f5356f3..cdcba56f 100644 --- a/src/core/UBPersistenceManager.h +++ b/src/core/UBPersistenceManager.h @@ -103,12 +103,11 @@ class UBPersistenceManager : public QObject virtual void copyDocumentScene(UBDocumentProxy *from, int fromIndex, UBDocumentProxy *to, int toIndex); - virtual void persistDocumentScene(UBDocumentProxy* pDocumentProxy, - UBGraphicsScene* pScene, const int pSceneIndex); + virtual void persistDocumentScene(UBDocumentProxy* pDocumentProxy, UBGraphicsScene* pScene, const int pSceneIndex, bool isAnAutomaticBackup = false); virtual UBGraphicsScene* createDocumentSceneAt(UBDocumentProxy* pDocumentProxy, int index, bool useUndoRedoStack = true); - virtual void insertDocumentSceneAt(UBDocumentProxy* pDocumentProxy, UBGraphicsScene* scene, int index, bool persist = true); + virtual void insertDocumentSceneAt(UBDocumentProxy* pDocumentProxy, UBGraphicsScene* scene, int index, bool persist = true, bool deleting = false); virtual void moveSceneToIndex(UBDocumentProxy* pDocumentProxy, int source, int target); @@ -132,7 +131,8 @@ class UBPersistenceManager : public QObject bool addDirectoryContentToDocument(const QString& documentRootFolder, UBDocumentProxy* pDocument); void createDocumentProxiesStructure(bool interactive = false); - void createDocumentProxiesStructure(const QFileInfoList &contentInfo, bool interactive = false); + void createDocumentProxiesStructure(const QFileInfoList &contentInfoList, bool interactive = false); + UBDocumentProxy* createDocumentProxyStructure(QFileInfo &contentInfo); QDialog::DialogCode processInteractiveReplacementDialog(UBDocumentProxy *pProxy); QStringList documentSubDirectories() @@ -166,7 +166,6 @@ class UBPersistenceManager : public QObject void documentWillBeDeleted(UBDocumentProxy* pDocumentProxy); void documentSceneCreated(UBDocumentProxy* pDocumentProxy, int pIndex); - void documentSceneWillBeDeleted(UBDocumentProxy* pDocumentProxy, int pIndex); private: int sceneCount(const UBDocumentProxy* pDocumentProxy); @@ -189,6 +188,8 @@ private: bool mHasPurgedDocuments; QString mDocumentRepositoryPath; QString mFoldersXmlStorageName; + QProgressDialog mProgress; + QFutureWatcher futureWatcher; private slots: void documentRepositoryChanged(const QString& path); diff --git a/src/core/UBSceneCache.cpp b/src/core/UBSceneCache.cpp index c5a110d1..f449c57a 100644 --- a/src/core/UBSceneCache.cpp +++ b/src/core/UBSceneCache.cpp @@ -222,6 +222,7 @@ void UBSceneCache::shiftUpScenes(UBDocumentProxy* proxy, int startIncIndex, int { for(int i = endIncIndex; i >= startIncIndex; i--) { + UBApplication::showMessage(QObject::tr("Moving cached scenes (%1/%2)").arg(i).arg(endIncIndex)); internalMoveScene(proxy, i, i + 1); } } diff --git a/src/core/UBSettings.cpp b/src/core/UBSettings.cpp index 23bbb069..c98f1d70 100644 --- a/src/core/UBSettings.cpp +++ b/src/core/UBSettings.cpp @@ -61,7 +61,6 @@ QString UBSettings::documentSize = QString("Size"); QString UBSettings::documentIdentifer = QString("ID"); QString UBSettings::documentVersion = QString("Version"); QString UBSettings::documentUpdatedAt = QString("UpdatedAt"); -QString UBSettings::documentPageCount = QString("PageCount"); QString UBSettings::documentDate = QString("date"); QString UBSettings::trashedDocumentGroupNamePrefix = QString("_Trash:"); @@ -73,6 +72,7 @@ QString UBSettings::undoCommandTransactionName = "UndoTransaction"; const int UBSettings::sDefaultFontPixelSize = 36; const char *UBSettings::sDefaultFontFamily = "Arial"; +const char *UBSettings::sDefaultFontStyleName = "Regular"; QString UBSettings::currentFileVersion = "4.8.0"; @@ -361,9 +361,6 @@ void UBSettings::init() webShowPageImmediatelyOnMirroredScreen = new UBSetting(this, "Web", "ShowPageImediatelyOnMirroredScreen", defaultShowPageImmediatelyOnMirroredScreen); webHomePage = new UBSetting(this, "Web", "Homepage", softwareHomeUrl); - webBookmarksPage = new UBSetting(this, "Web", "BookmarksPage", "http://www.myuniboard.com"); - webAddBookmarkUrl = new UBSetting(this, "Web", "AddBookmarkURL", "http://www.myuniboard.com/bookmarks/save/?url="); - webShowAddBookmarkButton = new UBSetting(this, "Web", "ShowAddBookmarkButton", false); pageCacheSize = new UBSetting(this, "App", "PageCacheSize", 20); @@ -412,6 +409,8 @@ void UBSettings::init() pdfZoomBehavior = new UBSetting(this, "PDF", "ZoomBehavior", "4"); enableQualityLossToIncreaseZoomPerfs = new UBSetting(this, "PDF", "enableQualityLossToIncreaseZoomPerfs", true); + exportBackgroundGrid = new UBSetting(this, "PDF", "ExportBackgroundGrid", false); + exportBackgroundColor = new UBSetting(this, "PDF", "ExportBackgroundColor", false); podcastFramesPerSecond = new UBSetting(this, "Podcast", "FramesPerSecond", 10); podcastVideoSize = new UBSetting(this, "Podcast", "VideoSize", "Medium"); @@ -871,6 +870,17 @@ void UBSettings::setFontFamily(const QString &family) } +QString UBSettings::fontStyleName() +{ + return value("Board/FontStyleName", sDefaultFontStyleName).toString(); +} + + +void UBSettings::setFontStyleName(const QString &styleName) +{ + setValue("Board/FontStyleName", styleName); +} + int UBSettings::fontPixelSize() { return value("Board/FontPixelSize", sDefaultFontPixelSize).toInt(); diff --git a/src/core/UBSettings.h b/src/core/UBSettings.h index 653eb6dc..6067db5b 100644 --- a/src/core/UBSettings.h +++ b/src/core/UBSettings.h @@ -97,6 +97,8 @@ class UBSettings : public QObject // Text related QString fontFamily(); void setFontFamily(const QString &family); + QString fontStyleName(); + void setFontStyleName(const QString &family); int fontPixelSize(); void setFontPixelSize(int pixelSize); int fontPointSize(); @@ -202,7 +204,6 @@ class UBSettings : public QObject static QString documentIdentifer; static QString documentVersion; static QString documentUpdatedAt; - static QString documentPageCount; static QString documentDate; @@ -328,9 +329,6 @@ class UBSettings : public QObject UBSetting* webShowPageImmediatelyOnMirroredScreen; UBSetting* webHomePage; - UBSetting* webBookmarksPage; - UBSetting* webAddBookmarkUrl; - UBSetting* webShowAddBookmarkButton; UBSetting* pageCacheSize; @@ -363,6 +361,8 @@ class UBSettings : public QObject UBSetting* pdfZoomBehavior; UBSetting* enableQualityLossToIncreaseZoomPerfs; + UBSetting* exportBackgroundGrid; + UBSetting* exportBackgroundColor; UBSetting* podcastFramesPerSecond; UBSetting* podcastVideoSize; @@ -466,6 +466,7 @@ class UBSettings : public QObject static const int sDefaultFontPixelSize; static const char *sDefaultFontFamily; + static const char *sDefaultFontStyleName; static QSettings* getAppSettings(); diff --git a/src/core/main.cpp b/src/core/main.cpp index ddb0c6ed..10f3f6ff 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -122,8 +122,8 @@ int main(int argc, char *argv[]) if (f.exists()) { fileToOpen += args[1]; - if (app.sendMessage(UBSettings::appPingMessage, 20000)) { - app.sendMessage(fileToOpen, 1000000); + if (app.sendMessage(UBSettings::appPingMessage.toUtf8(), 20000)) { + app.sendMessage(fileToOpen.toUtf8(), 1000000); return 0; } } diff --git a/src/desktop/UBCustomCaptureWindow.cpp b/src/desktop/UBCustomCaptureWindow.cpp index dc26abad..1a319738 100644 --- a/src/desktop/UBCustomCaptureWindow.cpp +++ b/src/desktop/UBCustomCaptureWindow.cpp @@ -79,6 +79,8 @@ int UBCustomCaptureWindow::execute(const QPixmap &pScreenPixmap) QDesktopWidget *desktop = QApplication::desktop(); int currentScreen = desktop->screenNumber(QCursor::pos()); + // necessary so that changing geometry really affects the widget + showNormal(); setGeometry(desktop->screenGeometry(currentScreen)); this->show(); setWindowOpacity(1.0); diff --git a/src/desktop/UBDesktopAnnotationController.cpp b/src/desktop/UBDesktopAnnotationController.cpp index ba269aa9..c810fd46 100644 --- a/src/desktop/UBDesktopAnnotationController.cpp +++ b/src/desktop/UBDesktopAnnotationController.cpp @@ -129,6 +129,7 @@ UBDesktopAnnotationController::UBDesktopAnnotationController(QObject *parent, UB connect(mDesktopPalette, SIGNAL(minimizeStart(eMinimizedLocation)), this, SLOT(onDesktopPaletteMinimize())); connect(mDesktopPalette, SIGNAL(mouseEntered()), mTransparentDrawingScene, SLOT(hideTool())); connect(mRightPalette, SIGNAL(mouseEntered()), mTransparentDrawingScene, SLOT(hideTool())); + connect(mRightPalette, SIGNAL(pageSelectionChangedRequired()), this, SLOT(updateBackground())); connect(mTransparentDrawingView, SIGNAL(resized(QResizeEvent*)), this, SLOT(onTransparentWidgetResized())); @@ -515,6 +516,7 @@ QPixmap UBDesktopAnnotationController::getScreenPixmap() QScreen * screen = UBApplication::controlScreen(); QRect rect = desktop->screenGeometry(QCursor::pos()); + rect.moveTo(0, 0); return screen->grabWindow(desktop->effectiveWinId(), rect.x(), rect.y(), rect.width(), rect.height()); @@ -555,7 +557,7 @@ void UBDesktopAnnotationController::penActionPressed() // Check if the mouse cursor is on the little arrow QPoint cursorPos = QCursor::pos(); - QPoint palettePos = mDesktopPalette->pos(); + QPoint palettePos = mDesktopPalette->mapToGlobal(QPoint(0, 0)); // global coordinates of palette QPoint buttonPos = mDesktopPalette->buttonPos(UBApplication::mainWindow->actionPen); int iX = cursorPos.x() - (palettePos.x() + buttonPos.x()); // x position of the cursor in the palette @@ -609,7 +611,7 @@ void UBDesktopAnnotationController::eraserActionPressed() // Check if the mouse cursor is on the little arrow QPoint cursorPos = QCursor::pos(); - QPoint palettePos = mDesktopPalette->pos(); + QPoint palettePos = mDesktopPalette->mapToGlobal(QPoint(0, 0)); QPoint buttonPos = mDesktopPalette->buttonPos(UBApplication::mainWindow->actionEraser); int iX = cursorPos.x() - (palettePos.x() + buttonPos.x()); // x position of the cursor in the palette @@ -664,7 +666,7 @@ void UBDesktopAnnotationController::markerActionPressed() // Check if the mouse cursor is on the little arrow QPoint cursorPos = QCursor::pos(); - QPoint palettePos = mDesktopPalette->pos(); + QPoint palettePos = mDesktopPalette->mapToGlobal(QPoint(0, 0)); QPoint buttonPos = mDesktopPalette->buttonPos(UBApplication::mainWindow->actionMarker); int iX = cursorPos.x() - (palettePos.x() + buttonPos.x()); // x position of the cursor in the palette @@ -966,7 +968,7 @@ void UBDesktopAnnotationController::updateMask(bool bTransparent) p.setPen(Qt::red); p.setBrush(QBrush(Qt::red)); - p.drawRect(mTransparentDrawingView->geometry().x(), mTransparentDrawingView->geometry().y(), mTransparentDrawingView->width(), mTransparentDrawingView->height()); + p.drawRect(0, 0, mTransparentDrawingView->width(), mTransparentDrawingView->height()); p.end(); mTransparentDrawingView->setMask(mMask.mask()); diff --git a/src/document/UBDocumentContainer.cpp b/src/document/UBDocumentContainer.cpp index 456c37a7..86e857a7 100644 --- a/src/document/UBDocumentContainer.cpp +++ b/src/document/UBDocumentContainer.cpp @@ -40,10 +40,7 @@ UBDocumentContainer::UBDocumentContainer(QObject * parent) UBDocumentContainer::~UBDocumentContainer() { - foreach(const QPixmap* pm, mDocumentThumbs){ - delete pm; - pm = NULL; - } + } void UBDocumentContainer::setDocument(UBDocumentProxy* document, bool forceReload) @@ -52,6 +49,7 @@ void UBDocumentContainer::setDocument(UBDocumentProxy* document, bool forceReloa { mCurrentDocument = document; + clearThumbPage(); reloadThumbnails(); emit documentSet(mCurrentDocument); } @@ -67,16 +65,20 @@ void UBDocumentContainer::duplicatePages(QList& pageIndexes) } } -bool UBDocumentContainer::movePageToIndex(int source, int target) +void UBDocumentContainer::duplicatePage(int index) { - //on document view - UBPersistenceManager::persistenceManager()->moveSceneToIndex(mCurrentDocument, source, target); - deleteThumbPage(source); - insertThumbPage(target); - emit documentThumbnailsUpdated(this); - //on board thumbnails view + UBPersistenceManager::persistenceManager()->duplicateDocumentScene(mCurrentDocument, index); +} + +void UBDocumentContainer::moveThumbPage(int source, int target) +{ + mDocumentThumbs.move(source, target); + + //on board thumbnails view (UBDocumentNavigator) + emit documentPageMoved(source, target); + + //on board thumbnails view (UBoardThumbnailsView) emit moveThumbnailRequired(source, target); - return true; } void UBDocumentContainer::deletePages(QList& pageIndexes) @@ -86,33 +88,25 @@ void UBDocumentContainer::deletePages(QList& pageIndexes) foreach(int index, pageIndexes) { deleteThumbPage(index - offset); - emit removeThumbnailRequired(index - offset); offset++; - } - emit documentThumbnailsUpdated(this); } void UBDocumentContainer::addPage(int index) { UBPersistenceManager::persistenceManager()->createDocumentSceneAt(mCurrentDocument, index); insertThumbPage(index); - - emit documentThumbnailsUpdated(this); - emit addThumbnailRequired(this, index); } -void UBDocumentContainer::addPixmapAt(const QPixmap *pix, int index) +void UBDocumentContainer::addPixmapAt(std::shared_ptr pix, int index) { mDocumentThumbs.insert(index, pix); - emit documentThumbnailsUpdated(this); } void UBDocumentContainer::clearThumbPage() { - qDeleteAll(mDocumentThumbs); mDocumentThumbs.clear(); } @@ -124,41 +118,47 @@ void UBDocumentContainer::initThumbPage() insertThumbPage(i); } -void UBDocumentContainer::updatePage(int index) -{ - updateThumbPage(index); - emit documentThumbnailsUpdated(this); -} - void UBDocumentContainer::deleteThumbPage(int index) { mDocumentThumbs.removeAt(index); + + //on board thumbnails view (UBDocumentNavigator) + emit documentPageRemoved(index); + + //on board thumbnails view (UBoardThumbnailsView) + emit removeThumbnailRequired(index); } void UBDocumentContainer::updateThumbPage(int index) { if (mDocumentThumbs.size() > index) { - mDocumentThumbs[index] = UBThumbnailAdaptor::get(mCurrentDocument, index); + QPixmap pixmap = UBThumbnailAdaptor::get(mCurrentDocument, index); + mDocumentThumbs[index] = std::make_shared(pixmap); + emit documentPageUpdated(index); } - else - { - qDebug() << "error [updateThumbPage] : index > mDocumentThumbs' size."; - } } void UBDocumentContainer::insertThumbPage(int index) { - mDocumentThumbs.insert(index, UBThumbnailAdaptor::get(mCurrentDocument, index)); + QPixmap newPixmap = UBThumbnailAdaptor::get(mCurrentDocument, index); + mDocumentThumbs.insert(index, std::make_shared(newPixmap)); + + emit documentPageInserted(index); + emit addThumbnailRequired(this, index); +} + +void UBDocumentContainer::insertExistingThumbPage(int index, std::shared_ptr thumbnailPixmap) +{ + mDocumentThumbs.insert(index, thumbnailPixmap); + + emit documentPageInserted(index); + emit addThumbnailRequired(this, index); } void UBDocumentContainer::reloadThumbnails() { - if (mCurrentDocument) - { - UBThumbnailAdaptor::load(mCurrentDocument, mDocumentThumbs); - } emit documentThumbnailsUpdated(this); } @@ -174,6 +174,5 @@ int UBDocumentContainer::sceneIndexFromPage(int page) void UBDocumentContainer::addEmptyThumbPage() { - const QPixmap* pThumb = new QPixmap(); - mDocumentThumbs.append(pThumb); + mDocumentThumbs.append(std::shared_ptr()); } diff --git a/src/document/UBDocumentContainer.h b/src/document/UBDocumentContainer.h index 497861b0..e02556b5 100644 --- a/src/document/UBDocumentContainer.h +++ b/src/document/UBDocumentContainer.h @@ -45,8 +45,9 @@ class UBDocumentContainer : public QObject void pureSetDocument(UBDocumentProxy *document) {mCurrentDocument = document;} UBDocumentProxy* selectedDocument(){return mCurrentDocument;} - int pageCount(){return mCurrentDocument->pageCount();} - const QPixmap* pageAt(int index) + QList>& documentThumbs() { return mDocumentThumbs; } + int pageCount() const{return mCurrentDocument->pageCount();} + std::shared_ptr pageAt(int index) { if (index < mDocumentThumbs.size()) return mDocumentThumbs[index]; @@ -60,38 +61,41 @@ class UBDocumentContainer : public QObject static int sceneIndexFromPage(int sceneIndex); void duplicatePages(QList& pageIndexes); - bool movePageToIndex(int source, int target); + void duplicatePage(int index); void deletePages(QList& pageIndexes); - void clearThumbPage(); - void initThumbPage(); - void addPage(int index); - void addPixmapAt(const QPixmap *pix, int index); - void updatePage(int index); - void addEmptyThumbPage(); - void reloadThumbnails(); - void insertThumbPage(int index); - private: - UBDocumentProxy* mCurrentDocument; - QList mDocumentThumbs; + void addPage(int index); + void addPixmapAt(std::shared_ptr pix, int index); + virtual void reloadThumbnails(); - protected: + void clearThumbPage(); + void initThumbPage(); + void insertExistingThumbPage(int index, std::shared_ptr thumbnailPixmap); + void insertThumbPage(int index); + void addEmptyThumbPage(); void deleteThumbPage(int index); void updateThumbPage(int index); + void moveThumbPage(int source, int target); + + private: + UBDocumentProxy* mCurrentDocument; + QList> mDocumentThumbs; signals: void documentSet(UBDocumentProxy* document); + void documentPageInserted(int index); void documentPageUpdated(int index); + void documentPageRemoved(int index); + void documentPageMoved(int from, int to); + void documentThumbnailsUpdated(UBDocumentContainer* source); void initThumbnailsRequired(UBDocumentContainer* source); void addThumbnailRequired(UBDocumentContainer* source, int index); void removeThumbnailRequired(int index); void moveThumbnailRequired(int from, int to); void updateThumbnailsRequired(); - - void documentThumbnailsUpdated(UBDocumentContainer* source); }; diff --git a/src/document/UBDocumentController.cpp b/src/document/UBDocumentController.cpp index 5f7bef20..f2b7c477 100644 --- a/src/document/UBDocumentController.cpp +++ b/src/document/UBDocumentController.cpp @@ -664,7 +664,7 @@ QMimeData *UBDocumentTreeModel::mimeData (const QModelIndexList &indexes) const #if defined(Q_OS_OSX) #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) - if (QOperatingSystemVersion::current().minorVersion() < 15) /* < Mojave */ + if (QOperatingSystemVersion::current().majorVersion() == 10 && QOperatingSystemVersion::current().minorVersion() < 15) /* <= Mojave */ mimeData->setUrls(urlList); #endif #else @@ -1034,6 +1034,16 @@ QString UBDocumentTreeModel::virtualPathForIndex(const QModelIndex &pIndex) cons return virtualDirForIndex(pIndex) + "/" + curNode->nodeName(); } +QList UBDocumentTreeModel::nodeChildrenFromIndex(const QModelIndex &pIndex) const +{ + UBDocumentTreeNode *node = nodeFromIndex(pIndex); + + if (node) + return node->children(); + else + return QList(); +} + QStringList UBDocumentTreeModel::nodeNameList(const QModelIndex &pIndex, bool distinctNodeType) const { QStringList result; @@ -1331,6 +1341,7 @@ UBDocumentTreeView::UBDocumentTreeView(QWidget *parent) : QTreeView(parent) { setObjectName("UBDocumentTreeView"); setRootIsDecorated(true); + setSelectionBehavior(SelectRows); } void UBDocumentTreeView::setSelectedAndExpanded(const QModelIndex &pIndex, bool pExpand, bool pEdit) @@ -1348,9 +1359,9 @@ void UBDocumentTreeView::setSelectedAndExpanded(const QModelIndex &pIndex, bool ? QItemSelectionModel::Select : QItemSelectionModel::Deselect; - setCurrentIndex(pExpand - ? indexCurrentDoc - : QModelIndex()); + setCurrentIndex(indexCurrentDoc); + + selectionModel()->setCurrentIndex(proxy->mapFromSource(indexCurrentDoc), QItemSelectionModel::SelectCurrent); selectionModel()->select(proxy->mapFromSource(indexCurrentDoc), QItemSelectionModel::Rows | sel); @@ -1359,7 +1370,7 @@ void UBDocumentTreeView::setSelectedAndExpanded(const QModelIndex &pIndex, bool indexCurrentDoc = indexCurrentDoc.parent(); } - scrollTo(proxy->mapFromSource(pIndex), QAbstractItemView::PositionAtCenter); + scrollTo(proxy->mapFromSource(pIndex)); if (pEdit) edit(proxy->mapFromSource(pIndex)); @@ -1387,6 +1398,12 @@ void UBDocumentTreeView::hSliderRangeChanged(int min, int max) } } +void UBDocumentTreeView::mousePressEvent(QMouseEvent *event) +{ + QTreeView::mousePressEvent(event); + UBApplication::documentController->clearThumbnailsSelection(); +} + void UBDocumentTreeView::dragEnterEvent(QDragEnterEvent *event) { QTreeView::dragEnterEvent(event); @@ -1539,18 +1556,25 @@ void UBDocumentTreeView::dropEvent(QDropEvent *event) Q_ASSERT(QFileInfo(thumbTmp).exists()); Q_ASSERT(QFileInfo(thumbTo).exists()); - const QPixmap *pix = new QPixmap(thumbTmp); - UBDocumentController *ctrl = UBApplication::documentController; - ctrl->addPixmapAt(pix, toIndex); - ctrl->TreeViewSelectionChanged(ctrl->firstSelectedTreeIndex(), QModelIndex()); + + auto pix = std::make_shared(thumbTmp); + UBApplication::documentController->insertExistingThumbPage(toIndex, pix); + if (UBApplication::documentController->selectedDocument() == targetDocProxy) + { + UBApplication::documentController->reloadThumbnails(); + } + if (UBApplication::boardController->selectedDocument() == targetDocProxy) + { + UBApplication::boardController->insertThumbPage(toIndex); + } } QApplication::restoreOverrideCursor(); - UBApplication::applicationController->showMessage(tr("%1 pages copied", "", total).arg(total), false); docModel->setHighLighted(QModelIndex()); } + UBApplication::applicationController->showMessage(tr("%1 pages copied", "", total).arg(total), false); } else { @@ -1569,7 +1593,7 @@ void UBDocumentTreeView::dropEvent(QDropEvent *event) QTreeView::dropEvent(event); - UBApplication::documentController->updateActions(); + UBApplication::documentController->pageSelectionChanged(); } void UBDocumentTreeView::paintEvent(QPaintEvent *event) @@ -1645,10 +1669,15 @@ UBDocumentTreeItemDelegate::UBDocumentTreeItemDelegate(QObject *parent) void UBDocumentTreeItemDelegate::commitAndCloseEditor() { - QLineEdit *lineEditor = qobject_cast(sender()); - if (lineEditor) { - emit commitData(lineEditor); + QLineEdit *lineEditor = dynamic_cast(sender()); + if (lineEditor) + { + if (lineEditor->hasAcceptableInput()) + { + emit commitData(lineEditor); //emit closeEditor(lineEditor); + } + emit UBApplication::documentController->reorderDocumentsRequested(); } } @@ -1656,14 +1685,20 @@ void UBDocumentTreeItemDelegate::commitAndCloseEditor() void UBDocumentTreeItemDelegate::processChangedText(const QString &str) const { QLineEdit *editor = qobject_cast(sender()); - if (!editor) { - return; - } - - if (!validateString(str)) { - editor->setStyleSheet("background-color: #FFB3C8;"); - } else { - editor->setStyleSheet("background-color: #FFFFFF;"); + if (editor) + { + if (editor->validator()) + { + int pos = 0; + if (editor->validator()->validate(const_cast(str), pos) != QValidator::Acceptable) + { + editor->setStyleSheet("background-color: #FFB3C8;"); + } + else + { + editor->setStyleSheet("background-color: #FFFFFF;"); + } + } } } @@ -1678,16 +1713,41 @@ QWidget *UBDocumentTreeItemDelegate::createEditor(QWidget *parent, const QStyleO //N/C - NNE - 20140407 : Add the test for the index column. if(index.column() == 0){ mExistingFileNames.clear(); - const UBDocumentTreeModel *indexModel = qobject_cast(index.model()); - if (indexModel) { - mExistingFileNames = indexModel->nodeNameList(index.parent()); - mExistingFileNames.removeOne(index.data().toString()); + const UBDocumentTreeModel *docModel = 0; + + const UBSortFilterProxyModel *proxy = dynamic_cast(index.model()); + + if(proxy){ + docModel = dynamic_cast(proxy->sourceModel()); + }else{ + docModel = dynamic_cast(index.model()); } - QLineEdit *nameEditor = new QLineEdit(parent); - connect(nameEditor, SIGNAL(editingFinished()), this, SLOT(commitAndCloseEditor())); - connect(nameEditor, SIGNAL(textChanged(QString)), this, SLOT(processChangedText(QString))); - return nameEditor; + QModelIndex sourceIndex = proxy->mapToSource(index); + + if (docModel) + { + mExistingFileNames = docModel->nodeNameList(sourceIndex.parent()); + mExistingFileNames.removeOne(sourceIndex.data().toString()); + + UBDocumentTreeNode* sourceNode = docModel->nodeFromIndex(sourceIndex); + + if (sourceNode) + { + QLineEdit *nameEditor = new QLineEdit(parent); + QList nodeChildren = docModel->nodeChildrenFromIndex(sourceIndex.parent()); + nodeChildren.removeOne(sourceNode); + + UBValidator* validator = new UBValidator(nodeChildren, sourceNode->nodeType()); + nameEditor->setValidator(validator); + connect(nameEditor, SIGNAL(editingFinished()), this, SLOT(commitAndCloseEditor())); + connect(nameEditor, SIGNAL(textChanged(QString)), this, SLOT(processChangedText(QString))); + + return nameEditor; + } + } + + return nullptr; } //N/C - NNe - 20140407 : the other column are not editable. @@ -1706,8 +1766,17 @@ void UBDocumentTreeItemDelegate::setEditorData(QWidget *editor, const QModelInde void UBDocumentTreeItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QLineEdit *lineEditor = qobject_cast(editor); - if (validateString(lineEditor->text())) { - model->setData(index, lineEditor->text()); + if (lineEditor) + { + int pos; + QString input = lineEditor->text(); + if (lineEditor->validator()) + { + if (lineEditor->validator()->validate(input, pos) == QValidator::Acceptable) + { + model->setData(index, input); + } + } } } @@ -1738,6 +1807,10 @@ UBDocumentController::UBDocumentController(UBMainWindow* mainWindow) setupToolbar(); connect(this, SIGNAL(exportDone()), mMainWindow, SLOT(onExportDone())); connect(this, SIGNAL(documentThumbnailsUpdated(UBDocumentContainer*)), this, SLOT(refreshDocumentThumbnailsView(UBDocumentContainer*))); + //connect(this, SIGNAL(documentPageInserted(int)), this, SLOT(insertThumbnail(int))); + connect(this, SIGNAL(documentPageUpdated(int)), this, SLOT(updateThumbnail(int))); + connect(this, SIGNAL(documentPageRemoved(int)), this, SLOT(removeThumbnail(int))); + connect(this, SIGNAL(documentPageMoved(int, int)), this, SLOT(moveThumbnail(int, int))); connect(this, SIGNAL(reorderDocumentsRequested()), this, SLOT(reorderDocuments())); } @@ -1760,11 +1833,27 @@ void UBDocumentController::createNewDocument() ? docModel->virtualPathForIndex(selectedIndex) : docModel->virtualDirForIndex(selectedIndex); - UBDocumentProxy *document = pManager->createDocument(groupName); + + QDateTime now = QDateTime::currentDateTime(); + QString documentName = ""; + if (docModel->isCatalog(selectedIndex)) + { + documentName = docModel->adjustNameForParentIndex(now.toString(Qt::SystemLocaleShortDate), selectedIndex); + } + else + { + documentName = docModel->adjustNameForParentIndex(now.toString(Qt::SystemLocaleShortDate), selectedIndex.parent()); + } + + + UBDocumentProxy *document = pManager->createDocument(groupName, documentName); + selectDocument(document, true, false, true); if (document) pManager->mDocumentTreeStructureModel->markDocumentAsNew(document); + + pageSelectionChanged(); } void UBDocumentController::selectDocument(UBDocumentProxy* proxy, bool setAsCurrentDocument, const bool onImport, const bool editMode) @@ -1775,19 +1864,26 @@ void UBDocumentController::selectDocument(UBDocumentProxy* proxy, bool setAsCurr return; } - if (setAsCurrentDocument) { + if (setAsCurrentDocument) + { UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel->setCurrentDocument(proxy); QModelIndex indexCurrentDoc = UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel->indexForProxy(proxy); - mDocumentUI->documentTreeView->setSelectedAndExpanded(indexCurrentDoc, true, editMode); + if (indexCurrentDoc.isValid()) + { + mDocumentUI->documentTreeView->setSelectedAndExpanded(indexCurrentDoc, true, editMode); - if (proxy != mBoardController->selectedDocument()) // only if wanted Document is different from document actually on Board, // ALTI/AOU - 20140217 + if (proxy != mBoardController->selectedDocument()) // only if wanted Document is different from document actually on Board, // ALTI/AOU - 20140217 + { + //issue 1629 - NNE - 20131105 : When set a current document, change in the board controller + mBoardController->setActiveDocumentScene(proxy, 0, true, onImport); + } + } + else { - //issue 1629 - NNE - 20131105 : When set a current document, change in the board controller - mBoardController->setActiveDocumentScene(proxy, 0, true, onImport); + qWarning() << "an issue occured while trying to select current index in document tree"; } } - mSelectionType = Document; setDocument(proxy); } @@ -1807,6 +1903,8 @@ void UBDocumentController::createNewDocumentGroup() QModelIndex newIndex = docModel->addCatalog(newFolderName, parentIndex); mDocumentUI->documentTreeView->setSelectedAndExpanded(newIndex, true, true); + + pageSelectionChanged(); } @@ -1849,7 +1947,8 @@ void UBDocumentController::TreeViewSelectionChanged(const QModelIndex ¤t, //We have just to pass a null proxy to disable the display of thumbnail UBDocumentProxy *currentDocumentProxy = 0; - if(current_index.isValid() && mDocumentUI->documentTreeView->selectionModel()->selectedRows(0).size() == 1){ + if(current_index.isValid() && mDocumentUI->documentTreeView->selectionModel()->selectedRows(0).size() == 1) + { currentDocumentProxy = docModel->proxyData(current_index); setDocument(currentDocumentProxy, false); } @@ -1864,8 +1963,6 @@ void UBDocumentController::TreeViewSelectionChanged(const QModelIndex ¤t, } mCurrentIndexMoved = false; } - - itemSelectionChanged(docModel->isCatalog(current_index) ? Folder : Document); } //N/C - NNE - 20140402 : workaround for using a proxy model @@ -2093,11 +2190,6 @@ void UBDocumentController::setupViews() connect(mDocumentUI->thumbnailWidget, SIGNAL(mouseDoubleClick(QGraphicsItem*,int)), this, SLOT(thumbnailPageDoubleClicked(QGraphicsItem*,int))); connect(mDocumentUI->thumbnailWidget, SIGNAL(mouseClick(QGraphicsItem*, int)), this, SLOT(pageClicked(QGraphicsItem*, int))); - connect(mDocumentUI->thumbnailWidget->scene(), SIGNAL(selectionChanged()), this, SLOT(pageSelectionChanged())); - - connect(UBPersistenceManager::persistenceManager(), SIGNAL(documentSceneCreated(UBDocumentProxy*, int)), this, SLOT(documentSceneChanged(UBDocumentProxy*, int))); - connect(UBPersistenceManager::persistenceManager(), SIGNAL(documentSceneWillBeDeleted(UBDocumentProxy*, int)), this, SLOT(documentSceneChanged(UBDocumentProxy*, int))); - mDocumentUI->thumbnailWidget->setBackgroundBrush(UBSettings::documentViewLightColor); #ifdef Q_WS_MACX @@ -2161,8 +2253,9 @@ void UBDocumentController::sortDocuments(int kind, int order) mDocumentUI->documentTreeView->hideColumn(2); } } -} + mDocumentUI->documentTreeView->setSelectedAndExpanded(firstSelectedTreeIndex(), true); +} void UBDocumentController::onSortOrderChanged(bool order) { @@ -2222,10 +2315,9 @@ void UBDocumentController::show() { selectDocument(mBoardController->selectedDocument()); - //to be sure thumbnails will be up-to-date - reloadThumbnails(); + reorderDocuments(); - updateActions(); + pageSelectionChanged(); if(!mToolsPalette) setupPalettes(); @@ -2299,7 +2391,10 @@ void UBDocumentController::duplicateSelectedItem() if (selectedSceneIndexes.count() > 0) { duplicatePages(selectedSceneIndexes); - emit documentThumbnailsUpdated(this); + if (selectedDocument() == selectedDocumentProxy()) + { + reloadThumbnails(); + } selectedDocument()->setMetaData(UBSettings::documentUpdatedAt, UBStringUtils::toUtcIsoDateTime(QDateTime::currentDateTime())); UBMetadataDcSubsetAdaptor::persist(selectedDocument()); int selectedThumbnail = selectedSceneIndexes.last() + selectedSceneIndexes.size(); @@ -2307,8 +2402,11 @@ void UBDocumentController::duplicateSelectedItem() int sceneCount = selectedSceneIndexes.count(); showMessage(tr("duplicated %1 page","duplicated %1 pages",sceneCount).arg(sceneCount), false); - mBoardController->setActiveDocumentScene(selectedThumbnail); - mBoardController->reloadThumbnails(); + if (selectedDocument() == mBoardController->selectedDocument()) + { + mBoardController->setActiveDocumentScene(selectedThumbnail); + mBoardController->reloadThumbnails(); + } } } else @@ -2326,6 +2424,7 @@ void UBDocumentController::duplicateSelectedItem() } emit reorderDocumentsRequested(); + pageSelectionChanged(); } void UBDocumentController::deleteSelectedItem() @@ -2346,7 +2445,7 @@ void UBDocumentController::deleteSelectedItem() deleteSingleItem(indexes.at(0), docModel); } - updateActions(); + pageSelectionChanged(); } void UBDocumentController::deleteMultipleItems(QModelIndexList indexes, UBDocumentTreeModel* docModel) @@ -2370,8 +2469,8 @@ void UBDocumentController::deleteMultipleItems(QModelIndexList indexes, UBDocume for (int i =0; i < indexes.size(); i++) { deleteIndexAndAssociatedData(indexes.at(i)); - emit documentThumbnailsUpdated(this); } + emit documentThumbnailsUpdated(this); break; } case EmptyFolder: @@ -2762,9 +2861,16 @@ void UBDocumentController::deleteIndexAndAssociatedData(const QModelIndex &pInde } //N/C - NNE - 20140408 - if(pIndex.column() == 0){ + UBDocumentProxy *proxyData = nullptr; + if(pIndex.column() == 0) + { if (docModel->isDocument(pIndex)) { - UBDocumentProxy *proxyData = docModel->proxyData(pIndex); + proxyData = docModel->proxyData(pIndex); + + if (selectedDocument() == proxyData) + { + setDocument(nullptr); + } if (proxyData) { UBPersistenceManager::persistenceManager()->deleteDocument(proxyData); @@ -2772,7 +2878,25 @@ void UBDocumentController::deleteIndexAndAssociatedData(const QModelIndex &pInde } } - docModel->removeRow(pIndex.row(), pIndex.parent()); + if (proxyData) + { + // need to recall indexForProxy as rows could have changed when performing a multiple deletion + QModelIndex indexForProxy = docModel->indexForProxy(proxyData); + if (!docModel->removeRow(indexForProxy.row(), indexForProxy.parent())) + { + qDebug() << "could not remove row (r:" << indexForProxy.row() << "p:" << indexForProxy.parent() << ")"; + } + } + else + { + if (docModel->isCatalog(pIndex)) + { + if (!docModel->removeRow(pIndex.row(), pIndex.parent())) + { + qDebug() << "could not remove row (r:" << pIndex.row() << "p:" << pIndex.parent() << ")"; + } + } + } } @@ -2846,6 +2970,7 @@ void UBDocumentController::importFile() if (createdDocument) { selectDocument(createdDocument, true, true, true); + pageSelectionChanged(); } else { showMessage(tr("Failed to import file ... ")); @@ -2888,6 +3013,10 @@ void UBDocumentController::addFolderOfImages() document->setMetaData(UBSettings::documentUpdatedAt, UBStringUtils::toUtcIsoDateTime(QDateTime::currentDateTime())); UBMetadataDcSubsetAdaptor::persist(document); reloadThumbnails(); + if (selectedDocument() == UBApplication::boardController->selectedDocument()) + UBApplication::boardController->reloadThumbnails(); + + pageSelectionChanged(); } } } @@ -2901,7 +3030,6 @@ void UBDocumentController::addFileToDocument() if (document) { addFileToDocument(document); - reloadThumbnails(); } } @@ -2934,6 +3062,11 @@ bool UBDocumentController::addFileToDocument(UBDocumentProxy* document) { document->setMetaData(UBSettings::documentUpdatedAt, UBStringUtils::toUtcIsoDateTime(QDateTime::currentDateTime())); UBMetadataDcSubsetAdaptor::persist(document); + reloadThumbnails(); + if (selectedDocument() == UBApplication::boardController->selectedDocument()) + UBApplication::boardController->reloadThumbnails(); + + pageSelectionChanged(); } else { @@ -2949,19 +3082,46 @@ bool UBDocumentController::addFileToDocument(UBDocumentProxy* document) void UBDocumentController::moveSceneToIndex(UBDocumentProxy* proxy, int source, int target) { - if (UBDocumentContainer::movePageToIndex(source, target)) - { - proxy->setMetaData(UBSettings::documentUpdatedAt, UBStringUtils::toUtcIsoDateTime(QDateTime::currentDateTime())); - UBMetadataDcSubsetAdaptor::persist(proxy); + UBPersistenceManager::persistenceManager()->moveSceneToIndex(proxy, source, target); - mDocumentUI->thumbnailWidget->hightlightItem(target); + proxy->setMetaData(UBSettings::documentUpdatedAt, UBStringUtils::toUtcIsoDateTime(QDateTime::currentDateTime())); + UBMetadataDcSubsetAdaptor::persist(proxy); - mBoardController->setActiveDocumentScene(target); - mBoardController->reloadThumbnails(); + UBDocumentContainer::moveThumbPage(source, target); + if (UBApplication::boardController->selectedDocument() == selectedDocument()) + { + UBApplication::boardController->moveThumbPage(source, target); } + mDocumentUI->thumbnailWidget->hightlightItem(target); + + //mBoardController->setActiveDocumentScene(target); +} + +void UBDocumentController::insertThumbnail(int index, const QPixmap& pix) +{ + QGraphicsPixmapItem *newThumbnail = new UBSceneThumbnailPixmap(pix, selectedDocument(), index); // deleted by the tree widget + + mDocumentUI->thumbnailWidget->insertThumbnail(index, newThumbnail); +} + +void UBDocumentController::updateThumbnail(int index) +{ + auto pix = UBApplication::boardController->pageAt(index); + + mDocumentUI->thumbnailWidget->updateThumbnailPixmap(index, *pix); } +void UBDocumentController::removeThumbnail(int index) +{ + mDocumentUI->thumbnailWidget->removeThumbnail(index); +} + +void UBDocumentController::moveThumbnail(int from, int to) +{ + mDocumentUI->thumbnailWidget->moveThumbnail(from, to); +} + void UBDocumentController::thumbnailViewResized() { int maxWidth = qMin(UBSettings::maxThumbnailWidth, mDocumentUI->thumbnailWidget->width()); @@ -2989,19 +3149,12 @@ void UBDocumentController::pageSelectionChanged() itemSelectionChanged(Folder); else itemSelectionChanged(None); - - updateActions(); } void UBDocumentController::documentSceneChanged(UBDocumentProxy* proxy, int pSceneIndex) { Q_UNUSED(pSceneIndex); - if (proxy == selectedDocumentProxy()) - { - reloadThumbnails(); - } - QModelIndexList sel = mDocumentUI->documentTreeView->selectionModel()->selectedRows(0); QModelIndex selection; @@ -3082,7 +3235,6 @@ void UBDocumentController::addToDocument() UBMetadataDcSubsetAdaptor::persist(mBoardController->selectedDocument()); mBoardController->reloadThumbnails(); - emit UBApplication::boardController->documentThumbnailsUpdated(this); UBApplication::applicationController->showBoard(); mBoardController->setActiveDocumentScene(newActiveSceneIndex); @@ -3191,6 +3343,10 @@ void UBDocumentController::addImages() document->setMetaData(UBSettings::documentUpdatedAt, UBStringUtils::toUtcIsoDateTime(QDateTime::currentDateTime())); UBMetadataDcSubsetAdaptor::persist(document); reloadThumbnails(); + if (selectedDocument() == UBApplication::boardController->selectedDocument()) + UBApplication::boardController->reloadThumbnails(); + + pageSelectionChanged(); } } } @@ -3260,16 +3416,6 @@ void UBDocumentController::focusChanged(QWidget *old, QWidget *current) else mSelectionType = None; } - else - { - if (old != mDocumentUI->thumbnailWidget && - old != mDocumentUI->documentTreeView && - old != mDocumentUI->documentZoomSlider) - { - if (current && (current->metaObject()->className() != QPushButton::staticMetaObject.className())) - mSelectionType = None; - } - } } void UBDocumentController::updateActions() @@ -3335,6 +3481,7 @@ void UBDocumentController::updateActions() updateExportSubActions(selectedIndex); bool firstSceneSelected = false; + bool everyPageSelected = false; if (docSelected) { mMainWindow->actionDuplicate->setEnabled(!trashSelected); @@ -3372,22 +3519,77 @@ void UBDocumentController::updateActions() switch (static_cast(deletionForSelection)) { case MoveToTrash : + if (mSelectionType == Folder) + { + mMainWindow->actionDelete->setIcon(QIcon(":/images/trash-folder.png")); + mMainWindow->actionDelete->setText(tr("Delete")); + } + else if (mSelectionType == Document) + { + mMainWindow->actionDelete->setIcon(QIcon(":/images/trash-document.png")); + mMainWindow->actionDelete->setText(tr("Trash")); + } + else if (mSelectionType == Page) + { + mMainWindow->actionDelete->setIcon(QIcon(":/images/trash-document-page.png")); + mMainWindow->actionDelete->setText(tr("Trash")); + } + else + {//can happen ? + mMainWindow->actionDelete->setIcon(QIcon(":/images/trash.png")); + mMainWindow->actionDelete->setText(tr("Trash")); + } + break; case DeletePage : - mMainWindow->actionDelete->setIcon(QIcon(":/images/trash.png")); + mMainWindow->actionDelete->setIcon(QIcon(":/images/trash-document-page.png")); mMainWindow->actionDelete->setText(tr("Trash")); break; case CompleteDelete : - mMainWindow->actionDelete->setIcon(QIcon(":/images/toolbar/deleteDocument.png")); - mMainWindow->actionDelete->setText(tr("Delete")); + if (mSelectionType == Folder) + { + mMainWindow->actionDelete->setIcon(QIcon(":/images/trash-delete-folder.png")); + mMainWindow->actionDelete->setText(tr("Delete")); + } + else + { + mMainWindow->actionDelete->setIcon(QIcon(":/images/trash-delete-document.png")); + mMainWindow->actionDelete->setText(tr("Delete")); + } break; case EmptyFolder : - mMainWindow->actionDelete->setIcon(QIcon(":/images/trash.png")); - mMainWindow->actionDelete->setText(tr("Empty")); + if (firstSelectedTreeIndex() == docModel->myDocumentsIndex()) + { + mMainWindow->actionDelete->setIcon(QIcon(":/images/trash-my-documents.png")); + mMainWindow->actionDelete->setText(tr("Empty")); + } + else + { + mMainWindow->actionDelete->setIcon(QIcon(":/images/trash-folder.png")); + mMainWindow->actionDelete->setText(tr("Delete")); + } break; case EmptyTrash : - mMainWindow->actionDelete->setIcon(QIcon(":/images/toolbar/deleteDocument.png")); + mMainWindow->actionDelete->setIcon(QIcon(":/images/trash-empty.png")); mMainWindow->actionDelete->setText(tr("Empty")); break; + case NoDeletion : + default: + if (mSelectionType == Folder) + { + mMainWindow->actionDelete->setIcon(QIcon(":/images/trash-delete-folder.png")); + mMainWindow->actionDelete->setText(tr("Delete")); + } + else if (mSelectionType == Document) + { + mMainWindow->actionDelete->setIcon(QIcon(":/images/trash-delete-document.png")); + mMainWindow->actionDelete->setText(tr("Delete")); + } + else if (mSelectionType == Page) + { + mMainWindow->actionDelete->setIcon(QIcon(":/images/trash-document-page.png")); + mMainWindow->actionDelete->setText(tr("Delete")); + } + break; } mMainWindow->actionDocumentAdd->setEnabled((docSelected || pageSelected) && !trashSelected); @@ -3445,7 +3647,12 @@ void UBDocumentController::deletePages(QList itemsToDelete) } } UBDocumentContainer::deletePages(sceneIndexes); - emit UBApplication::boardController->documentThumbnailsUpdated(this); + if (mBoardController->selectedDocument() == selectedDocument()) + { + std::sort(sceneIndexes.begin(), sceneIndexes.end(), std::greater<>()); + for (auto index : sceneIndexes) + mBoardController->deleteThumbPage(index); + } proxy->setMetaData(UBSettings::documentUpdatedAt, UBStringUtils::toUtcIsoDateTime(QDateTime::currentDateTime())); UBMetadataDcSubsetAdaptor::persist(proxy); @@ -3462,7 +3669,6 @@ void UBDocumentController::deletePages(QList itemsToDelete) mDocumentUI->thumbnailWidget->selectItemAt(minIndex); mBoardController->setActiveDocumentScene(minIndex); - mBoardController->reloadThumbnails(); } } @@ -3514,7 +3720,12 @@ UBDocumentController::deletionTypeForSelection(LastSelectedElementType pTypeSele , UBDocumentTreeModel *docModel) const { - if (pTypeSelection == Page) { + if (pTypeSelection == Page) + { + if (everySceneSelected()) + { + return NoDeletion; + } if (!firstAndOnlySceneSelected()) { return DeletePage; } @@ -3541,11 +3752,24 @@ UBDocumentController::deletionTypeForSelection(LastSelectedElementType pTypeSele return NoDeletion; } +bool UBDocumentController::everySceneSelected() const +{ + QList selection = mDocumentUI->thumbnailWidget->selectedItems(); + if (selection.count() > 0) + { + UBSceneThumbnailPixmap* p = dynamic_cast(selection.at(0)); + if (p) + { + return (selection.count() == p->proxy()->pageCount()); + } + } + return false; +} + bool UBDocumentController::firstAndOnlySceneSelected() const { - bool firstSceneSelected = false; QList selection = mDocumentUI->thumbnailWidget->selectedItems(); - for(int i = 0; i < selection.count() && !firstSceneSelected; i += 1) + for(int i = 0; i < selection.count(); i += 1) { UBSceneThumbnailPixmap* p = dynamic_cast(selection.at(i)); if (p) @@ -3568,7 +3792,7 @@ bool UBDocumentController::firstAndOnlySceneSelected() const return false; } -void UBDocumentController:: refreshDocumentThumbnailsView(UBDocumentContainer*) +void UBDocumentController:: refreshDocumentThumbnailsView(UBDocumentContainer* source) { UBDocumentTreeModel *docModel = UBPersistenceManager::persistenceManager()->mDocumentTreeStructureModel; UBDocumentProxy *currentDocumentProxy = selectedDocument(); @@ -3581,14 +3805,14 @@ void UBDocumentController:: refreshDocumentThumbnailsView(UBDocumentContainer*) , QList() , QStringList() , UBApplication::mimeTypeUniboardPage); + + QApplication::restoreOverrideCursor(); return; } - QList thumbs; - if (currentDocumentProxy) { - UBThumbnailAdaptor::load(currentDocumentProxy, thumbs); + UBThumbnailAdaptor::load(currentDocumentProxy, documentThumbs()); } QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); @@ -3604,7 +3828,9 @@ void UBDocumentController:: refreshDocumentThumbnailsView(UBDocumentContainer*) { for (int i = 0; i < currentDocumentProxy->pageCount(); i++) { - const QPixmap* pix = thumbs.at(i); + UBApplication::showMessage(tr("Refreshing Document Thumbnails View (%1/%2)").arg(i+1).arg(source->selectedDocument()->pageCount())); + + auto pix = documentThumbs().at(i); QGraphicsPixmapItem *pixmapItem = new UBSceneThumbnailPixmap(*pix, currentDocumentProxy, i); // deleted by the tree widget if (currentDocumentProxy == mBoardController->selectedDocument() && mBoardController->activeSceneIndex() == i) @@ -3635,11 +3861,9 @@ void UBDocumentController:: refreshDocumentThumbnailsView(UBDocumentContainer*) if (selection) { - disconnect(mDocumentUI->thumbnailWidget->scene(), SIGNAL(selectionChanged()), this, SLOT(pageSelectionChanged())); - UBSceneThumbnailPixmap *currentScene = dynamic_cast(selection); - if (currentScene) - mDocumentUI->thumbnailWidget->hightlightItem(currentScene->sceneIndex()); - connect(mDocumentUI->thumbnailWidget->scene(), SIGNAL(selectionChanged()), this, SLOT(pageSelectionChanged())); + UBSceneThumbnailPixmap *currentSceneThumbnailPixmap = dynamic_cast(selection); + if (currentSceneThumbnailPixmap) + mDocumentUI->thumbnailWidget->hightlightItem(currentSceneThumbnailPixmap->sceneIndex()); } QApplication::restoreOverrideCursor(); @@ -3657,6 +3881,8 @@ void UBDocumentController::createNewDocumentInUntitledFolder() if (document) pManager->mDocumentTreeStructureModel->markDocumentAsNew(document); + + pageSelectionChanged(); } void UBDocumentController::collapseAll() @@ -3692,3 +3918,9 @@ void UBDocumentController::expandAll() mDocumentUI->documentTreeView->setAnimated(true); } + +void UBDocumentController::clearThumbnailsSelection() +{ + mDocumentUI->thumbnailWidget->clearSelection(); + pageSelectionChanged(); +} diff --git a/src/document/UBDocumentController.h b/src/document/UBDocumentController.h index a67908be..03c76e92 100644 --- a/src/document/UBDocumentController.h +++ b/src/document/UBDocumentController.h @@ -210,6 +210,7 @@ public: QString virtualDirForIndex(const QModelIndex &pIndex) const; QString virtualPathForIndex(const QModelIndex &pIndex) const; QStringList nodeNameList(const QModelIndex &pIndex, bool distinctNodeType = false) const; + QList nodeChildrenFromIndex(const QModelIndex &pIndex) const; bool newNodeAllowed(const QModelIndex &pSelectedIndex) const; QModelIndex goTo(const QString &dir); bool inTrash(const QModelIndex &index) const; @@ -305,6 +306,7 @@ public slots: void hSliderRangeChanged(int min, int max); protected: + void mousePressEvent(QMouseEvent *event) override; void dragEnterEvent(QDragEnterEvent *event); void dragLeaveEvent(QDragLeaveEvent *event); void dragMoveEvent(QDragMoveEvent *event); @@ -321,6 +323,32 @@ private: void updateIndexEnvirons(const QModelIndex &index); }; +class UBValidator : public QValidator +{ + const QList mExistingNodes; + UBDocumentTreeNode::Type mEditedNodeType; + + public: + UBValidator(const QList existingNodes, UBDocumentTreeNode::Type editedNodeType, QObject *parent = nullptr) + : QValidator(parent) + , mExistingNodes(existingNodes) + , mEditedNodeType(editedNodeType) + { + + } + + QValidator::State validate(QString &input, int &pos) const + { + for (auto node : mExistingNodes) + { + if (node->nodeName() == input && node->nodeType() == mEditedNodeType) + return QValidator::Intermediate; + } + + return QValidator::Acceptable; + } +}; + class UBDocumentTreeItemDelegate : public QStyledItemDelegate { Q_OBJECT @@ -401,6 +429,7 @@ class UBDocumentController : public UBDocumentContainer , const QModelIndex &selectedIndex , UBDocumentTreeModel *docModel) const; bool firstAndOnlySceneSelected() const; + bool everySceneSelected() const; QWidget *mainWidget() const {return mDocumentWidget;} //issue 1629 - NNE - 20131212 @@ -429,6 +458,8 @@ class UBDocumentController : public UBDocumentContainer QModelIndex findNextSiblingNotSelected(const QModelIndex &index, QItemSelectionModel *selectionModel); bool parentIsSelected(const QModelIndex& child, QItemSelectionModel *selectionModel); + void clearThumbnailsSelection(); + signals: void exportDone(); void reorderDocumentsRequested(); @@ -461,7 +492,6 @@ class UBDocumentController : public UBDocumentContainer void copy(); void paste(); void focusChanged(QWidget *old, QWidget *current); - void updateActions(); void updateExportSubActions(const QModelIndex &selectedIndex); void currentIndexMoved(const QModelIndex &newIndex, const QModelIndex &PreviousIndex); @@ -472,6 +502,11 @@ class UBDocumentController : public UBDocumentContainer void collapseAll(); void expandAll(); + void updateThumbnail(int index); + void removeThumbnail(int index); + void moveThumbnail(int from, int to); + void insertThumbnail(int index, const QPixmap& pix); + protected: virtual void setupViews(); virtual void setupToolbar(); @@ -515,6 +550,8 @@ protected: void TreeViewSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous); void TreeViewSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + void pageSelectionChanged(); + private slots: void documentZoomSliderValueChanged (int value); void itemSelectionChanged(LastSelectedElementType newSelection); @@ -522,7 +559,7 @@ protected: void exportDocumentSet(); void thumbnailViewResized(); - void pageSelectionChanged(); + void updateActions(); void documentSceneChanged(UBDocumentProxy* proxy, int pSceneIndex); diff --git a/src/document/UBDocumentProxy.cpp b/src/document/UBDocumentProxy.cpp index 0bf6e565..6f9aa711 100644 --- a/src/document/UBDocumentProxy.cpp +++ b/src/document/UBDocumentProxy.cpp @@ -56,6 +56,7 @@ UBDocumentProxy::UBDocumentProxy(const UBDocumentProxy &rValue) : mPageCount = rValue.mPageCount; } + UBDocumentProxy::UBDocumentProxy(const QString& pPersistancePath) : mPageCount(0) , mPageDpi(0) @@ -67,17 +68,6 @@ UBDocumentProxy::UBDocumentProxy(const QString& pPersistancePath) } -UBDocumentProxy::UBDocumentProxy(const QString& pPersistancePath, QMap metadatas) - : mPageCount(0) - , mPageDpi(0) -{ - init(); - setPersistencePath(pPersistancePath); - - mMetaDatas = metadatas; -} - - void UBDocumentProxy::init() { setMetaData(UBSettings::documentGroupName, ""); diff --git a/src/document/UBDocumentProxy.h b/src/document/UBDocumentProxy.h index dfff6908..cb507dec 100644 --- a/src/document/UBDocumentProxy.h +++ b/src/document/UBDocumentProxy.h @@ -49,7 +49,6 @@ class UBDocumentProxy : public QObject UBDocumentProxy(); UBDocumentProxy(const UBDocumentProxy &rValue); UBDocumentProxy(const QString& pPersistencePath); - UBDocumentProxy(const QString& pPersistencePath, QMap metadatas); virtual ~UBDocumentProxy(); diff --git a/src/domain/UBGraphicsDelegateFrame.cpp b/src/domain/UBGraphicsDelegateFrame.cpp index 36334510..e0970eb5 100644 --- a/src/domain/UBGraphicsDelegateFrame.cpp +++ b/src/domain/UBGraphicsDelegateFrame.cpp @@ -103,11 +103,10 @@ UBGraphicsDelegateFrame::UBGraphicsDelegateFrame(UBGraphicsItemDelegate* pDelega mRotateButton->setCursor(UBResources::resources()->rotateCursor); mRotateButton->setVisible(mDelegate->testUBFlags(GF_REVOLVABLE)); - updateResizeCursors(); - setAntiScale(1.0); positionHandles(); + updateResizeCursors(); this->setAcceptHoverEvents(true); } @@ -839,6 +838,7 @@ void UBGraphicsDelegateFrame::positionHandles() resetTransform(); setTransform(QTransform::fromTranslate(center.x(), center.y()), true); setTransform(QTransform().rotate(-angle), true); + mAngle = angle; setTransform(QTransform::fromTranslate(-center.x(), -center.y()), true); //TODO: combine these transforms into one diff --git a/src/domain/UBGraphicsItemDelegate.cpp b/src/domain/UBGraphicsItemDelegate.cpp index bb19e2ca..458f0115 100644 --- a/src/domain/UBGraphicsItemDelegate.cpp +++ b/src/domain/UBGraphicsItemDelegate.cpp @@ -328,12 +328,15 @@ void UBGraphicsItemDelegate::postpaint(QPainter *painter, const QStyleOptionGrap { Q_UNUSED(widget) if (option->state & QStyle::State_Selected && !controlsExist()) { - painter->save(); - painter->setPen(Qt::NoPen); - painter->setBrush(QColor(0x88, 0x88, 0x88, 0x77)); - painter->drawRect(option->rect); + if (UBStylusTool::Play != UBDrawingController::drawingController()->stylusTool()) + { + painter->save(); + painter->setPen(Qt::NoPen); + painter->setBrush(QColor(0x88, 0x88, 0x88, 0x77)); + painter->drawRect(option->rect); - painter->restore(); + painter->restore(); + } } } @@ -1304,7 +1307,7 @@ void MediaTimer::setNumDigits(int numDigits) } else { if (numDigits == ndigits) // no change return; - register int i; + int i; int dif; if (numDigits > ndigits) { // expand dif = numDigits - ndigits; diff --git a/src/domain/UBGraphicsItemUndoCommand.cpp b/src/domain/UBGraphicsItemUndoCommand.cpp index 7653a054..8ad68b22 100644 --- a/src/domain/UBGraphicsItemUndoCommand.cpp +++ b/src/domain/UBGraphicsItemUndoCommand.cpp @@ -128,7 +128,7 @@ void UBGraphicsItemUndoCommand::undo() QGraphicsItem* item = itRemoved.next(); if (item) { - if (UBItemLayerType::FixedBackground == item->data(UBGraphicsItemData::ItemLayerType)) + if (itemLayerType::BackgroundItem == item->data(UBGraphicsItemData::itemLayerType)) mScene->setAsBackgroundObject(item); else mScene->addItem(item); @@ -246,7 +246,11 @@ void UBGraphicsItemUndoCommand::redo() polygonItem->strokesGroup()->removeFromGroup(polygonItem); } - mScene->removeItem(item); + + if (itemLayerType::BackgroundItem == item->data(UBGraphicsItemData::itemLayerType)) + mScene->setAsBackgroundObject(nullptr); + else + mScene->removeItem(item); if (bApplyTransform) item->setTransform(t); diff --git a/src/domain/UBGraphicsMediaItem.cpp b/src/domain/UBGraphicsMediaItem.cpp index 47b025cb..8ec7da2e 100644 --- a/src/domain/UBGraphicsMediaItem.cpp +++ b/src/domain/UBGraphicsMediaItem.cpp @@ -68,6 +68,7 @@ UBGraphicsMediaItem::UBGraphicsMediaItem(const QUrl& pMediaFileUrl, QGraphicsIte , mMuted(sIsMutedByDefault) , mMutedByUserAction(sIsMutedByDefault) , mStopped(false) + , mFirstLoad(true) , mMediaFileUrl(pMediaFileUrl) , mLinkedImage(NULL) , mInitialPos(0) @@ -135,8 +136,8 @@ UBGraphicsVideoItem::UBGraphicsVideoItem(const QUrl &pMediaFileUrl, QGraphicsIte * active scene has changed, or when the item is first created. * If and when Qt fix this issue, this should be changed back. * */ - //mMediaObject->setVideoOutput(mVideoItem); - mHasVideoOutput = false; + mMediaObject->setVideoOutput(mVideoItem); + mHasVideoOutput = true; mMediaObject->setNotifyInterval(50); @@ -175,12 +176,12 @@ QVariant UBGraphicsMediaItem::itemChange(GraphicsItemChange change, const QVaria || (change == QGraphicsItem::ItemVisibleChange)) { if (mMediaObject && (!isEnabled() || !isVisible() || !scene())) - mMediaObject->pause(); + pause(); } else if (change == QGraphicsItem::ItemSceneHasChanged) { if (!scene()) - mMediaObject->stop(); + stop(); else { QString absoluteMediaFilename; @@ -225,6 +226,16 @@ bool UBGraphicsMediaItem::isStopped() const return mStopped; } +bool UBGraphicsMediaItem::firstLoad() const +{ + return mFirstLoad; +} + +void UBGraphicsMediaItem::setFirstLoad(bool firstLoad) +{ + mFirstLoad = firstLoad; +} + qint64 UBGraphicsMediaItem::mediaDuration() const { return mMediaObject->duration(); @@ -333,7 +344,7 @@ UBGraphicsScene* UBGraphicsMediaItem::scene() void UBGraphicsMediaItem::activeSceneChanged() { if (UBApplication::boardController->activeScene() != scene()) - mMediaObject->pause(); + pause(); } @@ -374,17 +385,19 @@ void UBGraphicsMediaItem::togglePlayPause() } if (mMediaObject->state() == QMediaPlayer::StoppedState) - mMediaObject->play(); + { + play(); + } else if (mMediaObject->state() == QMediaPlayer::PlayingState) { if ((mMediaObject->duration() - mMediaObject->position()) <= 0) { - mMediaObject->stop(); - mMediaObject->play(); + stop(); + play(); } else { - mMediaObject->pause(); + pause(); if(scene()) scene()->setModified(true); } @@ -392,14 +405,14 @@ void UBGraphicsMediaItem::togglePlayPause() else if (mMediaObject->state() == QMediaPlayer::PausedState) { if ((mMediaObject->duration() - mMediaObject->position()) <= 0) - mMediaObject->stop(); + stop(); - mMediaObject->play(); + play(); } else if ( mMediaObject->mediaStatus() == QMediaPlayer::LoadingMedia) { mMediaObject->setMedia(mediaFileUrl()); - mMediaObject->play(); + play(); } } @@ -427,6 +440,7 @@ void UBGraphicsMediaItem::mediaError(QMediaPlayer::Error errorCode) if (!mErrorString.isEmpty() ) { UBApplication::showMessage(mErrorString); qDebug() << mErrorString; + mErrorString.clear(); } } diff --git a/src/domain/UBGraphicsMediaItem.h b/src/domain/UBGraphicsMediaItem.h index 5f2ba757..5ec5eb08 100644 --- a/src/domain/UBGraphicsMediaItem.h +++ b/src/domain/UBGraphicsMediaItem.h @@ -88,6 +88,8 @@ public: bool isPlaying() const { return (mMediaObject->state() == QMediaPlayer::PlayingState); } bool isPaused() const { return (mMediaObject->state() == QMediaPlayer::PausedState); } bool isStopped() const; + bool firstLoad() const; + void setFirstLoad(bool firstLoad); QRectF boundingRect() const; @@ -140,6 +142,7 @@ protected: bool mMutedByUserAction; static bool sIsMutedByDefault; bool mStopped; + bool mFirstLoad; QUrl mMediaFileUrl; QString mMediaSource; diff --git a/src/domain/UBGraphicsMediaItemDelegate.cpp b/src/domain/UBGraphicsMediaItemDelegate.cpp index 4ab1a298..c510a59d 100644 --- a/src/domain/UBGraphicsMediaItemDelegate.cpp +++ b/src/domain/UBGraphicsMediaItemDelegate.cpp @@ -242,17 +242,26 @@ void UBGraphicsMediaItemDelegate::mediaStatusChanged(QMediaPlayer::MediaStatus s if (status == QMediaPlayer::LoadedMedia) mMediaControl->totalTimeChanged(delegated()->mediaDuration()); - // At the beginning of the video, play/pause to load and display the first frame + // At the beginning of the video, play/pause to load and display the first frame (not working on OSX) +#ifndef Q_OS_OSX if ((status == QMediaPlayer::LoadedMedia || status == QMediaPlayer::BufferedMedia) && delegated()->mediaPosition() == delegated()->initialPos() - && !delegated()->isStopped()) { + && !delegated()->isStopped() + && delegated()->firstLoad() + ) + { delegated()->play(); delegated()->pause(); + delegated()->setFirstLoad(false); } +#endif // At the end of the video, make sure the progress bar doesn't autohide if (status == QMediaPlayer::EndOfMedia) + { + delegated()->setFirstLoad(true); showToolBar(false); + } // in most cases, the only necessary action is to update the play/pause state @@ -265,6 +274,9 @@ void UBGraphicsMediaItemDelegate::mediaStateChanged(QMediaPlayer::State state) // Possible states are StoppedState, PlayingState and PausedState // updatePlayPauseState handles this functionality + if (state == QMediaPlayer::StoppedState) + delegated()->setMediaPos(0); + updatePlayPauseState(); } @@ -280,8 +292,11 @@ void UBGraphicsMediaItemDelegate::updatePlayPauseState() void UBGraphicsMediaItemDelegate::updateTicker(qint64 time) { - mMediaControl->totalTimeChanged(delegated()->mediaDuration()); - mMediaControl->updateTicker(time); + if (!delegated()->isStopped()) + { + mMediaControl->totalTimeChanged(delegated()->mediaDuration()); + mMediaControl->updateTicker(time); + } } diff --git a/src/domain/UBGraphicsScene.cpp b/src/domain/UBGraphicsScene.cpp index 327b43c2..1f82c089 100644 --- a/src/domain/UBGraphicsScene.cpp +++ b/src/domain/UBGraphicsScene.cpp @@ -840,6 +840,8 @@ void UBGraphicsScene::drawMarkerCircle(const QPointF &pPoint) void UBGraphicsScene::drawPenCircle(const QPointF &pPoint) { + QCursor cursor; + if (mPenCircle && UBSettings::settings()->showPenPreviewCircle->get().toBool() && UBSettings::settings()->currentPenWidth() >= UBSettings::settings()->penPreviewFromSize->get().toInt()) { qreal penDiameter = UBSettings::settings()->currentPenWidth(); @@ -849,20 +851,20 @@ void UBGraphicsScene::drawPenCircle(const QPointF &pPoint) mPenCircle->setRect(QRectF(pPoint.x() - penRadius, pPoint.y() - penRadius, penDiameter, penDiameter)); - - if (controlView()) - if (controlView()->viewport()) - controlView()->viewport()->setCursor(QCursor (Qt::BlankCursor)); - mPenCircle->show(); + cursor = Qt::BlankCursor; } else { - if (controlView()) - if (controlView()->viewport()) - controlView()->viewport()->setCursor(UBResources::resources()->penCursor); + cursor = UBResources::resources()->penCursor; } + if (!UBDrawingController::drawingController()->mActiveRuler) + { + // set cursor only if no active ruler + if (controlView() && controlView()->viewport()) + controlView()->viewport()->setCursor(cursor); + } } void UBGraphicsScene::hideMarkerCircle() @@ -1335,9 +1337,12 @@ void UBGraphicsScene::updateSelectionFrame() mSelectionFrame->setEnclosedItems(QList()); UBGraphicsItemDelegate *itemDelegate = UBGraphicsItem::Delegate(selItems.first()); - itemDelegate->createControls(); - selItems.first()->setVisible(true); - itemDelegate->showControls(); + if (itemDelegate) + { + itemDelegate->createControls(); + selItems.first()->setVisible(true); + itemDelegate->showControls(); + } } break; default: { @@ -1473,6 +1478,7 @@ void UBGraphicsScene::clearContent(clearCase pCase) if(mBackgroundObject){ removeItem(mBackgroundObject); removedItems << mBackgroundObject; + mBackgroundObject = nullptr; } break; @@ -1930,9 +1936,12 @@ UBGraphicsTextItem* UBGraphicsScene::addTextWithFont(const QString& pString, con UBGraphicsTextItem *UBGraphicsScene::addTextHtml(const QString &pString, const QPointF& pTopLeft) { UBGraphicsTextItem *textItem = new UBGraphicsTextItem(); + textItem->setPlainText(""); textItem->setHtml(UBTextTools::cleanHtml(pString)); + textItem->initFontProperties(); + addItem(textItem); textItem->show(); @@ -2377,6 +2386,9 @@ void UBGraphicsScene::changeMagnifierMode(int mode) { if(magniferControlViewWidget) magniferControlViewWidget->setDrawingMode(mode); + + if(magniferDisplayViewWidget) + magniferDisplayViewWidget->setDrawingMode(mode); } void UBGraphicsScene::resizedMagnifier(qreal newPercent) @@ -2463,9 +2475,53 @@ void UBGraphicsScene::setRenderingQuality(UBItem::RenderingQuality pRenderingQua } } +QList UBGraphicsScene::relativeDependenciesOfItem(QGraphicsItem* item) const +{ + QList relativePaths; + + UBGraphicsVideoItem *videoItem = dynamic_cast (item); + if (videoItem){ + QString completeFileName = QFileInfo(videoItem->mediaFileUrl().toLocalFile()).fileName(); + QString path = UBPersistenceManager::videoDirectory + "/"; + relativePaths << QUrl(path + completeFileName); + return relativePaths; + } + + UBGraphicsAudioItem *audioItem = dynamic_cast (item); + if (audioItem){ + QString completeFileName = QFileInfo(audioItem->mediaFileUrl().toLocalFile()).fileName(); + QString path = UBPersistenceManager::audioDirectory + "/"; + relativePaths << QUrl(path + completeFileName); + return relativePaths; + } + + UBGraphicsWidgetItem* widget = dynamic_cast(item); + if(widget){ + QString widgetPath = UBPersistenceManager::widgetDirectory + "/" + widget->uuid().toString() + ".wgt"; + QString screenshotPath = UBPersistenceManager::widgetDirectory + "/" + widget->uuid().toString().remove("{").remove("}") + ".png"; + relativePaths << QUrl(widgetPath); + relativePaths << QUrl(screenshotPath); + return relativePaths; + } + + UBGraphicsPixmapItem* pixmapItem = dynamic_cast(item); + if(pixmapItem){ + relativePaths << QUrl(UBPersistenceManager::imageDirectory + "/" + pixmapItem->uuid().toString() + ".png"); + return relativePaths; + } + + UBGraphicsSvgItem* svgItem = dynamic_cast(item); + if(svgItem){ + relativePaths << QUrl(UBPersistenceManager::imageDirectory + "/" + svgItem->uuid().toString() + ".svg"); + return relativePaths; + } + + return relativePaths; +} + QList UBGraphicsScene::relativeDependencies() const { - QList relativePathes; + QList relativePaths; QListIterator itItems(mFastAccessItems); @@ -2473,45 +2529,19 @@ QList UBGraphicsScene::relativeDependencies() const { QGraphicsItem* item = itItems.next(); - UBGraphicsVideoItem *videoItem = qgraphicsitem_cast (item); - if (videoItem){ - QString completeFileName = QFileInfo(videoItem->mediaFileUrl().toLocalFile()).fileName(); - QString path = UBPersistenceManager::videoDirectory + "/"; - relativePathes << QUrl(path + completeFileName); - continue; - } - - UBGraphicsAudioItem *audioItem = qgraphicsitem_cast (item); - if (audioItem){ - QString completeFileName = QFileInfo(audioItem->mediaFileUrl().toLocalFile()).fileName(); - QString path = UBPersistenceManager::audioDirectory + "/"; - relativePathes << QUrl(path + completeFileName); - continue; - } - - UBGraphicsWidgetItem* widget = qgraphicsitem_cast(item); - if(widget){ - QString widgetPath = UBPersistenceManager::widgetDirectory + "/" + widget->uuid().toString() + ".wgt"; - QString screenshotPath = UBPersistenceManager::widgetDirectory + "/" + widget->uuid().toString().remove("{").remove("}") + ".png"; - relativePathes << QUrl(widgetPath); - relativePathes << QUrl(screenshotPath); - continue; - } - - UBGraphicsPixmapItem* pixmapItem = qgraphicsitem_cast(item); - if(pixmapItem){ - relativePathes << QUrl(UBPersistenceManager::imageDirectory + "/" + pixmapItem->uuid().toString() + ".png"); - continue; - } - - UBGraphicsSvgItem* svgItem = qgraphicsitem_cast(item); - if(svgItem){ - relativePathes << QUrl(UBPersistenceManager::imageDirectory + "/" + svgItem->uuid().toString() + ".svg"); - continue; + UBGraphicsGroupContainerItem* groupItem = dynamic_cast(item); + if(groupItem) + { + for (auto child : groupItem->childItems()) + { + relativePaths << relativeDependenciesOfItem(child); + } } + else + relativePaths << relativeDependenciesOfItem(item); } - return relativePathes; + return relativePaths; } QSize UBGraphicsScene::nominalSize() diff --git a/src/domain/UBGraphicsScene.h b/src/domain/UBGraphicsScene.h index 6a22f14f..82e3a982 100644 --- a/src/domain/UBGraphicsScene.h +++ b/src/domain/UBGraphicsScene.h @@ -303,6 +303,7 @@ class UBGraphicsScene: public UBCoreGraphicsScene, public UBItem virtual void setRenderingQuality(UBItem::RenderingQuality pRenderingQuality, UBItem::CacheBehavior cacheBehavior); + QList relativeDependenciesOfItem(QGraphicsItem* item) const; QList relativeDependencies() const; QSize nominalSize(); diff --git a/src/domain/UBGraphicsTextItem.cpp b/src/domain/UBGraphicsTextItem.cpp index 5a96dff6..7e850169 100644 --- a/src/domain/UBGraphicsTextItem.cpp +++ b/src/domain/UBGraphicsTextItem.cpp @@ -81,6 +81,64 @@ UBGraphicsTextItem::~UBGraphicsTextItem() { } +void UBGraphicsTextItem::initFontProperties() +{ + QTextCursor curCursor = textCursor(); + QTextCharFormat format; + QFont font(createDefaultFont()); + + font.setPointSize(UBSettings::settings()->fontPointSize()); + format.setFont(font); + if (UBSettings::settings()->isDarkBackground()) + { + if (UBGraphicsTextItem::lastUsedTextColor == Qt::black) + UBGraphicsTextItem::lastUsedTextColor = Qt::white; + } + else + { + if (UBGraphicsTextItem::lastUsedTextColor == Qt::white) + UBGraphicsTextItem::lastUsedTextColor = Qt::black; + } + + setDefaultTextColor(UBGraphicsTextItem::lastUsedTextColor); + format.setForeground(QBrush(UBGraphicsTextItem::lastUsedTextColor)); + curCursor.mergeCharFormat(format); + setTextCursor(curCursor); + setFont(font); + + adjustSize(); + contentsChanged(); +} + +QFont UBGraphicsTextItem::createDefaultFont() +{ + QFont font; + + QString fFamily = UBSettings::settings()->fontFamily(); + if (!fFamily.isEmpty()) + font.setFamily(fFamily); + + QString fStyleName = UBSettings::settings()->fontStyleName(); + if (!fStyleName .isEmpty()) + font.setStyleName(fStyleName); + + bool bold = UBSettings::settings()->isBoldFont(); + if (bold) + font.setWeight(QFont::Bold); + + bool italic = UBSettings::settings()->isItalicFont(); + if (italic) + font.setItalic(true); + + + int pointSize = UBSettings::settings()->fontPointSize(); + if (pointSize > 0) { + font.setPointSize(pointSize); + } + + return font; +} + void UBGraphicsTextItem::recolor() { UBGraphicsTextItemDelegate * del = dynamic_cast(Delegate()); diff --git a/src/domain/UBGraphicsTextItem.h b/src/domain/UBGraphicsTextItem.h index 584a593c..8c51adef 100644 --- a/src/domain/UBGraphicsTextItem.h +++ b/src/domain/UBGraphicsTextItem.h @@ -100,6 +100,8 @@ class UBGraphicsTextItem : public QGraphicsTextItem, public UBItem, public UBRes void activateTextEditor(bool activate); void setSelected(bool selected); void recolor(); + void initFontProperties(); + QFont createDefaultFont(); QString mTypeTextHereLabel; diff --git a/src/domain/UBGraphicsTextItemDelegate.cpp b/src/domain/UBGraphicsTextItemDelegate.cpp index a385b3ac..b925ae49 100644 --- a/src/domain/UBGraphicsTextItemDelegate.cpp +++ b/src/domain/UBGraphicsTextItemDelegate.cpp @@ -110,32 +110,6 @@ UBGraphicsTextItemDelegate::UBGraphicsTextItemDelegate(UBGraphicsTextItem* pDele , delta(5) { delegated()->setData(UBGraphicsItemData::ItemEditable, QVariant(true)); - delegated()->setPlainText(""); - - QTextCursor curCursor = delegated()->textCursor(); - QTextCharFormat format; - QFont font(createDefaultFont()); - - font.setPointSize(UBSettings::settings()->fontPointSize()); - format.setFont(font); - if (UBSettings::settings()->isDarkBackground()) - { - if (UBGraphicsTextItem::lastUsedTextColor == Qt::black) - UBGraphicsTextItem::lastUsedTextColor = Qt::white; - } - else - { - if (UBGraphicsTextItem::lastUsedTextColor == Qt::white) - UBGraphicsTextItem::lastUsedTextColor = Qt::black; - } - delegated()->setDefaultTextColor(UBGraphicsTextItem::lastUsedTextColor); - format.setForeground(QBrush(UBGraphicsTextItem::lastUsedTextColor)); - curCursor.mergeCharFormat(format); - delegated()->setTextCursor(curCursor); - delegated()->setFont(font); - - delegated()->adjustSize(); - delegated()->contentsChanged(); connect(delegated()->document(), SIGNAL(cursorPositionChanged(QTextCursor)), this, SLOT(onCursorPositionChanged(QTextCursor))); connect(delegated()->document(), SIGNAL(modificationChanged(bool)), this, SLOT(onModificationChanged(bool))); @@ -146,31 +120,6 @@ UBGraphicsTextItemDelegate::~UBGraphicsTextItemDelegate() // NOOP } -QFont UBGraphicsTextItemDelegate::createDefaultFont() -{ - QTextCharFormat textFormat; - - QString fFamily = UBSettings::settings()->fontFamily(); - if (!fFamily.isEmpty()) - textFormat.setFontFamily(fFamily); - - bool bold = UBSettings::settings()->isBoldFont(); - if (bold) - textFormat.setFontWeight(QFont::Bold); - - bool italic = UBSettings::settings()->isItalicFont(); - if (italic) - textFormat.setFontItalic(true); - - QFont font(fFamily, -1, bold ? QFont::Bold : -1, italic); - int pointSize = UBSettings::settings()->fontPointSize(); - if (pointSize > 0) { - font.setPointSize(pointSize); - } - - return font; -} - void UBGraphicsTextItemDelegate::createControls() { UBGraphicsItemDelegate::createControls(); @@ -336,6 +285,7 @@ void UBGraphicsTextItemDelegate::pickFont() QFontDialog fontDialog(static_cast(UBApplication::boardController->controlView())); fontDialog.setOption(QFontDialog::DontUseNativeDialog); + fontDialog.setCurrentFont(delegated()->textCursor().charFormat().font()); customize(fontDialog); @@ -343,6 +293,7 @@ void UBGraphicsTextItemDelegate::pickFont() { QFont selectedFont = fontDialog.selectedFont(); UBSettings::settings()->setFontFamily(selectedFont.family()); + UBSettings::settings()->setFontStyleName(selectedFont.styleName()); UBSettings::settings()->setBoldFont(selectedFont.bold()); UBSettings::settings()->setItalicFont(selectedFont.italic()); UBSettings::settings()->setFontPointSize(selectedFont.pointSize()); diff --git a/src/domain/UBGraphicsTextItemDelegate.h b/src/domain/UBGraphicsTextItemDelegate.h index 76aaf8f3..30c64849 100644 --- a/src/domain/UBGraphicsTextItemDelegate.h +++ b/src/domain/UBGraphicsTextItemDelegate.h @@ -158,7 +158,6 @@ class UBGraphicsTextItemDelegate : public UBGraphicsItemDelegate void restoreTextCursorFormats(); - QFont createDefaultFont(); QAction *mEditableAction; struct selectionData_t { selectionData_t() diff --git a/src/frameworks/UBFileSystemUtils.cpp b/src/frameworks/UBFileSystemUtils.cpp index 6c5ae6d6..42915957 100644 --- a/src/frameworks/UBFileSystemUtils.cpp +++ b/src/frameworks/UBFileSystemUtils.cpp @@ -105,6 +105,11 @@ bool UBFileSystemUtils::copyFile(const QString &source, const QString &destinati if (QFileInfo(normalizedDestination).isFile() && overwrite) { QFile::remove(normalizedDestination); } + else + { + if (!overwrite) + return true; // don't try to copy an existing file if overwrite is false + } } else { normalizedDestination = normalizedDestination.replace(QString("\\"), QString("/")); int pos = normalizedDestination.lastIndexOf("/"); @@ -300,7 +305,7 @@ bool UBFileSystemUtils::copyDir(const QString& pSourceDirPath, const QString& pT { if (dirContent.isDir()) { - successSoFar = copyDir(pSourceDirPath + "/" + dirContent.fileName(), pTargetDirPath + "/" + dirContent.fileName()); + successSoFar = copyDir(pSourceDirPath + "/" + dirContent.fileName(), pTargetDirPath + "/" + dirContent.fileName(), overwite); } else { diff --git a/src/frameworks/UBPlatformUtils.cpp b/src/frameworks/UBPlatformUtils.cpp index b5335f8f..74a17b95 100644 --- a/src/frameworks/UBPlatformUtils.cpp +++ b/src/frameworks/UBPlatformUtils.cpp @@ -66,7 +66,9 @@ UBKeyboardLocale::~UBKeyboardLocale() int UBPlatformUtils::nKeyboardLayouts; UBKeyboardLocale** UBPlatformUtils::keyboardLayouts; - +#ifdef Q_OS_OSX +bool UBPlatformUtils::errorOpeningVirtualKeyboard = false; +#endif UBKeyboardLocale** UBPlatformUtils::getKeyboardLayouts(int& nCount) { nCount = nKeyboardLayouts; diff --git a/src/frameworks/UBPlatformUtils.h b/src/frameworks/UBPlatformUtils.h index 8e6b91ed..6d8ad70c 100644 --- a/src/frameworks/UBPlatformUtils.h +++ b/src/frameworks/UBPlatformUtils.h @@ -213,6 +213,8 @@ public: #ifdef Q_OS_OSX static void SetMacLocaleByIdentifier(const QString& id); static void toggleFinder(const bool on); + + static bool errorOpeningVirtualKeyboard; #endif }; diff --git a/src/frameworks/UBPlatformUtils_mac.mm b/src/frameworks/UBPlatformUtils_mac.mm index ae5c5956..3138861f 100644 --- a/src/frameworks/UBPlatformUtils_mac.mm +++ b/src/frameworks/UBPlatformUtils_mac.mm @@ -31,6 +31,7 @@ #include "core/UBApplication.h" #include "core/UBSettings.h" #include "frameworks/UBFileSystemUtils.h" +#include "gui/UBMainWindow.h" #include @@ -622,35 +623,109 @@ void UBPlatformUtils::showFullScreen(QWidget *pWidget) void UBPlatformUtils::showOSK(bool show) { - @autoreleasepool { - CFDictionaryRef properties = (CFDictionaryRef)[NSDictionary - dictionaryWithObject: @"com.apple.KeyboardViewer" - forKey: (NSString *)kTISPropertyInputSourceID]; + if (QOperatingSystemVersion::current().majorVersion() == 10 && QOperatingSystemVersion::current().minorVersion() < 15) /* < Catalina */ + { + @autoreleasepool { + CFDictionaryRef properties = (CFDictionaryRef)[NSDictionary + dictionaryWithObject: @"com.apple.KeyboardViewer" + forKey: (NSString *)kTISPropertyInputSourceID]; + + NSArray *sources = (NSArray *)TISCreateInputSourceList(properties, true); - NSArray *sources = (NSArray *)TISCreateInputSourceList(properties, true); + if ([sources count] > 0) { + TISInputSourceRef osk = (TISInputSourceRef)[sources objectAtIndex: 0]; - if ([sources count] > 0) { - TISInputSourceRef osk = (TISInputSourceRef)[sources objectAtIndex: 0]; + OSStatus result; + if (show) { + TISEnableInputSource(osk); + result = TISSelectInputSource(osk); + } + else { + TISDisableInputSource(osk); + result = TISDeselectInputSource(osk); + } - OSStatus result; - if (show) { - TISEnableInputSource(osk); - result = TISSelectInputSource(osk); + if (result == paramErr) { + qWarning() << "Unable to select input source"; + UBApplication::showMessage(tr("Unable to activate system on-screen keyboard")); + } } + else { - TISDisableInputSource(osk); - result = TISDeselectInputSource(osk); + qWarning() << "System OSK not found"; + UBApplication::showMessage(tr("System on-screen keyboard not found")); } + } + } + else + { + NSString *source = + @"tell application \"System Events\"\n\ + if application process \"TextInputMenuAgent\" exists then\n\ + tell application process \"TextInputMenuAgent\"\n\ + tell menu bar item 1 of menu bar 2\n\ + ignoring application responses\n\ + click\n\ + delay 0.5\n\ + end ignoring\n\ + end tell\n\ + end tell\n\ + end if\n\ + end tell\n\ + do shell script \"killall 'System Events'\"\n"; + + source = [source stringByAppendingString:@"if application \"Assistive Control\" is"]; + + if (show) + { + source = [source stringByAppendingString:@" not"]; + } + + source = [source stringByAppendingString:@" running then\n\ + tell application \"System Events\"\n\ + tell application process \"TextInputMenuAgent\"\n\ + tell menu 1 of menu bar item 1 of menu bar 2\n\ + set nbItems to count menu items\n\ + if (nbItems = 4)\n\ + -- only one language so items are\n\ + -- 1. emojis&symbols n-2. keyboard n-1. separator n.preferences\n\ + click menu item (nbItems-2)\n\ + else\n\ + -- items are ... n-4. access keyboard n-3. separator n-2 display names n-1. separator n. preferences\n\ + -- target is in fourth position from bottom\n\ + click menu item (nbItems - 4)\n\ + end if\n\ + end tell\n\ + end tell\n\ + end tell\n\ + end if"]; + + NSAppleScript *script = [[[NSAppleScript alloc] initWithSource:source] autorelease]; + NSDictionary *errorInfo = nil; + [script executeAndReturnError:&errorInfo]; + + if(errorInfo!=nil) + { + errorOpeningVirtualKeyboard = true; - if (result == paramErr) { - qWarning() << "Unable to select input source"; - UBApplication::showMessage(tr("Unable to activate system on-screen keyboard")); + NSAlert *alert = [[NSAlert alloc] init]; + + if (alert != nil) + { + alert.messageText = errorInfo.allValues[0]; + [alert runModal]; + [alert release]; + + //restore action state to previous one as it failed + if (show) + UBApplication::mainWindow->actionVirtualKeyboard->setChecked(false); + else + UBApplication::mainWindow->actionVirtualKeyboard->setChecked(true); } } - - else { - qWarning() << "System OSK not found"; - UBApplication::showMessage(tr("System on-screen keyboard not found")); + else + { + errorOpeningVirtualKeyboard = false; } } } diff --git a/src/gui/UBBoardThumbnailsView.cpp b/src/gui/UBBoardThumbnailsView.cpp index fe10ecca..a6f9ba4e 100644 --- a/src/gui/UBBoardThumbnailsView.cpp +++ b/src/gui/UBBoardThumbnailsView.cpp @@ -307,7 +307,7 @@ void UBBoardThumbnailsView::dragMoveEvent(QDragMoveEvent *event) { y = item->pos().y() - UBSettings::thumbnailSpacing / 2; if (mDropBar->y() != y) - mDropBar->setRect(QRectF(item->pos().x(), y, mThumbnailWidth-verticalScrollBar()->width(), 3)); + mDropBar->setRect(QRectF(item->pos().x(), y, (item->boundingRect().width()-verticalScrollBar()->width())*scale, 3)); } } else @@ -316,7 +316,7 @@ void UBBoardThumbnailsView::dragMoveEvent(QDragMoveEvent *event) { y = item->pos().y() + item->boundingRect().height() * scale + UBSettings::thumbnailSpacing / 2; if (mDropBar->y() != y) - mDropBar->setRect(QRectF(item->pos().x(), y, mThumbnailWidth-verticalScrollBar()->width(), 3)); + mDropBar->setRect(QRectF(item->pos().x(), y, (item->boundingRect().width()-verticalScrollBar()->width())*scale, 3)); } } } diff --git a/src/gui/UBDocumentNavigator.cpp b/src/gui/UBDocumentNavigator.cpp index 1fa0a6db..73bf9724 100644 --- a/src/gui/UBDocumentNavigator.cpp +++ b/src/gui/UBDocumentNavigator.cpp @@ -83,7 +83,10 @@ UBDocumentNavigator::UBDocumentNavigator(QWidget *parent, const char *name):QGra setFrameShadow(QFrame::Plain); connect(UBApplication::boardController, SIGNAL(documentThumbnailsUpdated(UBDocumentContainer*)), this, SLOT(generateThumbnails(UBDocumentContainer*))); - connect(UBApplication::boardController, SIGNAL(documentPageUpdated(int)), this, SLOT(updateSpecificThumbnail(int))); + connect(UBApplication::boardController, SIGNAL(documentPageInserted(int)), this, SLOT(insertThumbnail(int))); + connect(UBApplication::boardController, SIGNAL(documentPageUpdated(int)), this, SLOT(updateThumbnail(int))); + connect(UBApplication::boardController, SIGNAL(documentPageRemoved(int)), this, SLOT(removeThumbnail(int))); + connect(UBApplication::boardController, SIGNAL(documentPageMoved(int, int)), this, SLOT(moveThumbnail(int, int))); connect(UBApplication::boardController, SIGNAL(pageSelectionChanged(int)), this, SLOT(onScrollToSelectedPage(int))); connect(&mLongPressTimer, SIGNAL(timeout()), this, SLOT(longPressTimeout()), Qt::UniqueConnection); @@ -133,29 +136,53 @@ void UBDocumentNavigator::generateThumbnails(UBDocumentContainer* source) for(int i = 0; i < source->selectedDocument()->pageCount(); i++) { - //claudio This is a very bad hack and shows a architectural problem - // source->selectedDocument()->pageCount() != source->pageCount() - if(i>=source->pageCount() || !source->pageAt(i)) - source->insertThumbPage(i); + UBApplication::showMessage(tr("Generating thumbnails for board (%1/%2)").arg(i+1).arg(source->selectedDocument()->pageCount())); - const QPixmap* pix = source->pageAt(i); - Q_ASSERT(!pix->isNull()); - int pageIndex = UBDocumentContainer::pageFromSceneIndex(i); + bool found = false; + if (UBApplication::documentController) + { + if (UBApplication::documentController->selectedDocument() == source->selectedDocument()) + { - UBSceneThumbnailNavigPixmap* pixmapItem = new UBSceneThumbnailNavigPixmap(*pix, source->selectedDocument(), i); + if (UBApplication::documentController->pageAt(i)) + { + found = true; + //thumbnail has already been loaded on the documentController so we don't need to do it again + source->insertExistingThumbPage(i, UBApplication::documentController->pageAt(i)); + } + } + } - QString label = tr("Page %0").arg(pageIndex); - UBThumbnailTextItem *labelItem = new UBThumbnailTextItem(label); + if (!found) + { + //claudio This is a very bad hack and shows a architectural problem + // source->selectedDocument()->pageCount() != source->pageCount() + if(i>=source->pageCount() || !source->pageAt(i)) + { + source->insertThumbPage(i); + } + else + { + auto pix = source->pageAt(i); + Q_ASSERT(!pix->isNull()); + int pageIndex = UBDocumentContainer::pageFromSceneIndex(i); + + UBSceneThumbnailNavigPixmap* pixmapItem = new UBSceneThumbnailNavigPixmap(*pix, source->selectedDocument(), i); + + QString label = tr("Page %0").arg(pageIndex); + UBThumbnailTextItem *labelItem = new UBThumbnailTextItem(label); - UBImgTextThumbnailElement thumbWithText(pixmapItem, labelItem); - thumbWithText.setBorder(border()); - mThumbsWithLabels.append(thumbWithText); + UBImgTextThumbnailElement thumbWithText(pixmapItem, labelItem); + thumbWithText.setBorder(border()); + mThumbsWithLabels.append(thumbWithText); - if (lastClickedIndex == i) - mLastClickedThumbnail = pixmapItem; + if (lastClickedIndex == i) + mLastClickedThumbnail = pixmapItem; - mScene->addItem(pixmapItem); - mScene->addItem(labelItem); + mScene->addItem(pixmapItem); + mScene->addItem(labelItem); + } + } } if (selectedIndex >= 0 && selectedIndex < mThumbsWithLabels.count()) @@ -191,24 +218,83 @@ void UBDocumentNavigator::onScrollToSelectedPage(int index) * \brief Refresh the given thumbnail * @param iPage as the given page related thumbnail */ -void UBDocumentNavigator::updateSpecificThumbnail(int iPage) +void UBDocumentNavigator::updateThumbnail(int index) { - const QPixmap* pix = UBApplication::boardController->pageAt(iPage); - UBSceneThumbnailNavigPixmap* newItem = new UBSceneThumbnailNavigPixmap(*pix, UBApplication::boardController->selectedDocument(), iPage); + auto pix = UBApplication::boardController->pageAt(index); + UBSceneThumbnailNavigPixmap* newItem = new UBSceneThumbnailNavigPixmap(*pix, UBApplication::boardController->selectedDocument(), index); // Get the old thumbnail - UBSceneThumbnailNavigPixmap* oldItem = mThumbsWithLabels.at(iPage).getThumbnail(); - if(NULL != oldItem) + UBSceneThumbnailNavigPixmap* oldItem = mThumbsWithLabels.at(index).getThumbnail(); + if(oldItem) { + if (oldItem->isSelected()) + mScene->removeItem(oldItem->selectionItem()); mScene->removeItem(oldItem); mScene->addItem(newItem); - mThumbsWithLabels[iPage].setThumbnail(newItem); + mThumbsWithLabels[index].setThumbnail(newItem); if (mLastClickedThumbnail == oldItem) mLastClickedThumbnail = newItem; + if (mSelectedThumbnail == oldItem) + mSelectedThumbnail = newItem; delete oldItem; oldItem = NULL; } + refreshScene(); +} + +void UBDocumentNavigator::removeThumbnail(int index) +{ + //remove selectionItem + if (mThumbsWithLabels.at(index).getThumbnail()->isSelected()) + mScene->removeItem(mThumbsWithLabels.at(index).getThumbnail()->selectionItem()); + + mScene->removeItem(mThumbsWithLabels.at(index).getCaption()); + mScene->removeItem(mThumbsWithLabels.at(index).getThumbnail()); + + mThumbsWithLabels.removeAt(index); + + //update thumbs page number accordingly + for (int i=0; i < mThumbsWithLabels.length(); i++) + { + mThumbsWithLabels.at(i).getThumbnail()->setSceneIndex(i); + mThumbsWithLabels.at(i).getCaption()->setText(tr("Page %0").arg(i+1)); + } + + refreshScene(); +} + +void UBDocumentNavigator::insertThumbnail(int index) +{ + auto pix = UBApplication::boardController->pageAt(index); + UBSceneThumbnailNavigPixmap* pixmapItem = new UBSceneThumbnailNavigPixmap(*pix, UBApplication::boardController->selectedDocument(), index); + + QString label = tr("Page %0").arg(index+1); + UBThumbnailTextItem *labelItem = new UBThumbnailTextItem(label); + labelItem->setWidth(mThumbnailWidth); + + UBImgTextThumbnailElement thumbWithText(pixmapItem, labelItem); + thumbWithText.setBorder(border()); + + mThumbsWithLabels.insert(index, thumbWithText); + + if (mLastClickedThumbnail) + { + if (mLastClickedThumbnail->sceneIndex() == index) + mLastClickedThumbnail = pixmapItem; + } + + //sceneIndex is 0-based while pageNumber is 1-based + for (int i=0; i < mThumbsWithLabels.size(); i++) + { + mThumbsWithLabels.at(i).getThumbnail()->setSceneIndex(i); + mThumbsWithLabels.at(i).getCaption()->setPageNumber(i+1); + } + + mScene->addItem(pixmapItem); + mScene->addItem(labelItem); + + refreshScene(); } /** @@ -285,11 +371,11 @@ void UBDocumentNavigator::resizeEvent(QResizeEvent *event) // Update the thumbnails width mThumbnailWidth = (width() > mThumbnailMinWidth) ? width() - 2*border() : mThumbnailMinWidth; - if(mSelectedThumbnail) - ensureVisible(mSelectedThumbnail); - // Refresh the scene refreshScene(); + + if(mSelectedThumbnail) + ensureVisible(mSelectedThumbnail); } /** @@ -302,7 +388,6 @@ void UBDocumentNavigator::mousePressEvent(QMouseEvent *event) if (!event->isAccepted()) { - mLongPressTimer.start(); mLastPressedMousePos = event->pos(); mLastClickedThumbnail = clickedThumbnail(mLastPressedMousePos); @@ -314,6 +399,19 @@ void UBDocumentNavigator::mousePressEvent(QMouseEvent *event) UBApplication::boardController->setActiveDocumentScene(mLastClickedThumbnail->sceneIndex()); UBApplication::boardController->centerOn(UBApplication::boardController->activeScene()->lastCenter()); } + + mLongPressTimer.start(); + } +} + +void UBDocumentNavigator::clearSelection() +{ + for (auto item : mScene->items()) + { + if (item->type() == QGraphicsRectItem::Type && item != mDropBar) + { + mScene->removeItem(item); + } } } @@ -518,8 +616,6 @@ void UBDocumentNavigator::dragMoveEvent(QDragMoveEvent *event) int thumbnailHeight = mThumbnailWidth / UBSettings::minScreenRatio; QRectF thumbnailArea(0, scenePos.y() - thumbnailHeight/2, mThumbnailWidth, thumbnailHeight); - ensureVisible(thumbnailArea); - UBSceneThumbnailNavigPixmap* item = dynamic_cast(itemAt(position.toPoint())); if (item) { @@ -543,7 +639,7 @@ void UBDocumentNavigator::dragMoveEvent(QDragMoveEvent *event) { y = item->pos().y() - UBSettings::thumbnailSpacing / 2; if (mDropBar->y() != y) - mDropBar->setRect(QRectF(item->pos().x(), y, mThumbnailWidth-verticalScrollBar()->width(), 3)); + mDropBar->setRect(QRectF(item->pos().x(), y, (item->boundingRect().width()-verticalScrollBar()->width())*scale, 3)); } } else @@ -552,11 +648,14 @@ void UBDocumentNavigator::dragMoveEvent(QDragMoveEvent *event) { y = item->pos().y() + item->boundingRect().height() * scale + UBSettings::thumbnailSpacing / 2; if (mDropBar->y() != y) - mDropBar->setRect(QRectF(item->pos().x(), y, mThumbnailWidth-verticalScrollBar()->width(), 3)); + mDropBar->setRect(QRectF(item->pos().x(), y, (item->boundingRect().width()-verticalScrollBar()->width())*scale, 3)); } } } } + + ensureVisible(thumbnailArea); + event->acceptProposedAction(); } @@ -566,8 +665,12 @@ void UBDocumentNavigator::dropEvent(QDropEvent *event) if (mDropSource && mDropTarget) { - if (mDropSource->sceneIndex() != mDropTarget->sceneIndex()) - UBApplication::boardController->moveSceneToIndex(mDropSource->sceneIndex(), mDropTarget->sceneIndex()); + int sourceIndex = mDropSource->sceneIndex(); + int targetIndex = mDropTarget->sceneIndex(); + if (sourceIndex != targetIndex) + { + UBApplication::boardController->moveSceneToIndex(sourceIndex, targetIndex); + } } mDropSource = NULL; @@ -577,3 +680,17 @@ void UBDocumentNavigator::dropEvent(QDropEvent *event) mDropBar->setRect(QRectF()); mDropBar->hide(); } + +void UBDocumentNavigator::moveThumbnail(int from, int to) +{ + mThumbsWithLabels.move(from, to); + + //sceneIndex is 0-based while pageNumber is 1-based + for (int i=0; i < mThumbsWithLabels.size(); i++) + { + mThumbsWithLabels.at(i).getThumbnail()->setSceneIndex(i); + mThumbsWithLabels.at(i).getCaption()->setPageNumber(i+1); + } + + refreshScene(); +} diff --git a/src/gui/UBDocumentNavigator.h b/src/gui/UBDocumentNavigator.h index 9e29a2c9..1a252a6e 100644 --- a/src/gui/UBDocumentNavigator.h +++ b/src/gui/UBDocumentNavigator.h @@ -53,12 +53,16 @@ public: int nbColumns(); void setThumbnailMinWidth(int width); int thumbnailMinWidth(); + void clearSelection(); UBSceneThumbnailNavigPixmap* clickedThumbnail(const QPoint pos) const; public slots: void onScrollToSelectedPage(int index);// { if (mCrntItem) centerOn(mCrntItem); } void generateThumbnails(UBDocumentContainer* source); - void updateSpecificThumbnail(int iPage); + void insertThumbnail(int index); + void updateThumbnail(int index); + void removeThumbnail(int index); + void moveThumbnail(int from, int to); void longPressTimeout(); void mousePressAndHoldEvent(); diff --git a/src/gui/UBDocumentThumbnailWidget.cpp b/src/gui/UBDocumentThumbnailWidget.cpp index 44cf9ebe..aeb2f473 100644 --- a/src/gui/UBDocumentThumbnailWidget.cpp +++ b/src/gui/UBDocumentThumbnailWidget.cpp @@ -58,7 +58,6 @@ UBDocumentThumbnailWidget::~UBDocumentThumbnailWidget() // NOOP } - void UBDocumentThumbnailWidget::mouseMoveEvent(QMouseEvent *event) { if (!dragEnabled()) @@ -297,6 +296,64 @@ bool UBDocumentThumbnailWidget::dragEnabled() const return mDragEnabled; } +void UBDocumentThumbnailWidget::updateThumbnailPixmap(int index, const QPixmap& newThumbnail) +{ + if (index >= 0 && index < mGraphicItems.length()) + { + UBSceneThumbnailPixmap *thumbnail = dynamic_cast(mGraphicItems.at(index)); + if (thumbnail) + { + thumbnail->setPixmap(newThumbnail); + } + } +} + +void UBDocumentThumbnailWidget::removeThumbnail(int sceneIndex) +{ + if (sceneIndex >= 0 && sceneIndex < mGraphicItems.length()) + { + QGraphicsItem* thumbnailItem = mGraphicItems.at(sceneIndex); + QGraphicsItem* textItem = mLabelsItems.at(sceneIndex); + UBSceneThumbnailPixmap *thumbnail = dynamic_cast(thumbnailItem); + if (thumbnail) + { + if (thumbnail->isSelected()) + scene()->removeItem(thumbnail->selectionItem()); + + mGraphicItems.removeAt(sceneIndex); + if (thumbnailItem) + scene()->removeItem(thumbnailItem); + + mLabelsItems.removeAt(sceneIndex); + if (textItem) + scene()->removeItem(textItem); + } + refreshScene(); + } +} + +void UBDocumentThumbnailWidget::insertThumbnail(int index, QGraphicsPixmapItem* newThumbnail) +{ + if (!mGraphicItems.contains(newThumbnail)) //sometimes, refreshDocumentThumbnailsView is called before + { + auto thumbnailTextItem = new UBThumbnailTextItem(index); + mGraphicItems.insert(index, newThumbnail); + mLabelsItems.insert(index, thumbnailTextItem); + } + + refreshScene(); +} + +void UBDocumentThumbnailWidget::moveThumbnail(int from, int to) +{ + UBSceneThumbnailPixmap *thumbnail = dynamic_cast(mGraphicItems.at(from)); + if (thumbnail) + { + mGraphicItems.move(from, to); + } + refreshScene(); +} + void UBDocumentThumbnailWidget::hightlightItem(int index) { if (0 <= index && index < mLabelsItems.length()) diff --git a/src/gui/UBDocumentThumbnailWidget.h b/src/gui/UBDocumentThumbnailWidget.h index e08b464d..5ac9e620 100644 --- a/src/gui/UBDocumentThumbnailWidget.h +++ b/src/gui/UBDocumentThumbnailWidget.h @@ -48,9 +48,11 @@ class UBDocumentThumbnailWidget: public UBThumbnailWidget void hightlightItem(int index); public slots: - virtual void setGraphicsItems(const QList& pGraphicsItems, - const QList& pItemPaths, const QStringList pLabels = QStringList(), - const QString& pMimeType = QString("")); + void updateThumbnailPixmap(int index, const QPixmap& newThumbnail); + void removeThumbnail(int index); + void moveThumbnail(int from, int to); + void insertThumbnail(int index, QGraphicsPixmapItem *newThumbnail); + virtual void setGraphicsItems(const QList& pGraphicsItems, const QList& pItemPaths, const QStringList pLabels = QStringList(), const QString& pMimeType = QString("")); signals: void sceneDropped(UBDocumentProxy* proxy, int source, int target); diff --git a/src/gui/UBThumbnailWidget.cpp b/src/gui/UBThumbnailWidget.cpp index b31bdd1e..779974bc 100644 --- a/src/gui/UBThumbnailWidget.cpp +++ b/src/gui/UBThumbnailWidget.cpp @@ -67,13 +67,11 @@ UBThumbnailWidget::UBThumbnailWidget(QWidget* parent) setAlignment(Qt::AlignLeft | Qt::AlignTop); - connect(&mThumbnailsScene, SIGNAL(selectionChanged()), this, SLOT(sceneSelectionChanged())); } UBThumbnailWidget::~UBThumbnailWidget() { - disconnect(&mThumbnailsScene, SIGNAL(selectionChanged())); } @@ -108,7 +106,6 @@ void UBThumbnailWidget::setGraphicsItems(const QList& pGraphicsI { mThumbnailsScene.removeItem(it, true); } - // set lasso to 0 as it has been cleared as well mLassoRectItem = 0; @@ -136,6 +133,12 @@ void UBThumbnailWidget::setGraphicsItems(const QList& pGraphicsI mLastSelectedThumbnail = 0; } +void UBThumbnailWidget::insertThumbnailToScene(QGraphicsPixmapItem* newThumbnail, UBThumbnailTextItem* thumbnailTextItem) +{ + mThumbnailsScene.addItem(newThumbnail); + mThumbnailsScene.addItem(thumbnailTextItem); +} + void UBThumbnailWidget::refreshScene() { @@ -156,6 +159,10 @@ void UBThumbnailWidget::refreshScene() { QGraphicsItem* item = mGraphicItems.at(i); + UBSceneThumbnailPixmap *thumbnail = dynamic_cast(item); + if (thumbnail) + thumbnail->setSceneIndex(i); + qreal scaleWidth = mThumbnailWidth / item->boundingRect().width(); qreal scaleHeight = thumbnailHeight / item->boundingRect().height(); @@ -192,8 +199,10 @@ void UBThumbnailWidget::refreshScene() if (mLabelsItems.size() > i) { + mLabelsItems.at(i)->setWidth(mThumbnailWidth); + mLabelsItems.at(i)->setPageNumber(i+1); QFontMetrics fm(mLabelsItems.at(i)->font(), this); - QString elidedText = fm.elidedText(mLabels.at(i), Qt::ElideRight, mThumbnailWidth); + QString elidedText = fm.elidedText(mLabelsItems.at(i)->toPlainText(), Qt::ElideRight, mThumbnailWidth); mLabelsItems.at(i)->setPlainText(elidedText); mLabelsItems.at(i)->setWidth(fm.width(elidedText) + 2 * mLabelsItems.at(i)->document()->documentMargin()); @@ -223,6 +232,11 @@ QList UBThumbnailWidget::selectedItems() return sortedSelectedItems; } +void UBThumbnailWidget::clearSelection() +{ + mThumbnailsScene.clearSelection(); +} + void UBThumbnailWidget::mousePressEvent(QMouseEvent *event) { @@ -301,7 +315,6 @@ void UBThumbnailWidget::mousePressEvent(QMouseEvent *event) } mSelectionSpan = index2 - index1; selectItems(qMin(index1, index2), mSelectionSpan < 0 ? - mSelectionSpan + 1 : mSelectionSpan + 1); - return; } } } @@ -320,8 +333,9 @@ void UBThumbnailWidget::mousePressEvent(QMouseEvent *event) if (!mLastSelectedThumbnail && mGraphicItems.count() > 0) mLastSelectedThumbnail = dynamic_cast(mGraphicItems.at(0)); mSelectionSpan = 0; - return; } + + UBApplication::documentController->pageSelectionChanged(); } diff --git a/src/gui/UBThumbnailWidget.h b/src/gui/UBThumbnailWidget.h index c286f00d..9b1a3cbb 100644 --- a/src/gui/UBThumbnailWidget.h +++ b/src/gui/UBThumbnailWidget.h @@ -66,6 +66,7 @@ class UBThumbnailWidget : public QGraphicsView QList selectedItems(); void selectItemAt(int pIndex, bool extend = false); void unselectItemAt(int pIndex); + void clearSelection(); qreal thumbnailWidth() { @@ -81,6 +82,7 @@ class UBThumbnailWidget : public QGraphicsView void setThumbnailWidth(qreal pThumbnailWidth); void setSpacing(qreal pSpacing); virtual void setGraphicsItems(const QList& pGraphicsItems, const QList& pItemPaths, const QStringList pLabels = QStringList(), const QString& pMimeType = QString("")); + void insertThumbnailToScene(QGraphicsPixmapItem* newThumbnail, UBThumbnailTextItem* thumbnailTextItem); void refreshScene(); void sceneSelectionChanged(); @@ -184,7 +186,6 @@ class UBThumbnail item->sceneBoundingRect().height() + 10); mSelectionItem->show(); - } else { @@ -199,6 +200,7 @@ class UBThumbnail void setRow(int row) { mRow = row; } UBThumbnailTextItem *label(){return mLabel;} void setLabel(UBThumbnailTextItem *label){mLabel = label;} + QGraphicsRectItem* selectionItem(){ return mSelectionItem; } protected: QGraphicsRectItem *mSelectionItem; @@ -255,6 +257,18 @@ class UBThumbnailTextItem : public QGraphicsTextItem } } + void setPageNumber(int i) + { + mUnelidedText = tr("Page %0").arg(i); + computeText(); + } + + void setText(const QString& text) + { + mUnelidedText = text; + computeText(); + } + void computeText() { QFontMetricsF fm(font()); @@ -339,6 +353,11 @@ class UBSceneThumbnailPixmap : public UBThumbnailPixmap return mSceneIndex; } + void setSceneIndex(int i) + { + mSceneIndex = i; + } + void highlight() { //NOOP diff --git a/src/gui/UBWebToolsPalette.cpp b/src/gui/UBWebToolsPalette.cpp index dcca8fce..3149dc79 100644 --- a/src/gui/UBWebToolsPalette.cpp +++ b/src/gui/UBWebToolsPalette.cpp @@ -49,7 +49,7 @@ UBWebToolsPalette::UBWebToolsPalette(QWidget *parent) { QList actions; - actions << UBApplication::mainWindow->actionWebTrapFlash; + actions << UBApplication::mainWindow->actionCaptureWebContent; actions << UBApplication::mainWindow->actionWebCustomCapture; actions << UBApplication::mainWindow->actionWebWindowCapture; diff --git a/src/pdf/XPDFRenderer.cpp b/src/pdf/XPDFRenderer.cpp index a81616b8..56826565 100644 --- a/src/pdf/XPDFRenderer.cpp +++ b/src/pdf/XPDFRenderer.cpp @@ -95,6 +95,8 @@ XPDFRenderer::XPDFRenderer(const QString &filename, bool importingFile) } #ifdef USE_XPDF mDocument = new PDFDoc(new GString(filename.toLocal8Bit()), 0, 0, 0); // the filename GString is deleted on PDFDoc desctruction +#elif POPPLER_VERSION_MAJOR > 22 || (POPPLER_VERSION_MAJOR == 22 && POPPLER_VERSION_MINOR >= 3) + mDocument = new PDFDoc(std::make_unique(filename.toLocal8Bit())); #else mDocument = new PDFDoc(new GooString(filename.toLocal8Bit()), 0, 0, 0); // the filename GString is deleted on PDFDoc desctruction #endif diff --git a/src/pdf/pdf.pri b/src/pdf/pdf.pri index 9be1b7fe..82b7bf50 100644 --- a/src/pdf/pdf.pri +++ b/src/pdf/pdf.pri @@ -1,4 +1,3 @@ - HEADERS += src/pdf/GraphicsPDFItem.h \ src/pdf/PDFRenderer.h \ src/pdf/UBWebPluginPDFWidget.h \ @@ -8,4 +7,4 @@ SOURCES += src/pdf/GraphicsPDFItem.cpp \ src/pdf/PDFRenderer.cpp \ src/pdf/UBWebPluginPDFWidget.cpp \ src/pdf/XPDFRenderer.cpp - \ No newline at end of file + diff --git a/src/qtsingleapplication/INSTALL.TXT b/src/qtsingleapplication/INSTALL.TXT deleted file mode 100644 index bbb74a9d..00000000 --- a/src/qtsingleapplication/INSTALL.TXT +++ /dev/null @@ -1,254 +0,0 @@ -INSTALLATION INSTRUCTIONS - -These instructions refer to the package you are installing as -some-package.tar.gz or some-package.zip. The .zip file is intended for use -on Windows. - -The directory you choose for the installation will be referred to as -your-install-dir. - -Note to Qt Visual Studio Integration users: In the instructions below, -instead of building from command line with nmake, you can use the menu -command 'Qt->Open Solution from .pro file' on the .pro files in the -example and plugin directories, and then build from within Visual -Studio. - -Unpacking and installation --------------------------- - -1. Unpacking the archive (if you have not done so already). - - On Unix and Mac OS X (in a terminal window): - - cd your-install-dir - gunzip some-package.tar.gz - tar xvf some-package.tar - - This creates the subdirectory some-package containing the files. - - On Windows: - - Unpack the .zip archive by right-clicking it in explorer and - choosing "Extract All...". If your version of Windows does not - have zip support, you can use the infozip tools available - from www.info-zip.org. - - If you are using the infozip tools (in a command prompt window): - cd your-install-dir - unzip some-package.zip - -2. Configuring the package. - - The configure script is called "configure" on unix/mac and - "configure.bat" on Windows. It should be run from a command line - after cd'ing to the package directory. - - You can choose whether you want to use the component by including - its source code directly into your project, or build the component - as a dynamic shared library (DLL) that is loaded into the - application at run-time. The latter may be preferable for - technical or licensing (LGPL) reasons. If you want to build a DLL, - run the configure script with the argument "-library". Also see - the note about usage below. - - (Components that are Qt plugins, e.g. styles and image formats, - are by default built as a plugin DLL.) - - The configure script will prompt you in some cases for further - information. Answer these questions and carefully read the license text - before accepting the license conditions. The package cannot be used if - you do not accept the license conditions. - -3. Building the component and examples (when required). - - If a DLL is to be built, or if you would like to build the - examples, next give the commands - - qmake - make [or nmake if your are using Microsoft Visual C++] - - The example program(s) can be found in the directory called - "examples" or "example". - - Components that are Qt plugins, e.g. styles and image formats, are - ready to be used as soon as they are built, so the rest of this - installation instruction can be skipped. - -4. Building the Qt Designer plugin (optional). - - Some of the widget components are provided with plugins for Qt - Designer. To build and install the plugin, cd into the - some-package/plugin directory and give the commands - - qmake - make [or nmake if your are using Microsoft Visual C++] - - Restart Qt Designer to make it load the new widget plugin. - - Note: If you are using the built-in Qt Designer from the Qt Visual - Studio Integration, you will need to manually copy the plugin DLL - file, i.e. copy - %QTDIR%\plugins\designer\some-component.dll - to the Qt Visual Studio Integration plugin path, typically: - C:\Program Files\Trolltech\Qt VS Integration\plugins - - Note: If you for some reason are using a Qt Designer that is built - in debug mode, you will need to build the plugin in debug mode - also. Edit the file plugin.pro in the plugin directory, changing - 'release' to 'debug' in the CONFIG line, before running qmake. - - - -Solutions components are intended to be used directly from the package -directory during development, so there is no 'make install' procedure. - - -Using a component in your project ---------------------------------- - -To use this component in your project, add the following line to the -project's .pro file (or do the equivalent in your IDE): - - include(your-install-dir/some-package/src/some-package.pri) - -This adds the package's sources and headers to the SOURCES and HEADERS -project variables respectively (or, if the component has been -configured as a DLL, it adds that library to the LIBS variable), and -updates INCLUDEPATH to contain the package's src -directory. Additionally, the .pri file may include some dependencies -needed by the package. - -To include a header file from the package in your sources, you can now -simply use: - - #include - -or alternatively, in pre-Qt 4 style: - - #include - -Refer to the documentation to see the classes and headers this -components provides. - - - -Install documentation (optional) --------------------------------- - -The HTML documentation for the package's classes is located in the -your-install-dir/some-package/doc/html/index.html. You can open this -file and read the documentation with any web browser. - -To install the documentation into Qt Assistant (for Qt version 4.4 and -later): - -1. In Assistant, open the Edit->Preferences dialog and choose the - Documentation tab. Click the Add... button and select the file - your-install-dir/some-package/doc/html/some-package.qch - -For Qt versions prior to 4.4, do instead the following: - -1. The directory your-install-dir/some-package/doc/html contains a - file called some-package.dcf. Execute the following commands in a - shell, command prompt or terminal window: - - cd your-install-dir/some-package/doc/html/ - assistant -addContentFile some-package.dcf - -The next time you start Qt Assistant, you can access the package's -documentation. - - -Removing the documentation from assistant ------------------------------------------ - -If you have installed the documentation into Qt Assistant, and want to uninstall it, do as follows, for Qt version 4.4 and later: - -1. In Assistant, open the Edit->Preferences dialog and choose the - Documentation tab. In the list of Registered Documentation, select - the item com.nokia.qtsolutions.some-package_version, and click - the Remove button. - -For Qt versions prior to 4.4, do instead the following: - -1. The directory your-install-dir/some-package/doc/html contains a - file called some-package.dcf. Execute the following commands in a - shell, command prompt or terminal window: - - cd your-install-dir/some-package/doc/html/ - assistant -removeContentFile some-package.dcf - - - -Using the component as a DLL ----------------------------- - -1. Normal components - - The shared library (DLL) is built and placed in the - some-package/lib directory. It is intended to be used directly - from there during development. When appropriate, both debug and - release versions are built, since the run-time linker will in some - cases refuse to load a debug-built DLL into a release-built - application or vice versa. - - The following steps are taken by default to help the dynamic - linker to locate the DLL at run-time (during development): - - Unix: The some-package.pri file will add linker instructions to - add the some-package/lib directory to the rpath of the - executable. (When distributing, or if your system does not support - rpath, you can copy the shared library to another place that is - searched by the dynamic linker, e.g. the "lib" directory of your - Qt installation.) - - Mac: The full path to the library is hardcoded into the library - itself, from where it is copied into the executable at link time, - and ready by the dynamic linker at run-time. (When distributing, - you will want to edit these hardcoded paths in the same way as for - the Qt DLLs. Refer to the document "Deploying an Application on - Mac OS X" in the Qt Reference Documentation.) - - Windows: the .dll file(s) are copied into the "bin" directory of - your Qt installation. The Qt installation will already have set up - that directory to be searched by the dynamic linker. - - -2. Plugins - - For Qt Solutions plugins (e.g. image formats), both debug and - release versions of the plugin are built by default when - appropriate, since in some cases the release Qt library will not - load a debug plugin, and vice versa. The plugins are automatically - copied into the plugins directory of your Qt installation when - built, so no further setup is required. - - Plugins may also be built statically, i.e. as a library that will be - linked into your application executable, and so will not need to - be redistributed as a separate plugin DLL to end users. Static - building is required if Qt itself is built statically. To do it, - just add "static" to the CONFIG variable in the plugin/plugin.pro - file before building. Refer to the "Static Plugins" section in the - chapter "How to Create Qt Plugins" for explanation of how to use a - static plugin in your application. The source code of the example - program(s) will also typically contain the relevant instructions - as comments. - - - -Uninstalling ------------- - - The following command will remove any fils that have been - automatically placed outside the package directory itself during - installation and building - - make distclean [or nmake if your are using Microsoft Visual C++] - - If Qt Assistant documentation or Qt Designer plugins have been - installed, they can be uninstalled manually, ref. above. - - -Enjoy! :) - -- The Qt Solutions Team. diff --git a/src/qtsingleapplication/README.TXT b/src/qtsingleapplication/README.TXT deleted file mode 100644 index 06abb095..00000000 --- a/src/qtsingleapplication/README.TXT +++ /dev/null @@ -1,33 +0,0 @@ -Qt Solutions Component: Single Application - -The QtSingleApplication component provides support for -applications that can be only started once per user. - - - -Version history: - -2.0: - Version 1.3 ported to Qt 4. - -2.1: - Fix compilation problem on Mac. - -2.2: - Really fix the Mac compilation problem. - - Mac: fix crash due to wrong object releasing. - - Mac: Fix memory leak. - -2.3: - Windows: Force creation of internal widget to make it work - with Qt 4.2. - -2.4: - Fix the system for automatic window raising on message - reception. NOTE: minor API change. - -2.5: - Mac: Fix isRunning() to work and report correctly. - -2.6: - - initialize() is now obsolete, no longer necessary to call - it - - - Fixed race condition where multiple instances migth be started - - - QtSingleCoreApplication variant provided for non-GUI (console) - usage - - Complete reimplementation. Visible changes: - - LGPL release. - diff --git a/src/qtsingleapplication/buildlib/buildlib.pro b/src/qtsingleapplication/buildlib/buildlib.pro deleted file mode 100644 index 37dddcda..00000000 --- a/src/qtsingleapplication/buildlib/buildlib.pro +++ /dev/null @@ -1,13 +0,0 @@ -TEMPLATE=lib -CONFIG += qt dll qtsingleapplication-buildlib -mac:CONFIG += absolute_library_soname -win32|mac:!wince*:!win32-msvc:!macx-xcode:CONFIG += debug_and_release build_all -include(../src/qtsingleapplication.pri) -TARGET = $$QTSINGLEAPPLICATION_LIBNAME -DESTDIR = $$QTSINGLEAPPLICATION_LIBDIR -win32 { - DLLDESTDIR = $$[QT_INSTALL_BINS] - QMAKE_DISTCLEAN += $$[QT_INSTALL_BINS]\\$${QTSINGLEAPPLICATION_LIBNAME}.dll -} -target.path = $$DESTDIR -INSTALLS += target diff --git a/src/qtsingleapplication/common.pri b/src/qtsingleapplication/common.pri deleted file mode 100644 index 924c57c8..00000000 --- a/src/qtsingleapplication/common.pri +++ /dev/null @@ -1,14 +0,0 @@ -exists(config.pri):infile(config.pri, SOLUTIONS_LIBRARY, yes): CONFIG += qtsingleapplication-uselib - -TEMPLATE += fakelib -greaterThan(QT_MAJOR_VERSION, 5)|\ - if(equals(QT_MAJOR_VERSION, 5):greaterThan(QT_MINOR_VERSION, 4))|\ - if(equals(QT_MAJOR_VERSION, 5):equals(QT_MINOR_VERSION, 4):greaterThan(QT_PATCH_VERSION, 1)) { - QTSINGLEAPPLICATION_LIBNAME = $$qt5LibraryTarget(QtSolutions_SingleApplication-head) -} else { - QTSINGLEAPPLICATION_LIBNAME = $$qtLibraryTarget(QtSolutions_SingleApplication-head) -} -TEMPLATE -= fakelib - -QTSINGLEAPPLICATION_LIBDIR = $$PWD/lib -unix:qtsingleapplication-uselib:!qtsingleapplication-buildlib:QMAKE_RPATHDIR += $$QTSINGLEAPPLICATION_LIBDIR diff --git a/src/qtsingleapplication/configure b/src/qtsingleapplication/configure deleted file mode 100755 index 3c4edfff..00000000 --- a/src/qtsingleapplication/configure +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh - -if [ "x$1" != "x" -a "x$1" != "x-library" ]; then - echo "Usage: $0 [-library]" - echo - echo "-library: Build the component as a dynamic library (DLL). Default is to" - echo " include the component source code directly in the application." - echo - exit 0 -fi - -rm -f config.pri -if [ "x$1" = "x-library" ]; then - echo "Configuring to build this component as a dynamic library." - echo "SOLUTIONS_LIBRARY = yes" > config.pri -fi - -echo -echo "This component is now configured." -echo -echo "To build the component library (if requested) and example(s)," -echo "run qmake and your make command." -echo -echo "To remove or reconfigure, run make distclean." -echo diff --git a/src/qtsingleapplication/configure.bat b/src/qtsingleapplication/configure.bat deleted file mode 100644 index 69efec35..00000000 --- a/src/qtsingleapplication/configure.bat +++ /dev/null @@ -1,80 +0,0 @@ -::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -:: -:: Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -:: Contact: http://www.qt-project.org/legal -:: -:: This file is part of the Qt Solutions component. -:: -:: $QT_BEGIN_LICENSE:BSD$ -:: 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 Digia Plc and its Subsidiary(-ies) 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$ -:: -::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: - -@echo off - -rem -rem "Main" -rem - -if not "%1"=="" ( - if not "%1"=="-library" ( - call :PrintUsage - goto EOF - ) -) - -if exist config.pri. del config.pri -if "%1"=="-library" ( - echo Configuring to build this component as a dynamic library. - echo SOLUTIONS_LIBRARY = yes > config.pri -) - -echo . -echo This component is now configured. -echo . -echo To build the component library (if requested) and example(s), -echo run qmake and your make or nmake command. -echo . -echo To remove or reconfigure, run make (nmake) distclean. -echo . -goto EOF - -:PrintUsage -echo Usage: configure.bat [-library] -echo . -echo -library: Build the component as a dynamic library (DLL). Default is to -echo include the component source directly in the application. -echo A DLL may be preferable for technical or licensing (LGPL) reasons. -echo . -goto EOF - - -:EOF diff --git a/src/qtsingleapplication/doc/html/classic.css b/src/qtsingleapplication/doc/html/classic.css deleted file mode 100644 index b8cae8e1..00000000 --- a/src/qtsingleapplication/doc/html/classic.css +++ /dev/null @@ -1,284 +0,0 @@ -BODY,H1,H2,H3,H4,H5,H6,P,CENTER,TD,TH,UL,DL,DIV { - font-family: Arial, Geneva, Helvetica, sans-serif; -} -H1 { - text-align: center; - font-size: 160%; -} -H2 { - font-size: 120%; -} -H3 { - font-size: 100%; -} - -h3.fn,span.fn -{ - background-color: #eee; - border-width: 1px; - border-style: solid; - border-color: #ddd; - font-weight: bold; - padding: 6px 0px 6px 10px; - margin: 42px 0px 0px 0px; -} - -hr { - border: 0; - color: #a0a0a0; - background-color: #ccc; - height: 1px; - width: 100%; - text-align: left; - margin: 34px 0px 34px 0px; -} - -table.valuelist { - border-width: 1px 1px 1px 1px; - border-style: solid; - border-color: #dddddd; - border-collapse: collapse; - background-color: #f0f0f0; -} - -table.indextable { - border-width: 1px 1px 1px 1px; - border-style: solid; - border-collapse: collapse; - background-color: #f0f0f0; - border-color:#555; - font-size: 100%; -} - -table td.largeindex { - border-width: 1px 1px 1px 1px; - border-collapse: collapse; - background-color: #f0f0f0; - border-color:#555; - font-size: 120%; -} - -table.valuelist th { - border-width: 1px 1px 1px 2px; - padding: 4px; - border-style: solid; - border-color: #666; - color:white; - background-color:#666; -} - -th.titleheader { - border-width: 1px 0px 1px 0px; - padding: 2px; - border-style: solid; - border-color: #666; - color:white; - background-color:#555; - background-image:url('images/gradient.png')}; - background-repeat: repeat-x; - font-size: 100%; -} - - -th.largeheader { - border-width: 1px 0px 1px 0px; - padding: 4px; - border-style: solid; - border-color: #444; - color:white; - background-color:#555555; - font-size: 120%; -} - -p { - - margin-left: 4px; - margin-top: 8px; - margin-bottom: 8px; -} - -a:link -{ - color: #0046ad; - text-decoration: none -} - -a:visited -{ - color: #672967; - text-decoration: none -} - -a.obsolete -{ - color: #661100; - text-decoration: none -} - -a.compat -{ - color: #661100; - text-decoration: none -} - -a.obsolete:visited -{ - color: #995500; - text-decoration: none -} - -a.compat:visited -{ - color: #995500; - text-decoration: none -} - -body -{ - background: #ffffff; - color: black -} - -table.generic, table.annotated -{ - border-width: 1px; - border-color:#bbb; - border-style:solid; - border-collapse:collapse; -} - -table td.memItemLeft { - width: 180px; - padding: 2px 0px 0px 8px; - margin: 4px; - border-width: 1px; - border-color: #E0E0E0; - border-style: none; - font-size: 100%; - white-space: nowrap -} - -table td.memItemRight { - padding: 2px 8px 0px 8px; - margin: 4px; - border-width: 1px; - border-color: #E0E0E0; - border-style: none; - font-size: 100%; -} - -table tr.odd { - background: #f0f0f0; - color: black; -} - -table tr.even { - background: #e4e4e4; - color: black; -} - -table.annotated th { - padding: 3px; - text-align: left -} - -table.annotated td { - padding: 3px; -} - -table tr pre -{ - padding-top: 0px; - padding-bottom: 0px; - padding-left: 0px; - padding-right: 0px; - border: none; - background: none -} - -tr.qt-style -{ - background: #96E066; - color: black -} - -body pre -{ - padding: 0.2em; - border: #e7e7e7 1px solid; - background: #f1f1f1; - color: black -} - -table tr.qt-code pre -{ - padding: 0.2em; - border: #e7e7e7 1px solid; - background: #f1f1f1; - color: black -} - -span.preprocessor, span.preprocessor a -{ - color: darkblue; -} - -span.comment -{ - color: darkred; - font-style: italic -} - -span.string,span.char -{ - color: darkgreen; -} - -.title -{ - text-align: center -} - -.subtitle -{ - font-size: 0.8em -} - -.small-subtitle -{ - font-size: 0.65em -} - -.qmlitem { - padding: 0; -} - -.qmlname { - white-space: nowrap; -} - -.qmltype { - text-align: center; - font-size: 160%; -} - -.qmlproto { - background-color: #eee; - border-width: 1px; - border-style: solid; - border-color: #ddd; - font-weight: bold; - padding: 6px 10px 6px 10px; - margin: 42px 0px 0px 0px; -} - -.qmlreadonly { - float: right; - color: red -} - -.qmldoc { -} - -*.qmlitem p { -} diff --git a/src/qtsingleapplication/doc/html/images/qt-logo.png b/src/qtsingleapplication/doc/html/images/qt-logo.png deleted file mode 100644 index 794162f5..00000000 Binary files a/src/qtsingleapplication/doc/html/images/qt-logo.png and /dev/null differ diff --git a/src/qtsingleapplication/doc/html/index.html b/src/qtsingleapplication/doc/html/index.html deleted file mode 100644 index af9dab10..00000000 --- a/src/qtsingleapplication/doc/html/index.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - Single Application - - - - - - - -
  Home

Single Application
-

- -

Description

-

The QtSingleApplication component provides support for applications that can be only started once per user.

-

For some applications it is useful or even critical that they are started only once by any user. Future attempts to start the application should activate any already running instance, and possibly perform requested actions, e.g. loading a file, in that instance.

-

The QtSingleApplication class provides an interface to detect a running instance, and to send command strings to that instance. For console (non-GUI) applications, the QtSingleCoreApplication variant is provided, which avoids dependency on QtGui.

- -

Classes

- - -

Examples

- - -

Tested platforms

-
    -
  • Qt 4.4, 4.5 / Windows XP / MSVC.NET 2005
  • -
  • Qt 4.4, 4.5 / Linux / gcc
  • -
  • Qt 4.4, 4.5 / MacOS X 10.5 / gcc
  • -
-


- - - - -
Copyright © 2010 Nokia Corporation and/or its subsidiary(-ies)Trademarks
Qt Solutions
- diff --git a/src/qtsingleapplication/doc/html/qtsingleapplication-example-loader.html b/src/qtsingleapplication/doc/html/qtsingleapplication-example-loader.html deleted file mode 100644 index 6a366329..00000000 --- a/src/qtsingleapplication/doc/html/qtsingleapplication-example-loader.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - Loading Documents - - - - - - - -
  Home

Loading Documents
-

-

The application in this example loads or prints the documents passed as commandline parameters to further instances of this application.

-
 /****************************************************************************
- **
- ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
- ** Contact: http://www.qt-project.org/legal
- **
- ** This file is part of the Qt Solutions component.
- **
- ** 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 Nokia Corporation and its Subsidiary(-ies) 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."
- **
- ****************************************************************************/
-
- #include <qtsingleapplication.h>
- #include <QtCore/QFile>
- #include <QtGui/QMainWindow>
- #include <QtGui/QPrinter>
- #include <QtGui/QPainter>
- #include <QtGui/QTextEdit>
- #include <QtGui/QMdiArea>
- #include <QtCore/QTextStream>
-
- class MainWindow : public QMainWindow
- {
-     Q_OBJECT
- public:
-     MainWindow();
-
- public slots:
-     void handleMessage(const QString& message);
-
- signals:
-     void needToShow();
-
- private:
-     QMdiArea *workspace;
- };
-

The user interface in this application is a QMainWindow subclass with a QMdiArea as the central widget. It implements a slot handleMessage() that will be connected to the messageReceived() signal of the QtSingleApplication class.

-
 MainWindow::MainWindow()
- {
-     workspace = new QMdiArea(this);
-
-     setCentralWidget(workspace);
- }
-

The MainWindow constructor creates a minimal user interface.

-
 void MainWindow::handleMessage(const QString& message)
- {
-     enum Action {
-         Nothing,
-         Open,
-         Print
-     } action;
-
-     action = Nothing;
-     QString filename = message;
-     if (message.toLower().startsWith("/print ")) {
-         filename = filename.mid(7);
-         action = Print;
-     } else if (!message.isEmpty()) {
-         action = Open;
-     }
-     if (action == Nothing) {
-         emit needToShow();
-         return;
-     }
-
-     QFile file(filename);
-     QString contents;
-     if (file.open(QIODevice::ReadOnly))
-         contents = file.readAll();
-     else
-         contents = "[[Error: Could not load file " + filename + "]]";
-
-     QTextEdit *view = new QTextEdit;
-     view->setPlainText(contents);
-
-     switch(action) {
-

The handleMessage() slot interprets the message passed in as a filename that can be prepended with /print to indicate that the file should just be printed rather than loaded.

-
     case Print:
-         {
-             QPrinter printer;
-             view->print(&printer);
-             delete view;
-         }
-         break;
-
-     case Open:
-         {
-             workspace->addSubWindow(view);
-             view->setWindowTitle(message);
-             view->show();
-             emit needToShow();
-         }
-         break;
-     default:
-         break;
-     };
- }
-

Loading the file will also activate the window.

-
 #include "main.moc"
-
- int main(int argc, char **argv)
- {
-     QtSingleApplication instance("File loader QtSingleApplication example", argc, argv);
-     QString message;
-     for (int a = 1; a < argc; ++a) {
-         message += argv[a];
-         if (a < argc-1)
-             message += " ";
-     }
-
-     if (instance.sendMessage(message))
-         return 0;
-

The main entry point function creates a QtSingleApplication object, and creates a message to send to a running instance of the application. If the message was sent successfully the process exits immediately.

-
     MainWindow mw;
-     mw.handleMessage(message);
-     mw.show();
-
-     QObject::connect(&instance, SIGNAL(messageReceived(const QString&)),
-                      &mw, SLOT(handleMessage(const QString&)));
-
-     instance.setActivationWindow(&mw, false);
-     QObject::connect(&mw, SIGNAL(needToShow()), &instance, SLOT(activateWindow()));
-
-     return instance.exec();
- }
-

If the message could not be sent the application starts up. Note that false is passed to the call to setActivationWindow() to prevent automatic activation for every message received, e.g. when the application should just print a file. Instead, the message handling function determines whether activation is requested, and signals that by emitting the needToShow() signal. This is then simply connected directly to QtSingleApplication's activateWindow() slot.

-


- - - - -
Copyright © 2010 Nokia Corporation and/or its subsidiary(-ies)Trademarks
Qt Solutions
- diff --git a/src/qtsingleapplication/doc/html/qtsingleapplication-example-trivial.html b/src/qtsingleapplication/doc/html/qtsingleapplication-example-trivial.html deleted file mode 100644 index 5e60cfa4..00000000 --- a/src/qtsingleapplication/doc/html/qtsingleapplication-example-trivial.html +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - A Trivial Example - - - - - - - -
  Home

A Trivial Example
-

-

The application in this example has a log-view that displays messages sent by further instances of the same application.

-

The example demonstrates the use of the QtSingleApplication class to detect and communicate with a running instance of the application using the sendMessage() API. The messageReceived() signal is used to display received messages in a QTextEdit log.

-
 /****************************************************************************
- **
- ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
- ** Contact: http://www.qt-project.org/legal
- **
- ** This file is part of the Qt Solutions component.
- **
- ** 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 Nokia Corporation and its Subsidiary(-ies) 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."
- **
- ****************************************************************************/
-
- #include <qtsingleapplication.h>
- #include <QtGui/QTextEdit>
-
- class TextEdit : public QTextEdit
- {
-     Q_OBJECT
- public:
-     TextEdit(QWidget *parent = 0)
-         : QTextEdit(parent)
-     {}
- public slots:
-     void append(const QString &str)
-     {
-         QTextEdit::append(str);
-     }
- };
-
- #include "main.moc"
-
- int main(int argc, char **argv)
- {
-     QtSingleApplication instance(argc, argv);
-

The example has only the main entry point function. A QtSingleApplication object is created immediately.

-
     if (instance.sendMessage("Wake up!"))
-         return 0;
-

If another instance of this application is already running, sendMessage() will succeed, and this instance just exits immediately.

-
     TextEdit logview;
-     logview.setReadOnly(true);
-     logview.show();
-

Otherwise the instance continues as normal and creates the user interface.

-
     instance.setActivationWindow(&logview);
-
-     QObject::connect(&instance, SIGNAL(messageReceived(const QString&)),
-                      &logview, SLOT(append(const QString&)));
-
-     return instance.exec();
-

The logview object is also set as the application's activation window. Every time a message is received, the window will be raised and activated automatically.

-

The messageReceived() signal is also connected to the QTextEdit's append() slot. Every message received from further instances of this application will be displayed in the log.

-

Finally the event loop is entered.

-


- - - - -
Copyright © 2010 Nokia Corporation and/or its subsidiary(-ies)Trademarks
Qt Solutions
- diff --git a/src/qtsingleapplication/doc/html/qtsingleapplication-members.html b/src/qtsingleapplication/doc/html/qtsingleapplication-members.html deleted file mode 100644 index c995ce3f..00000000 --- a/src/qtsingleapplication/doc/html/qtsingleapplication-members.html +++ /dev/null @@ -1,235 +0,0 @@ - - - - - - List of All Members for QtSingleApplication - - - - - - - -
  Home

List of All Members for QtSingleApplication

-

This is the complete list of members for QtSingleApplication, including inherited members.

-

- -
-

-


- - - - -
Copyright © 2010 Nokia Corporation and/or its subsidiary(-ies)Trademarks
Qt Solutions
- diff --git a/src/qtsingleapplication/doc/html/qtsingleapplication-obsolete.html b/src/qtsingleapplication/doc/html/qtsingleapplication-obsolete.html deleted file mode 100644 index 0d07dfae..00000000 --- a/src/qtsingleapplication/doc/html/qtsingleapplication-obsolete.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - Obsolete Members for QtSingleApplication - - - - - - - -
  Home

Obsolete Members for QtSingleApplication

-

The following class members are obsolete. They are provided to keep old source code working. We strongly advise against using them in new code.

-

-

Public Functions

- - -
void initialize ( bool dummy = true )   (obsolete)
-
-

Member Function Documentation

-

void QtSingleApplication::initialize ( bool dummy = true )

-


- - - - -
Copyright © 2010 Nokia Corporation and/or its subsidiary(-ies)Trademarks
Qt Solutions
- diff --git a/src/qtsingleapplication/doc/html/qtsingleapplication.dcf b/src/qtsingleapplication/doc/html/qtsingleapplication.dcf deleted file mode 100644 index d81f87fb..00000000 --- a/src/qtsingleapplication/doc/html/qtsingleapplication.dcf +++ /dev/null @@ -1,40 +0,0 @@ - - -
-
- QtSingleApplication - activateWindow - activationWindow - id - isRunning - messageReceived - sendMessage - setActivationWindow -
-
-
-
- QtSingleCoreApplication - id - isRunning - messageReceived - sendMessage -
-
-
-
-
- A non-GUI example -
-
- A Trivial Example -
-
- Loading Documents -
-
- Single Application -
-
-
- diff --git a/src/qtsingleapplication/doc/html/qtsingleapplication.html b/src/qtsingleapplication/doc/html/qtsingleapplication.html deleted file mode 100644 index 2754a3b9..00000000 --- a/src/qtsingleapplication/doc/html/qtsingleapplication.html +++ /dev/null @@ -1,162 +0,0 @@ - - - - - - QtSingleApplication Class Reference - - - - - - - -
  Home

QtSingleApplication Class Reference

-

The QtSingleApplication class provides an API to detect and communicate with running instances of an application. More...

-
 #include <QtSingleApplication>

Inherits QApplication.

- -
- -

Public Functions

- - - - - - - - - - - -
QtSingleApplication ( int & argc, char ** argv, bool GUIenabled = true )
QtSingleApplication ( const QString & appId, int & argc, char ** argv )
QtSingleApplication ( int & argc, char ** argv, Type type )
QtSingleApplication ( Display * dpy, Qt::HANDLE visual = 0, Qt::HANDLE cmap = 0 )
QtSingleApplication ( Display * dpy, int & argc, char ** argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap = 0 )
QtSingleApplication ( Display * dpy, const QString & appId, int argc, char ** argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap = 0 )
QWidget * activationWindow () const
QString id () const
bool isRunning ()
void setActivationWindow ( QWidget * aw, bool activateOnMessage = true )
- -
- -

Public Slots

- - - -
void activateWindow ()
bool sendMessage ( const QString & message, int timeout = 5000 )
- -
- -

Signals

- - -
void messageReceived ( const QString & message )
- -

Additional Inherited Members

- - -
-

Detailed Description

-

The QtSingleApplication class provides an API to detect and communicate with running instances of an application.

-

This class allows you to create applications where only one instance should be running at a time. I.e., if the user tries to launch another instance, the already running instance will be activated instead. Another usecase is a client-server system, where the first started instance will assume the role of server, and the later instances will act as clients of that server.

-

By default, the full path of the executable file is used to determine whether two processes are instances of the same application. You can also provide an explicit identifier string that will be compared instead.

-

The application should create the QtSingleApplication object early in the startup phase, and call isRunning() to find out if another instance of this application is already running. If isRunning() returns false, it means that no other instance is running, and this instance has assumed the role as the running instance. In this case, the application should continue with the initialization of the application user interface before entering the event loop with exec(), as normal.

-

The messageReceived() signal will be emitted when the running application receives messages from another instance of the same application. When a message is received it might be helpful to the user to raise the application so that it becomes visible. To facilitate this, QtSingleApplication provides the setActivationWindow() function and the activateWindow() slot.

-

If isRunning() returns true, another instance is already running. It may be alerted to the fact that another instance has started by using the sendMessage() function. Also data such as startup parameters (e.g. the name of the file the user wanted this new instance to open) can be passed to the running instance with this function. Then, the application should terminate (or enter client mode).

-

If isRunning() returns true, but sendMessage() fails, that is an indication that the running instance is frozen.

-

Here's an example that shows how to convert an existing application to use QtSingleApplication. It is very simple and does not make use of all QtSingleApplication's functionality (see the examples for that).

-
 // Original
- int main(int argc, char **argv)
- {
-     QApplication app(argc, argv);
-
-     MyMainWidget mmw;
-     mmw.show();
-     return app.exec();
- }
-
- // Single instance
- int main(int argc, char **argv)
- {
-     QtSingleApplication app(argc, argv);
-
-     if (app.isRunning())
-         return !app.sendMessage(someDataString);
-
-     MyMainWidget mmw;
-     app.setActivationWindow(&mmw);
-     mmw.show();
-     return app.exec();
- }
-

Once this QtSingleApplication instance is destroyed (normally when the process exits or crashes), when the user next attempts to run the application this instance will not, of course, be encountered. The next instance to call isRunning() or sendMessage() will assume the role as the new running instance.

-

For console (non-GUI) applications, QtSingleCoreApplication may be used instead of this class, to avoid the dependency on the QtGui library.

-

See also QtSingleCoreApplication.

-
-

Member Function Documentation

-

QtSingleApplication::QtSingleApplication ( int & argc, char ** argv, bool GUIenabled = true )

-

Creates a QtSingleApplication object. The application identifier will be QCoreApplication::applicationFilePath(). argc, argv, and GUIenabled are passed on to the QAppliation constructor.

-

If you are creating a console application (i.e. setting GUIenabled to false), you may consider using QtSingleCoreApplication instead.

-

QtSingleApplication::QtSingleApplication ( const QString & appId, int & argc, char ** argv )

-

Creates a QtSingleApplication object with the application identifier appId. argc and argv are passed on to the QAppliation constructor.

-

QtSingleApplication::QtSingleApplication ( int & argc, char ** argv, Type type )

-

Creates a QtSingleApplication object. The application identifier will be QCoreApplication::applicationFilePath(). argc, argv, and type are passed on to the QAppliation constructor.

-

QtSingleApplication::QtSingleApplication ( Display * dpy, Qt::HANDLE visual = 0, Qt::HANDLE cmap = 0 )

-

Special constructor for X11, ref. the documentation of QApplication's corresponding constructor. The application identifier will be QCoreApplication::applicationFilePath(). dpy, visual, and cmap are passed on to the QApplication constructor.

-

QtSingleApplication::QtSingleApplication ( Display * dpy, int & argc, char ** argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap = 0 )

-

Special constructor for X11, ref. the documentation of QApplication's corresponding constructor. The application identifier will be QCoreApplication::applicationFilePath(). dpy, argc, argv, visual, and cmap are passed on to the QApplication constructor.

-

QtSingleApplication::QtSingleApplication ( Display * dpy, const QString & appId, int argc, char ** argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap = 0 )

-

Special constructor for X11, ref. the documentation of QApplication's corresponding constructor. The application identifier will be appId. dpy, argc, argv, visual, and cmap are passed on to the QApplication constructor.

-

void QtSingleApplication::activateWindow ()   [slot]

-

De-minimizes, raises, and activates this application's activation window. This function does nothing if no activation window has been set.

-

This is a convenience function to show the user that this application instance has been activated when he has tried to start another instance.

-

This function should typically be called in response to the messageReceived() signal. By default, that will happen automatically, if an activation window has been set.

-

See also setActivationWindow(), messageReceived(), and initialize().

-

QWidget * QtSingleApplication::activationWindow () const

-

Returns the applications activation window if one has been set by calling setActivationWindow(), otherwise returns 0.

-

See also setActivationWindow().

-

QString QtSingleApplication::id () const

-

Returns the application identifier. Two processes with the same identifier will be regarded as instances of the same application.

-

bool QtSingleApplication::isRunning ()

-

Returns true if another instance of this application is running; otherwise false.

-

This function does not find instances of this application that are being run by a different user (on Windows: that are running in another session).

-

See also sendMessage().

-

void QtSingleApplication::messageReceived ( const QString & message )   [signal]

-

This signal is emitted when the current instance receives a message from another instance of this application.

-

See also sendMessage(), setActivationWindow(), and activateWindow().

-

bool QtSingleApplication::sendMessage ( const QString & message, int timeout = 5000 )   [slot]

-

Tries to send the text message to the currently running instance. The QtSingleApplication object in the running instance will emit the messageReceived() signal when it receives the message.

-

This function returns true if the message has been sent to, and processed by, the current instance. If there is no instance currently running, or if the running instance fails to process the message within timeout milliseconds, this function return false.

-

See also isRunning() and messageReceived().

-

void QtSingleApplication::setActivationWindow ( QWidget * aw, bool activateOnMessage = true )

-

Sets the activation window of this application to aw. The activation window is the widget that will be activated by activateWindow(). This is typically the application's main window.

-

If activateOnMessage is true (the default), the window will be activated automatically every time a message is received, just prior to the messageReceived() signal being emitted.

-

See also activationWindow(), activateWindow(), and messageReceived().

-


- - - - -
Copyright © 2010 Nokia Corporation and/or its subsidiary(-ies)Trademarks
Qt Solutions
- diff --git a/src/qtsingleapplication/doc/html/qtsingleapplication.index b/src/qtsingleapplication/doc/html/qtsingleapplication.index deleted file mode 100644 index 56052c2a..00000000 --- a/src/qtsingleapplication/doc/html/qtsingleapplication.index +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/qtsingleapplication/doc/html/qtsingleapplication.qhp b/src/qtsingleapplication/doc/html/qtsingleapplication.qhp deleted file mode 100644 index ff42d9df..00000000 --- a/src/qtsingleapplication/doc/html/qtsingleapplication.qhp +++ /dev/null @@ -1,53 +0,0 @@ - - - com.nokia.qtsolutions.qtsingleapplication_head - qdoc - - qt - solutions - qtsingleapplication - - - qt - solutions - qtsingleapplication - -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - qtsingleapplication.html - index.html - qtsingleapplication-example-trivial.html - qtsinglecoreapplication.html - qtsingleapplication-example-loader.html - qtsinglecoreapplication-example-console.html - classic.css - images/qt-logo.png - - - diff --git a/src/qtsingleapplication/doc/html/qtsinglecoreapplication-example-console.html b/src/qtsingleapplication/doc/html/qtsinglecoreapplication-example-console.html deleted file mode 100644 index 18a9ae89..00000000 --- a/src/qtsingleapplication/doc/html/qtsinglecoreapplication-example-console.html +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - A non-GUI example - - - - - - - -
  Home

A non-GUI example
-

-

This example shows how to use the single-application functionality in a console application. It does not require the QtGui library at all.

-

The only differences from the GUI application usage demonstrated in the other examples are:

-

1) The .pro file should include qtsinglecoreapplication.pri instead of qtsingleapplication.pri

-

2) The class name is QtSingleCoreApplication instead of QtSingleApplication.

-

3) No calls are made regarding window activation, for obvious reasons.

-

console.pro:

-
 TEMPLATE   = app
- CONFIG    += console
- SOURCES   += main.cpp
- include(../../src/qtsinglecoreapplication.pri)
- QT -= gui
-

main.cpp:

-
 /****************************************************************************
- **
- ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
- ** Contact: http://www.qt-project.org/legal
- **
- ** This file is part of the Qt Solutions component.
- **
- ** 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 Nokia Corporation and its Subsidiary(-ies) 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."
- **
- ****************************************************************************/
-
- #include "qtsinglecoreapplication.h"
- #include <QtCore/QDebug>
-
- void report(const QString& msg)
- {
-     qDebug("[%i] %s", (int)QCoreApplication::applicationPid(), qPrintable(msg));
- }
-
- class MainClass : public QObject
- {
-     Q_OBJECT
- public:
-     MainClass()
-         : QObject()
-         {}
-
- public slots:
-     void handleMessage(const QString& message)
-         {
-             report( "Message received: \"" + message + "\"");
-         }
- };
-
- int main(int argc, char **argv)
- {
-     report("Starting up");
-
-     QtSingleCoreApplication app(argc, argv);
-
-     if (app.isRunning()) {
-         QString msg(QString("Hi master, I am %1.").arg(QCoreApplication::applicationPid()));
-         bool sentok = app.sendMessage(msg, 2000);
-         QString rep("Another instance is running, so I will exit.");
-         rep += sentok ? " Message sent ok." : " Message sending failed; the other instance may be frozen.";
-         report(rep);
-         return 0;
-     } else {
-         report("No other instance is running; so I will.");
-         MainClass mainObj;
-         QObject::connect(&app, SIGNAL(messageReceived(const QString&)),
-                          &mainObj, SLOT(handleMessage(const QString&)));
-         return app.exec();
-     }
- }
-
- #include "main.moc"
-


- - - - -
Copyright © 2010 Nokia Corporation and/or its subsidiary(-ies)Trademarks
Qt Solutions
- diff --git a/src/qtsingleapplication/doc/html/qtsinglecoreapplication-members.html b/src/qtsingleapplication/doc/html/qtsinglecoreapplication-members.html deleted file mode 100644 index 69fb8581..00000000 --- a/src/qtsingleapplication/doc/html/qtsinglecoreapplication-members.html +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - List of All Members for QtSingleCoreApplication - - - - - - - -
  Home

List of All Members for QtSingleCoreApplication

-

This is the complete list of members for QtSingleCoreApplication, including inherited members.

-

- -
-

-


- - - - -
Copyright © 2010 Nokia Corporation and/or its subsidiary(-ies)Trademarks
Qt Solutions
- diff --git a/src/qtsingleapplication/doc/html/qtsinglecoreapplication.html b/src/qtsingleapplication/doc/html/qtsinglecoreapplication.html deleted file mode 100644 index a20cf2f4..00000000 --- a/src/qtsingleapplication/doc/html/qtsinglecoreapplication.html +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - QtSingleCoreApplication Class Reference - - - - - - - -
  Home

QtSingleCoreApplication Class Reference

-

A variant of the QtSingleApplication class for non-GUI applications. More...

-
 #include <QtSingleCoreApplication>

Inherits QCoreApplication.

- -
- -

Public Functions

- - - - - -
QtSingleCoreApplication ( int & argc, char ** argv )
QtSingleCoreApplication ( const QString & appId, int & argc, char ** argv )
QString id () const
bool isRunning ()
- -
- -

Public Slots

- - -
bool sendMessage ( const QString & message, int timeout = 5000 )
- -
- -

Signals

- - -
void messageReceived ( const QString & message )
- -

Additional Inherited Members

- - -
-

Detailed Description

-

A variant of the QtSingleApplication class for non-GUI applications.

-

This class is a variant of QtSingleApplication suited for use in console (non-GUI) applications. It is an extension of QCoreApplication (instead of QApplication). It does not require the QtGui library.

-

The API and usage is identical to QtSingleApplication, except that functions relating to the "activation window" are not present, for obvious reasons. Please refer to the QtSingleApplication documentation for explanation of the usage.

-

A QtSingleCoreApplication instance can communicate to a QtSingleApplication instance if they share the same application id. Hence, this class can be used to create a light-weight command-line tool that sends commands to a GUI application.

-

See also QtSingleApplication.

-
-

Member Function Documentation

-

QtSingleCoreApplication::QtSingleCoreApplication ( int & argc, char ** argv )

-

Creates a QtSingleCoreApplication object. The application identifier will be QCoreApplication::applicationFilePath(). argc and argv are passed on to the QCoreAppliation constructor.

-

QtSingleCoreApplication::QtSingleCoreApplication ( const QString & appId, int & argc, char ** argv )

-

Creates a QtSingleCoreApplication object with the application identifier appId. argc and argv are passed on to the QCoreAppliation constructor.

-

QString QtSingleCoreApplication::id () const

-

Returns the application identifier. Two processes with the same identifier will be regarded as instances of the same application.

-

bool QtSingleCoreApplication::isRunning ()

-

Returns true if another instance of this application is running; otherwise false.

-

This function does not find instances of this application that are being run by a different user (on Windows: that are running in another session).

-

See also sendMessage().

-

void QtSingleCoreApplication::messageReceived ( const QString & message )   [signal]

-

This signal is emitted when the current instance receives a message from another instance of this application.

-

See also sendMessage().

-

bool QtSingleCoreApplication::sendMessage ( const QString & message, int timeout = 5000 )   [slot]

-

Tries to send the text message to the currently running instance. The QtSingleCoreApplication object in the running instance will emit the messageReceived() signal when it receives the message.

-

This function returns true if the message has been sent to, and processed by, the current instance. If there is no instance currently running, or if the running instance fails to process the message within timeout milliseconds, this function return false.

-

See also isRunning() and messageReceived().

-


- - - - -
Copyright © 2010 Nokia Corporation and/or its subsidiary(-ies)Trademarks
Qt Solutions
- diff --git a/src/qtsingleapplication/doc/images/qt-logo.png b/src/qtsingleapplication/doc/images/qt-logo.png deleted file mode 100644 index 794162f5..00000000 Binary files a/src/qtsingleapplication/doc/images/qt-logo.png and /dev/null differ diff --git a/src/qtsingleapplication/doc/index.qdoc b/src/qtsingleapplication/doc/index.qdoc deleted file mode 100644 index 6a2a768a..00000000 --- a/src/qtsingleapplication/doc/index.qdoc +++ /dev/null @@ -1,87 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 Digia Plc and its Subsidiary(-ies) 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$ -** -****************************************************************************/ - -/*! - \page index.html - \title Single Application - - \section1 Description - - The QtSingleApplication component provides support - for applications that can be only started once per user. - - - - For some applications it is useful or even critical that they are started - only once by any user. Future attempts to start the application should - activate any already running instance, and possibly perform requested - actions, e.g. loading a file, in that instance. - - The QtSingleApplication class provides an interface to detect a running - instance, and to send command strings to that instance. - For console (non-GUI) applications, the QtSingleCoreApplication variant is provided, which avoids dependency on QtGui. - - - - - \section1 Classes - \list - \i QtSingleApplication \i QtSingleCoreApplication\endlist - - \section1 Examples - \list - \i \link qtsingleapplication-example-trivial.html A Trivial Example \endlink \i \link qtsingleapplication-example-loader.html Loading Documents \endlink \i \link qtsinglecoreapplication-example-console.html A Non-GUI Example \endlink \endlist - - - - - - - \section1 Tested platforms - \list - \i Qt 4.4, 4.5 / Windows XP / MSVC.NET 2005 - \i Qt 4.4, 4.5 / Linux / gcc - \i Qt 4.4, 4.5 / MacOS X 10.5 / gcc - \endlist - - - - -*/ diff --git a/src/qtsingleapplication/examples/console/console.pro b/src/qtsingleapplication/examples/console/console.pro deleted file mode 100644 index e0390e23..00000000 --- a/src/qtsingleapplication/examples/console/console.pro +++ /dev/null @@ -1,5 +0,0 @@ -TEMPLATE = app -CONFIG += console -SOURCES += main.cpp -include(../../src/qtsinglecoreapplication.pri) -QT -= gui diff --git a/src/qtsingleapplication/examples/console/console.qdoc b/src/qtsingleapplication/examples/console/console.qdoc deleted file mode 100644 index 77b0d736..00000000 --- a/src/qtsingleapplication/examples/console/console.qdoc +++ /dev/null @@ -1,65 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 Digia Plc and its Subsidiary(-ies) 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$ -** -****************************************************************************/ - -/*! \page qtsinglecoreapplication-example-console.html - \title A non-GUI example - - This example shows how to use the single-application functionality - in a console application. It does not require the \c QtGui library - at all. - - The only differences from the GUI application usage demonstrated - in the other examples are: - - 1) The \c.pro file should include \c qtsinglecoreapplication.pri - instead of \c qtsingleapplication.pri - - 2) The class name is \c QtSingleCoreApplication instead of \c - QtSingleApplication. - - 3) No calls are made regarding window activation, for obvious reasons. - - console.pro: - \quotefile console/console.pro - - main.cpp: - \quotefile console/main.cpp - -*/ diff --git a/src/qtsingleapplication/examples/console/main.cpp b/src/qtsingleapplication/examples/console/main.cpp deleted file mode 100644 index 652feb93..00000000 --- a/src/qtsingleapplication/examples/console/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 Digia Plc and its Subsidiary(-ies) 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 "qtsinglecoreapplication.h" -#include - - -void report(const QString& msg) -{ - qDebug("[%i] %s", (int)QCoreApplication::applicationPid(), qPrintable(msg)); -} - -class MainClass : public QObject -{ - Q_OBJECT -public: - MainClass() - : QObject() - {} - -public slots: - void handleMessage(const QString& message) - { - report( "Message received: \"" + message + "\""); - } -}; - -int main(int argc, char **argv) -{ - report("Starting up"); - - QtSingleCoreApplication app(argc, argv); - - if (app.isRunning()) { - QString msg(QString("Hi master, I am %1.").arg(QCoreApplication::applicationPid())); - bool sentok = app.sendMessage(msg, 2000); - QString rep("Another instance is running, so I will exit."); - rep += sentok ? " Message sent ok." : " Message sending failed; the other instance may be frozen."; - report(rep); - return 0; - } else { - report("No other instance is running; so I will."); - MainClass mainObj; - QObject::connect(&app, SIGNAL(messageReceived(const QString&)), - &mainObj, SLOT(handleMessage(const QString&))); - return app.exec(); - } -} - - -#include "main.moc" diff --git a/src/qtsingleapplication/examples/examples.pro b/src/qtsingleapplication/examples/examples.pro deleted file mode 100644 index 36b8fd38..00000000 --- a/src/qtsingleapplication/examples/examples.pro +++ /dev/null @@ -1,4 +0,0 @@ -TEMPLATE = subdirs -SUBDIRS = trivial \ - loader \ - console diff --git a/src/qtsingleapplication/examples/loader/file1.qsl b/src/qtsingleapplication/examples/loader/file1.qsl deleted file mode 100644 index 50fcd26d..00000000 --- a/src/qtsingleapplication/examples/loader/file1.qsl +++ /dev/null @@ -1 +0,0 @@ -File 1 diff --git a/src/qtsingleapplication/examples/loader/file2.qsl b/src/qtsingleapplication/examples/loader/file2.qsl deleted file mode 100644 index 4475433e..00000000 --- a/src/qtsingleapplication/examples/loader/file2.qsl +++ /dev/null @@ -1 +0,0 @@ -File 2 diff --git a/src/qtsingleapplication/examples/loader/loader.pro b/src/qtsingleapplication/examples/loader/loader.pro deleted file mode 100644 index 3e52586b..00000000 --- a/src/qtsingleapplication/examples/loader/loader.pro +++ /dev/null @@ -1,6 +0,0 @@ -greaterThan(QT_MAJOR_VERSION, 4): QT += printsupport -TEMPLATE = app - -include(../../src/qtsingleapplication.pri) - -SOURCES += main.cpp diff --git a/src/qtsingleapplication/examples/loader/loader.qdoc b/src/qtsingleapplication/examples/loader/loader.qdoc deleted file mode 100644 index bfd15d06..00000000 --- a/src/qtsingleapplication/examples/loader/loader.qdoc +++ /dev/null @@ -1,81 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 Digia Plc and its Subsidiary(-ies) 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$ -** -****************************************************************************/ - -/*! \page qtsingleapplication-example-loader.html - \title Loading Documents - - The application in this example loads or prints the documents - passed as commandline parameters to further instances of this - application. - - \quotefromfile loader/main.cpp - \printuntil }; - The user interface in this application is a QMainWindow subclass - with a QMdiArea as the central widget. It implements a slot - \c handleMessage() that will be connected to the messageReceived() - signal of the QtSingleApplication class. - - \printuntil } - The MainWindow constructor creates a minimal user interface. - - \printto case Print: - The handleMessage() slot interprets the message passed in as a - filename that can be prepended with \e /print to indicate that - the file should just be printed rather than loaded. - - \printto #include - Loading the file will also activate the window. - - \printto mw - The \c main entry point function creates a QtSingleApplication - object, and creates a message to send to a running instance - of the application. If the message was sent successfully the - process exits immediately. - - \printuntil } - If the message could not be sent the application starts up. Note - that \c false is passed to the call to setActivationWindow() to - prevent automatic activation for every message received, e.g. when - the application should just print a file. Instead, the message - handling function determines whether activation is requested, and - signals that by emitting the needToShow() signal. This is then - simply connected directly to QtSingleApplication's - activateWindow() slot. -*/ diff --git a/src/qtsingleapplication/examples/loader/main.cpp b/src/qtsingleapplication/examples/loader/main.cpp deleted file mode 100644 index f55e57ac..00000000 --- a/src/qtsingleapplication/examples/loader/main.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 Digia Plc and its Subsidiary(-ies) 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 -#include -#include -#include -#include -#include -#include -#include - -class MainWindow : public QMainWindow -{ - Q_OBJECT -public: - MainWindow(); - -public slots: - void handleMessage(const QString& message); - -signals: - void needToShow(); - -private: - QMdiArea *workspace; -}; - -MainWindow::MainWindow() -{ - workspace = new QMdiArea(this); - - setCentralWidget(workspace); -} - -void MainWindow::handleMessage(const QString& message) -{ - enum Action { - Nothing, - Open, - Print - } action; - - action = Nothing; - QString filename = message; - if (message.toLower().startsWith("/print ")) { - filename = filename.mid(7); - action = Print; - } else if (!message.isEmpty()) { - action = Open; - } - if (action == Nothing) { - emit needToShow(); - return; - } - - QFile file(filename); - QString contents; - if (file.open(QIODevice::ReadOnly)) - contents = file.readAll(); - else - contents = "[[Error: Could not load file " + filename + "]]"; - - QTextEdit *view = new QTextEdit; - view->setPlainText(contents); - - switch(action) { - case Print: - { - QPrinter printer; - view->print(&printer); - delete view; - } - break; - - case Open: - { - workspace->addSubWindow(view); - view->setWindowTitle(message); - view->show(); - emit needToShow(); - } - break; - default: - break; - }; -} - -#include "main.moc" - -int main(int argc, char **argv) -{ - QtSingleApplication instance("File loader QtSingleApplication example", argc, argv); - QString message; - for (int a = 1; a < argc; ++a) { - message += argv[a]; - if (a < argc-1) - message += " "; - } - - if (instance.sendMessage(message)) - return 0; - - MainWindow mw; - mw.handleMessage(message); - mw.show(); - - QObject::connect(&instance, SIGNAL(messageReceived(const QString&)), - &mw, SLOT(handleMessage(const QString&))); - - instance.setActivationWindow(&mw, false); - QObject::connect(&mw, SIGNAL(needToShow()), &instance, SLOT(activateWindow())); - - return instance.exec(); -} diff --git a/src/qtsingleapplication/examples/trivial/main.cpp b/src/qtsingleapplication/examples/trivial/main.cpp deleted file mode 100644 index 69e102f3..00000000 --- a/src/qtsingleapplication/examples/trivial/main.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 Digia Plc and its Subsidiary(-ies) 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 -#include - -class TextEdit : public QTextEdit -{ - Q_OBJECT -public: - TextEdit(QWidget *parent = 0) - : QTextEdit(parent) - {} -public slots: - void append(const QString &str) - { - QTextEdit::append(str); - } -}; - -#include "main.moc" - - - -int main(int argc, char **argv) -{ - QtSingleApplication instance(argc, argv); - if (instance.sendMessage("Wake up!")) - return 0; - - TextEdit logview; - logview.setReadOnly(true); - logview.show(); - - instance.setActivationWindow(&logview); - - QObject::connect(&instance, SIGNAL(messageReceived(const QString&)), - &logview, SLOT(append(const QString&))); - - return instance.exec(); -} diff --git a/src/qtsingleapplication/examples/trivial/trivial.pro b/src/qtsingleapplication/examples/trivial/trivial.pro deleted file mode 100644 index 673497a4..00000000 --- a/src/qtsingleapplication/examples/trivial/trivial.pro +++ /dev/null @@ -1,5 +0,0 @@ -TEMPLATE = app - -include(../../src/qtsingleapplication.pri) - -SOURCES += main.cpp diff --git a/src/qtsingleapplication/examples/trivial/trivial.qdoc b/src/qtsingleapplication/examples/trivial/trivial.qdoc deleted file mode 100644 index 9491cb65..00000000 --- a/src/qtsingleapplication/examples/trivial/trivial.qdoc +++ /dev/null @@ -1,76 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 Digia Plc and its Subsidiary(-ies) 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$ -** -****************************************************************************/ - -/*! \page qtsingleapplication-example-trivial.html - \title A Trivial Example - - The application in this example has a log-view that displays - messages sent by further instances of the same application. - - The example demonstrates the use of the QtSingleApplication - class to detect and communicate with a running instance of - the application using the sendMessage() API. The messageReceived() - signal is used to display received messages in a QTextEdit log. - - \quotefromfile trivial/main.cpp - \printuntil instance - The example has only the \c main entry point function. - A QtSingleApplication object is created immediately. - - \printuntil return - If another instance of this application is already running, - sendMessage() will succeed, and this instance just exits - immediately. - - \printuntil show() - Otherwise the instance continues as normal and creates the - user interface. - - \printuntil return instance.exec(); - The \c logview object is also set as the application's activation - window. Every time a message is received, the window will be raised - and activated automatically. - - The messageReceived() signal is also connected to the QTextEdit's - append() slot. Every message received from further instances of - this application will be displayed in the log. - - Finally the event loop is entered. -*/ diff --git a/src/qtsingleapplication/qtsingleapplication.pro b/src/qtsingleapplication/qtsingleapplication.pro deleted file mode 100644 index 07257c5d..00000000 --- a/src/qtsingleapplication/qtsingleapplication.pro +++ /dev/null @@ -1,5 +0,0 @@ -TEMPLATE=subdirs -CONFIG += ordered -include(common.pri) -qtsingleapplication-uselib:SUBDIRS=buildlib -SUBDIRS+=examples diff --git a/src/qtsingleapplication/src/QtLockedFile b/src/qtsingleapplication/src/QtLockedFile deleted file mode 100644 index 16b48ba9..00000000 --- a/src/qtsingleapplication/src/QtLockedFile +++ /dev/null @@ -1 +0,0 @@ -#include "qtlockedfile.h" diff --git a/src/qtsingleapplication/src/QtSingleApplication b/src/qtsingleapplication/src/QtSingleApplication deleted file mode 100644 index d111bf72..00000000 --- a/src/qtsingleapplication/src/QtSingleApplication +++ /dev/null @@ -1 +0,0 @@ -#include "qtsingleapplication.h" diff --git a/src/qtsingleapplication/src/qtlocalpeer.cpp b/src/qtsingleapplication/src/qtlocalpeer.cpp deleted file mode 100644 index f3c4546b..00000000 --- a/src/qtsingleapplication/src/qtlocalpeer.cpp +++ /dev/null @@ -1,213 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 Digia Plc and its Subsidiary(-ies) 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 "qtlocalpeer.h" -#include -#include -#include - -#if defined(Q_OS_WIN) -#include -#include -typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*); -static PProcessIdToSessionId pProcessIdToSessionId = 0; -#endif -#if defined(Q_OS_UNIX) -#include -#include -#include -#endif - -namespace QtLP_Private { -#include "qtlockedfile.cpp" -#if defined(Q_OS_WIN) -#include "qtlockedfile_win.cpp" -#else -#include "qtlockedfile_unix.cpp" -#endif -} - -const char* QtLocalPeer::ack = "ack"; - -QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId) - : QObject(parent), id(appId) -{ - QString prefix = id; - if (id.isEmpty()) { - id = QCoreApplication::applicationFilePath(); -#if defined(Q_OS_WIN) - id = id.toLower(); -#endif - prefix = id.section(QLatin1Char('/'), -1); - } - prefix.remove(QRegExp("[^a-zA-Z]")); - prefix.truncate(6); - - QByteArray idc = id.toUtf8(); - quint16 idNum = qChecksum(idc.constData(), idc.size()); - socketName = QLatin1String("qtsingleapp-") + prefix - + QLatin1Char('-') + QString::number(idNum, 16); - -#if defined(Q_OS_WIN) - if (!pProcessIdToSessionId) { - QLibrary lib("kernel32"); - pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId"); - } - if (pProcessIdToSessionId) { - DWORD sessionId = 0; - pProcessIdToSessionId(GetCurrentProcessId(), &sessionId); - socketName += QLatin1Char('-') + QString::number(sessionId, 16); - } -#else - socketName += QLatin1Char('-') + QString::number(::getuid(), 16); -#endif - - 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(QtLP_Private::QtLockedFile::WriteLock, false)) - return true; - - bool res = server->listen(socketName); -#if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(4,5,0)) - // ### Workaround - if (!res && server->serverError() == QAbstractSocket::AddressInUseError) { - QFile::remove(QDir::cleanPath(QDir::tempPath())+QLatin1Char('/')+socketName); - res = server->listen(socketName); - } -#endif - 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 QString &message, int timeout) -{ - 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.toUtf8()); - QDataStream ds(&socket); - ds.writeBytes(uMsg.constData(), uMsg.size()); - bool res = socket.waitForBytesWritten(timeout); - if (res) { - res &= socket.waitForReadyRead(timeout); // wait for ack - if (res) - res &= (socket.read(qstrlen(ack)) == ack); - } - return res; -} - - -void QtLocalPeer::receiveConnection() -{ - QLocalSocket* socket = server->nextPendingConnection(); - if (!socket) - return; - - while (true) { - if (socket->state() == QLocalSocket::UnconnectedState) { - qWarning("QtLocalPeer: Peer disconnected"); - delete socket; - return; - } - if (socket->bytesAvailable() >= qint64(sizeof(quint32))) - break; - socket->waitForReadyRead(); - } - - QDataStream ds(socket); - QByteArray uMsg; - quint32 remaining; - ds >> remaining; - uMsg.resize(remaining); - int got = 0; - char* uMsgBuf = uMsg.data(); - do { - got = ds.readRawData(uMsgBuf, remaining); - remaining -= got; - uMsgBuf += got; - } while (remaining && got >= 0 && socket->waitForReadyRead(2000)); - if (got < 0) { - qWarning("QtLocalPeer: Message reception failed %s", socket->errorString().toLatin1().constData()); - delete socket; - return; - } - QString message(QString::fromUtf8(uMsg)); - socket->write(ack, qstrlen(ack)); - socket->waitForBytesWritten(1000); - socket->waitForDisconnected(1000); // make sure client reads ack - delete socket; - emit messageReceived(message); //### (might take a long time to return) -} diff --git a/src/qtsingleapplication/src/qtlocalpeer.h b/src/qtsingleapplication/src/qtlocalpeer.h deleted file mode 100644 index 1b533b1a..00000000 --- a/src/qtsingleapplication/src/qtlocalpeer.h +++ /dev/null @@ -1,77 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 Digia Plc and its Subsidiary(-ies) 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 QTLOCALPEER_H -#define QTLOCALPEER_H - -#include -#include -#include - -#include "qtlockedfile.h" - -class QtLocalPeer : public QObject -{ - Q_OBJECT - -public: - QtLocalPeer(QObject *parent = 0, const QString &appId = QString()); - bool isClient(); - bool sendMessage(const QString &message, int timeout); - QString applicationId() const - { return id; } - -Q_SIGNALS: - void messageReceived(const QString &message); - -protected Q_SLOTS: - void receiveConnection(); - -protected: - QString id; - QString socketName; - QLocalServer* server; - QtLP_Private::QtLockedFile lockFile; - -private: - static const char* ack; -}; - -#endif // QTLOCALPEER_H diff --git a/src/qtsingleapplication/src/qtlockedfile.cpp b/src/qtsingleapplication/src/qtlockedfile.cpp deleted file mode 100644 index c142a863..00000000 --- a/src/qtsingleapplication/src/qtlockedfile.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 Digia Plc and its Subsidiary(-ies) 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 "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 - QLockedFile. 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 - wmutex = 0; - rmutex = 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 - wmutex = 0; - rmutex = 0; -#endif - m_lock_mode = NoLock; -} - -/*! - Opens the file in OpenMode \a mode. - - This is identical to QFile::open(), with the one exception that the - Truncate mode flag is disallowed. Truncation would conflict with the - advisory file locking, since the file would be modified before the - write lock is obtained. If truncation is required, use resize(0) - after obtaining the write lock. - - Returns true if successful; otherwise false. - - \sa QFile::open(), QFile::resize() -*/ -bool QtLockedFile::open(OpenMode mode) -{ - if (mode & QIODevice::Truncate) { - qWarning("QtLockedFile::open(): Truncate mode not allowed."); - return false; - } - return QFile::open(mode); -} - -/*! - 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. The file must be opened before it - can be locked. - - If \a block is true, this function will block until the lock is - aquired. If \a block is false, this function returns \e false - immediately if the lock cannot be aquired. - - 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/src/qtsingleapplication/src/qtlockedfile.h b/src/qtsingleapplication/src/qtlockedfile.h deleted file mode 100644 index 84c18e5c..00000000 --- a/src/qtsingleapplication/src/qtlockedfile.h +++ /dev/null @@ -1,97 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 Digia Plc and its Subsidiary(-ies) 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 QTLOCKEDFILE_H -#define QTLOCKEDFILE_H - -#include -#ifdef Q_OS_WIN -#include -#endif - -#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 - -namespace QtLP_Private { - -class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile -{ -public: - enum LockMode { NoLock = 0, ReadLock, WriteLock }; - - QtLockedFile(); - QtLockedFile(const QString &name); - ~QtLockedFile(); - - bool open(OpenMode mode); - - bool lock(LockMode mode, bool block = true); - bool unlock(); - bool isLocked() const; - LockMode lockMode() const; - -private: -#ifdef Q_OS_WIN - Qt::HANDLE wmutex; - Qt::HANDLE rmutex; - QVector rmutexes; - QString mutexname; - - Qt::HANDLE getMutexHandle(int idx, bool doCreate); - bool waitMutex(Qt::HANDLE mutex, bool doBlock); - -#endif - LockMode m_lock_mode; -}; -} -#endif diff --git a/src/qtsingleapplication/src/qtlockedfile_unix.cpp b/src/qtsingleapplication/src/qtlockedfile_unix.cpp deleted file mode 100644 index 976c1b9e..00000000 --- a/src/qtsingleapplication/src/qtlockedfile_unix.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 Digia Plc and its Subsidiary(-ies) 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 -#include -#include -#include - -#include "qtlockedfile.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; - return true; -} - -QtLockedFile::~QtLockedFile() -{ - if (isOpen()) - unlock(); -} - diff --git a/src/qtsingleapplication/src/qtlockedfile_win.cpp b/src/qtsingleapplication/src/qtlockedfile_win.cpp deleted file mode 100644 index 5e212620..00000000 --- a/src/qtsingleapplication/src/qtlockedfile_win.cpp +++ /dev/null @@ -1,211 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 Digia Plc and its Subsidiary(-ies) 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 "qtlockedfile.h" -#include -#include - -#define MUTEX_PREFIX "QtLockedFile mutex " -// Maximum number of concurrent read locks. Must not be greater than MAXIMUM_WAIT_OBJECTS -#define MAX_READERS MAXIMUM_WAIT_OBJECTS - -#if QT_VERSION >= 0x050000 -#define QT_WA(unicode, ansi) unicode -#endif - -Qt::HANDLE QtLockedFile::getMutexHandle(int idx, bool doCreate) -{ - if (mutexname.isEmpty()) { - QFileInfo fi(*this); - mutexname = QString::fromLatin1(MUTEX_PREFIX) - + fi.absoluteFilePath().toLower(); - } - QString mname(mutexname); - if (idx >= 0) - mname += QString::number(idx); - - Qt::HANDLE mutex; - if (doCreate) { - QT_WA( { mutex = CreateMutexW(NULL, FALSE, (TCHAR*)mname.utf16()); }, - { mutex = CreateMutexA(NULL, FALSE, mname.toLocal8Bit().constData()); } ); - if (!mutex) { - qErrnoWarning("QtLockedFile::lock(): CreateMutex failed"); - return 0; - } - } - else { - QT_WA( { mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, (TCHAR*)mname.utf16()); }, - { mutex = OpenMutexA(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, mname.toLocal8Bit().constData()); } ); - if (!mutex) { - if (GetLastError() != ERROR_FILE_NOT_FOUND) - qErrnoWarning("QtLockedFile::lock(): OpenMutex failed"); - return 0; - } - } - return mutex; -} - -bool QtLockedFile::waitMutex(Qt::HANDLE mutex, bool doBlock) -{ - Q_ASSERT(mutex); - DWORD res = WaitForSingleObject(mutex, doBlock ? INFINITE : 0); - switch (res) { - case WAIT_OBJECT_0: - case WAIT_ABANDONED: - return true; - break; - case WAIT_TIMEOUT: - break; - default: - qErrnoWarning("QtLockedFile::lock(): WaitForSingleObject failed"); - } - return false; -} - - - -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(); - - if (!wmutex && !(wmutex = getMutexHandle(-1, true))) - return false; - - if (!waitMutex(wmutex, block)) - return false; - - if (mode == ReadLock) { - int idx = 0; - for (; idx < MAX_READERS; idx++) { - rmutex = getMutexHandle(idx, false); - if (!rmutex || waitMutex(rmutex, false)) - break; - CloseHandle(rmutex); - } - bool ok = true; - if (idx >= MAX_READERS) { - qWarning("QtLockedFile::lock(): too many readers"); - rmutex = 0; - ok = false; - } - else if (!rmutex) { - rmutex = getMutexHandle(idx, true); - if (!rmutex || !waitMutex(rmutex, false)) - ok = false; - } - if (!ok && rmutex) { - CloseHandle(rmutex); - rmutex = 0; - } - ReleaseMutex(wmutex); - if (!ok) - return false; - } - else { - Q_ASSERT(rmutexes.isEmpty()); - for (int i = 0; i < MAX_READERS; i++) { - Qt::HANDLE mutex = getMutexHandle(i, false); - if (mutex) - rmutexes.append(mutex); - } - if (rmutexes.size()) { - DWORD res = WaitForMultipleObjects(rmutexes.size(), rmutexes.constData(), - TRUE, block ? INFINITE : 0); - if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) { - if (res != WAIT_TIMEOUT) - qErrnoWarning("QtLockedFile::lock(): WaitForMultipleObjects failed"); - m_lock_mode = WriteLock; // trick unlock() to clean up - semiyucky - unlock(); - 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; - - if (m_lock_mode == ReadLock) { - ReleaseMutex(rmutex); - CloseHandle(rmutex); - rmutex = 0; - } - else { - foreach(Qt::HANDLE mutex, rmutexes) { - ReleaseMutex(mutex); - CloseHandle(mutex); - } - rmutexes.clear(); - ReleaseMutex(wmutex); - } - - m_lock_mode = QtLockedFile::NoLock; - return true; -} - -QtLockedFile::~QtLockedFile() -{ - if (isOpen()) - unlock(); - if (wmutex) - CloseHandle(wmutex); -} diff --git a/src/qtsingleapplication/src/qtsingleapplication.cpp b/src/qtsingleapplication/src/qtsingleapplication.cpp deleted file mode 100644 index d0fb15d7..00000000 --- a/src/qtsingleapplication/src/qtsingleapplication.cpp +++ /dev/null @@ -1,347 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 Digia Plc and its Subsidiary(-ies) 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 "qtsingleapplication.h" -#include "qtlocalpeer.h" -#include - - -/*! - \class QtSingleApplication qtsingleapplication.h - \brief The QtSingleApplication class provides an API to detect and - communicate with running instances of an application. - - This class allows you to create applications where only one - instance should be running at a time. I.e., if the user tries to - launch another instance, the already running instance will be - activated instead. Another usecase is a client-server system, - where the first started instance will assume the role of server, - and the later instances will act as clients of that server. - - By default, the full path of the executable file is used to - determine whether two processes are instances of the same - application. You can also provide an explicit identifier string - that will be compared instead. - - The application should create the QtSingleApplication object early - in the startup phase, and call isRunning() to find out if another - instance of this application is already running. If isRunning() - returns false, it means that no other instance is running, and - this instance has assumed the role as the running instance. In - this case, the application should continue with the initialization - of the application user interface before entering the event loop - with exec(), as normal. - - The messageReceived() signal will be emitted when the running - application receives messages from another instance of the same - application. When a message is received it might be helpful to the - user to raise the application so that it becomes visible. To - facilitate this, QtSingleApplication provides the - setActivationWindow() function and the activateWindow() slot. - - If isRunning() returns true, another instance is already - running. It may be alerted to the fact that another instance has - started by using the sendMessage() function. Also data such as - startup parameters (e.g. the name of the file the user wanted this - new instance to open) can be passed to the running instance with - this function. Then, the application should terminate (or enter - client mode). - - If isRunning() returns true, but sendMessage() fails, that is an - indication that the running instance is frozen. - - Here's an example that shows how to convert an existing - application to use QtSingleApplication. It is very simple and does - not make use of all QtSingleApplication's functionality (see the - examples for that). - - \code - // Original - int main(int argc, char **argv) - { - QApplication app(argc, argv); - - MyMainWidget mmw; - mmw.show(); - return app.exec(); - } - - // Single instance - int main(int argc, char **argv) - { - QtSingleApplication app(argc, argv); - - if (app.isRunning()) - return !app.sendMessage(someDataString); - - MyMainWidget mmw; - app.setActivationWindow(&mmw); - mmw.show(); - return app.exec(); - } - \endcode - - Once this QtSingleApplication instance is destroyed (normally when - the process exits or crashes), when the user next attempts to run the - application this instance will not, of course, be encountered. The - next instance to call isRunning() or sendMessage() will assume the - role as the new running instance. - - For console (non-GUI) applications, QtSingleCoreApplication may be - used instead of this class, to avoid the dependency on the QtGui - library. - - \sa QtSingleCoreApplication -*/ - - -void QtSingleApplication::sysInit(const QString &appId) -{ - actWin = 0; - peer = new QtLocalPeer(this, appId); - connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); -} - - -/*! - Creates a QtSingleApplication object. The application identifier - will be QCoreApplication::applicationFilePath(). \a argc, \a - argv, and \a GUIenabled are passed on to the QAppliation constructor. - - If you are creating a console application (i.e. setting \a - GUIenabled to false), you may consider using - QtSingleCoreApplication instead. -*/ - -QtSingleApplication::QtSingleApplication(int &argc, char **argv, bool GUIenabled) - : QApplication(argc, argv, GUIenabled) -{ - sysInit(); -} - - -/*! - Creates a QtSingleApplication object with the application - identifier \a appId. \a argc and \a argv are passed on to the - QAppliation constructor. -*/ - -QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv) - : QApplication(argc, argv) -{ - sysInit(appId); -} - -#if QT_VERSION < 0x050000 - -/*! - Creates a QtSingleApplication object. The application identifier - will be QCoreApplication::applicationFilePath(). \a argc, \a - argv, and \a type are passed on to the QAppliation constructor. -*/ -QtSingleApplication::QtSingleApplication(int &argc, char **argv, Type type) - : QApplication(argc, argv, type) -{ - sysInit(); -} - - -# if defined(Q_WS_X11) -/*! - Special constructor for X11, ref. the documentation of - QApplication's corresponding constructor. The application identifier - will be QCoreApplication::applicationFilePath(). \a dpy, \a visual, - and \a cmap are passed on to the QApplication constructor. -*/ -QtSingleApplication::QtSingleApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE cmap) - : QApplication(dpy, visual, cmap) -{ - sysInit(); -} - -/*! - Special constructor for X11, ref. the documentation of - QApplication's corresponding constructor. The application identifier - will be QCoreApplication::applicationFilePath(). \a dpy, \a argc, \a - argv, \a visual, and \a cmap are passed on to the QApplication - constructor. -*/ -QtSingleApplication::QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) - : QApplication(dpy, argc, argv, visual, cmap) -{ - sysInit(); -} - -/*! - Special constructor for X11, ref. the documentation of - QApplication's corresponding constructor. The application identifier - will be \a appId. \a dpy, \a argc, \a - argv, \a visual, and \a cmap are passed on to the QApplication - constructor. -*/ -QtSingleApplication::QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) - : QApplication(dpy, argc, argv, visual, cmap) -{ - sysInit(appId); -} -# endif // Q_WS_X11 -#endif // QT_VERSION < 0x050000 - - -/*! - Returns true if another instance of this application is running; - otherwise false. - - This function does not find instances of this application that are - being run by a different user (on Windows: that are running in - another session). - - \sa sendMessage() -*/ - -bool QtSingleApplication::isRunning() -{ - return peer->isClient(); -} - - -/*! - Tries to send the text \a message to the currently running - instance. The QtSingleApplication object in the running instance - will emit the messageReceived() signal when it receives the - message. - - This function returns true if the message has been sent to, and - processed by, the current instance. If there is no instance - currently running, or if the running instance fails to process the - message within \a timeout milliseconds, this function return false. - - \sa isRunning(), messageReceived() -*/ -bool QtSingleApplication::sendMessage(const QString &message, int timeout) -{ - return peer->sendMessage(message, timeout); -} - - -/*! - Returns the application identifier. Two processes with the same - identifier will be regarded as instances of the same application. -*/ -QString QtSingleApplication::id() const -{ - return peer->applicationId(); -} - - -/*! - Sets the activation window of this application to \a aw. The - activation window is the widget that will be activated by - activateWindow(). This is typically the application's main window. - - If \a activateOnMessage is true (the default), the window will be - activated automatically every time a message is received, just prior - to the messageReceived() signal being emitted. - - \sa activateWindow(), messageReceived() -*/ - -void QtSingleApplication::setActivationWindow(QWidget* aw, bool activateOnMessage) -{ - actWin = aw; - if (activateOnMessage) - connect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow())); - else - disconnect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow())); -} - - -/*! - Returns the applications activation window if one has been set by - calling setActivationWindow(), otherwise returns 0. - - \sa setActivationWindow() -*/ -QWidget* QtSingleApplication::activationWindow() const -{ - return actWin; -} - - -/*! - De-minimizes, raises, and activates this application's activation window. - This function does nothing if no activation window has been set. - - This is a convenience function to show the user that this - application instance has been activated when he has tried to start - another instance. - - This function should typically be called in response to the - messageReceived() signal. By default, that will happen - automatically, if an activation window has been set. - - \sa setActivationWindow(), messageReceived(), initialize() -*/ -void QtSingleApplication::activateWindow() -{ - if (actWin) { - actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized); - actWin->raise(); - actWin->activateWindow(); - } -} - - -/*! - \fn void QtSingleApplication::messageReceived(const QString& message) - - This signal is emitted when the current instance receives a \a - message from another instance of this application. - - \sa sendMessage(), setActivationWindow(), activateWindow() -*/ - - -/*! - \fn void QtSingleApplication::initialize(bool dummy = true) - - \obsolete -*/ diff --git a/src/qtsingleapplication/src/qtsingleapplication.h b/src/qtsingleapplication/src/qtsingleapplication.h deleted file mode 100644 index 049406f7..00000000 --- a/src/qtsingleapplication/src/qtsingleapplication.h +++ /dev/null @@ -1,105 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 Digia Plc and its Subsidiary(-ies) 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 QTSINGLEAPPLICATION_H -#define QTSINGLEAPPLICATION_H - -#include - -class QtLocalPeer; - -#if defined(Q_OS_WIN) -# if !defined(QT_QTSINGLEAPPLICATION_EXPORT) && !defined(QT_QTSINGLEAPPLICATION_IMPORT) -# define QT_QTSINGLEAPPLICATION_EXPORT -# elif defined(QT_QTSINGLEAPPLICATION_IMPORT) -# if defined(QT_QTSINGLEAPPLICATION_EXPORT) -# undef QT_QTSINGLEAPPLICATION_EXPORT -# endif -# define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllimport) -# elif defined(QT_QTSINGLEAPPLICATION_EXPORT) -# undef QT_QTSINGLEAPPLICATION_EXPORT -# define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllexport) -# endif -#else -# define QT_QTSINGLEAPPLICATION_EXPORT -#endif - -class QT_QTSINGLEAPPLICATION_EXPORT QtSingleApplication : public QApplication -{ - Q_OBJECT - -public: - QtSingleApplication(int &argc, char **argv, bool GUIenabled = true); - QtSingleApplication(const QString &id, int &argc, char **argv); -#if QT_VERSION < 0x050000 - QtSingleApplication(int &argc, char **argv, Type type); -# if defined(Q_WS_X11) - QtSingleApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); - QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0); - QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); -# endif // Q_WS_X11 -#endif // QT_VERSION < 0x050000 - - bool isRunning(); - QString id() const; - - void setActivationWindow(QWidget* aw, bool activateOnMessage = true); - QWidget* activationWindow() const; - - // Obsolete: - void initialize(bool dummy = true) - { isRunning(); Q_UNUSED(dummy) } - -public Q_SLOTS: - bool sendMessage(const QString &message, int timeout = 5000); - void activateWindow(); - - -Q_SIGNALS: - void messageReceived(const QString &message); - - -private: - void sysInit(const QString &appId = QString()); - QtLocalPeer *peer; - QWidget *actWin; -}; - -#endif // QTSINGLEAPPLICATION_H diff --git a/src/qtsingleapplication/src/qtsingleapplication.pri b/src/qtsingleapplication/src/qtsingleapplication.pri deleted file mode 100644 index 6f2bced9..00000000 --- a/src/qtsingleapplication/src/qtsingleapplication.pri +++ /dev/null @@ -1,17 +0,0 @@ -include(../common.pri) -INCLUDEPATH += $$PWD -DEPENDPATH += $$PWD -QT *= network -greaterThan(QT_MAJOR_VERSION, 4): QT *= widgets - -qtsingleapplication-uselib:!qtsingleapplication-buildlib { - LIBS += -L$$QTSINGLEAPPLICATION_LIBDIR -l$$QTSINGLEAPPLICATION_LIBNAME -} else { - SOURCES += $$PWD/qtsingleapplication.cpp $$PWD/qtlocalpeer.cpp - HEADERS += $$PWD/qtsingleapplication.h $$PWD/qtlocalpeer.h -} - -win32 { - contains(TEMPLATE, lib):contains(CONFIG, shared):DEFINES += QT_QTSINGLEAPPLICATION_EXPORT - else:qtsingleapplication-uselib:DEFINES += QT_QTSINGLEAPPLICATION_IMPORT -} diff --git a/src/qtsingleapplication/src/qtsinglecoreapplication.cpp b/src/qtsingleapplication/src/qtsinglecoreapplication.cpp deleted file mode 100644 index 56345373..00000000 --- a/src/qtsingleapplication/src/qtsinglecoreapplication.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 Digia Plc and its Subsidiary(-ies) 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 "qtsinglecoreapplication.h" -#include "qtlocalpeer.h" - -/*! - \class QtSingleCoreApplication qtsinglecoreapplication.h - \brief A variant of the QtSingleApplication class for non-GUI applications. - - This class is a variant of QtSingleApplication suited for use in - console (non-GUI) applications. It is an extension of - QCoreApplication (instead of QApplication). It does not require - the QtGui library. - - The API and usage is identical to QtSingleApplication, except that - functions relating to the "activation window" are not present, for - obvious reasons. Please refer to the QtSingleApplication - documentation for explanation of the usage. - - A QtSingleCoreApplication instance can communicate to a - QtSingleApplication instance if they share the same application - id. Hence, this class can be used to create a light-weight - command-line tool that sends commands to a GUI application. - - \sa QtSingleApplication -*/ - -/*! - Creates a QtSingleCoreApplication object. The application identifier - will be QCoreApplication::applicationFilePath(). \a argc and \a - argv are passed on to the QCoreAppliation constructor. -*/ - -QtSingleCoreApplication::QtSingleCoreApplication(int &argc, char **argv) - : QCoreApplication(argc, argv) -{ - peer = new QtLocalPeer(this); - connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); -} - - -/*! - Creates a QtSingleCoreApplication object with the application - identifier \a appId. \a argc and \a argv are passed on to the - QCoreAppliation constructor. -*/ -QtSingleCoreApplication::QtSingleCoreApplication(const QString &appId, int &argc, char **argv) - : QCoreApplication(argc, argv) -{ - peer = new QtLocalPeer(this, appId); - connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); -} - - -/*! - Returns true if another instance of this application is running; - otherwise false. - - This function does not find instances of this application that are - being run by a different user (on Windows: that are running in - another session). - - \sa sendMessage() -*/ - -bool QtSingleCoreApplication::isRunning() -{ - return peer->isClient(); -} - - -/*! - Tries to send the text \a message to the currently running - instance. The QtSingleCoreApplication object in the running instance - will emit the messageReceived() signal when it receives the - message. - - This function returns true if the message has been sent to, and - processed by, the current instance. If there is no instance - currently running, or if the running instance fails to process the - message within \a timeout milliseconds, this function return false. - - \sa isRunning(), messageReceived() -*/ - -bool QtSingleCoreApplication::sendMessage(const QString &message, int timeout) -{ - return peer->sendMessage(message, timeout); -} - - -/*! - Returns the application identifier. Two processes with the same - identifier will be regarded as instances of the same application. -*/ - -QString QtSingleCoreApplication::id() const -{ - return peer->applicationId(); -} - - -/*! - \fn void QtSingleCoreApplication::messageReceived(const QString& message) - - This signal is emitted when the current instance receives a \a - message from another instance of this application. - - \sa sendMessage() -*/ diff --git a/src/qtsingleapplication/src/qtsinglecoreapplication.h b/src/qtsingleapplication/src/qtsinglecoreapplication.h deleted file mode 100644 index b87fffe4..00000000 --- a/src/qtsingleapplication/src/qtsinglecoreapplication.h +++ /dev/null @@ -1,71 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 Digia Plc and its Subsidiary(-ies) 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 QTSINGLECOREAPPLICATION_H -#define QTSINGLECOREAPPLICATION_H - -#include - -class QtLocalPeer; - -class QtSingleCoreApplication : public QCoreApplication -{ - Q_OBJECT - -public: - QtSingleCoreApplication(int &argc, char **argv); - QtSingleCoreApplication(const QString &id, int &argc, char **argv); - - bool isRunning(); - QString id() const; - -public Q_SLOTS: - bool sendMessage(const QString &message, int timeout = 5000); - - -Q_SIGNALS: - void messageReceived(const QString &message); - - -private: - QtLocalPeer* peer; -}; - -#endif // QTSINGLECOREAPPLICATION_H diff --git a/src/qtsingleapplication/src/qtsinglecoreapplication.pri b/src/qtsingleapplication/src/qtsinglecoreapplication.pri deleted file mode 100644 index d2d6cc3e..00000000 --- a/src/qtsingleapplication/src/qtsinglecoreapplication.pri +++ /dev/null @@ -1,10 +0,0 @@ -INCLUDEPATH += $$PWD -DEPENDPATH += $$PWD -HEADERS += $$PWD/qtsinglecoreapplication.h $$PWD/qtlocalpeer.h -SOURCES += $$PWD/qtsinglecoreapplication.cpp $$PWD/qtlocalpeer.cpp - -QT *= network - -win32:contains(TEMPLATE, lib):contains(CONFIG, shared) { - DEFINES += QT_QTSINGLECOREAPPLICATION_EXPORT=__declspec(dllexport) -} diff --git a/src/singleapplication/CHANGELOG.md b/src/singleapplication/CHANGELOG.md new file mode 100644 index 00000000..4368d0f5 --- /dev/null +++ b/src/singleapplication/CHANGELOG.md @@ -0,0 +1,306 @@ +Changelog +========= + +If by accident I have forgotten to credit someone in the CHANGELOG, email me and I will fix it. + + +__3.3.1__ +--------- + +* Added support for _AppImage_ dynamic executable paths. - _Michael Klein_ + +__3.3.0__ +--------- + +* Fixed message fragmentation issue causing crashes and incorrectly / inconsistently received messages. - _Nils Jeisecke_ + +__3.2.0__ +--------- + +* Added support for Qt 6 - _Jonas Kvinge_ +* Fixed warning in `Qt 5.9` with `min`/`max` functions on Windows - _Nick Korotysh_ +* Fix return value of connectToPrimary() when connect is successful - _Jonas Kvinge_ +* Fix build issue with MinGW GCC pedantic mode - _Iakov Kirilenko_ +* Fixed conversion from `int` to `quint32` and Clang Tidy warnings - _Hennadii Chernyshchyk_ + +__3.1.5__ +--------- + +* Improved library stability in edge cases and very rapid process initialisation +* Fixed Bug where the shared memory block may have been modified without a lock +* Fixed Bug causing `instanceStarted()` to not get emitted when a second instance + has been started before the primary has initiated it's `QLocalServer`. + +__3.1.4__ +--------- +* Officially supporting and build-testing against Qt 5.15 +* Fixed an MSVC C4996 warning that suggests using `strncpy_s`. + + _Hennadii Chernyshchyk_ + +__3.1.3.1__ +--------- +* CMake build system improvements +* Fixed Clang Tidy warnings + + _Hennadii Chernyshchyk_ + +__3.1.3__ +--------- +* Improved `CMakeLists.txt` + + _Hennadii Chernyshchyk_ + +__3.1.2__ +--------- + +* Fix a crash when exiting an application on Android and iOS + + _Emeric Grange_ + +__3.1.1a__ +---------- + +* Added currentUser() method that returns the user the current instance is running as. + + _Leander Schulten_ + +__3.1.0a__ +---------- + +* Added primaryUser() method that returns the user the primary instance is running as. + +__3.0.19__ +---------- + +* Fixed code warning for depricated functions in Qt 5.10 related to `QTime` and `qrand()`. + + _Hennadii Chernyshchyk_ + _Anton Filimonov_ + _Jonas Kvinge_ + +__3.0.18__ +---------- + +* Fallback to standard QApplication class on iOS and Android systems where + the library is not supported. + +* Added Build CI tests to verify the library builds successfully on Linux, Windows and MacOS across multiple Qt versions. + + _Anton Filimonov_ + +__3.0.17__ +---------- + +* Fixed compilation warning/error caused by `geteuid()` on unix based systems. + + _Iakov Kirilenko_ + +* Added CMake support + + _Hennadii Chernyshchyk_ + +__3.0.16__ +---------- + +* Use geteuid and getpwuid to get username on Unix, fallback to environment variable. + + _Jonas Kvinge_ + +__3.0.15__ +---------- + +* Bug Fix: sendMessage() might return false even though data was actually written. + + _Jonas Kvinge_ + +__3.0.14__ +---------- + +* Fixed uninitialised variables in the `SingleApplicationPrivate` constructor. + +__3.0.13a__ +---------- + +* Process socket events asynchronously +* Fix undefined variable error on Windows + + _Francis Giraldeau_ + +__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__ +--------- + +* Deprecated 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. diff --git a/src/singleapplication/CMakeLists.txt b/src/singleapplication/CMakeLists.txt new file mode 100644 index 00000000..ae1b1439 --- /dev/null +++ b/src/singleapplication/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.7.0) + +project(SingleApplication LANGUAGES CXX) + +set(CMAKE_AUTOMOC ON) + +add_library(${PROJECT_NAME} STATIC + singleapplication.cpp + singleapplication_p.cpp +) +add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) + +if(NOT QT_DEFAULT_MAJOR_VERSION) + set(QT_DEFAULT_MAJOR_VERSION 5 CACHE STRING "Qt version to use (5 or 6), defaults to 5") +endif() + +# Find dependencies +set(QT_COMPONENTS Core Network) +set(QT_LIBRARIES Qt${QT_DEFAULT_MAJOR_VERSION}::Core Qt${QT_DEFAULT_MAJOR_VERSION}::Network) + +if(QAPPLICATION_CLASS STREQUAL QApplication) + list(APPEND QT_COMPONENTS Widgets) + list(APPEND QT_LIBRARIES Qt${QT_DEFAULT_MAJOR_VERSION}::Widgets) +elseif(QAPPLICATION_CLASS STREQUAL QGuiApplication) + list(APPEND QT_COMPONENTS Gui) + list(APPEND QT_LIBRARIES Qt${QT_DEFAULT_MAJOR_VERSION}::Gui) +else() + set(QAPPLICATION_CLASS QCoreApplication) +endif() + +find_package(Qt${QT_DEFAULT_MAJOR_VERSION} COMPONENTS ${QT_COMPONENTS} REQUIRED) + +target_link_libraries(${PROJECT_NAME} PUBLIC ${QT_LIBRARIES}) + +if(WIN32) + target_link_libraries(${PROJECT_NAME} PRIVATE advapi32) +endif() + +target_compile_definitions(${PROJECT_NAME} PUBLIC QAPPLICATION_CLASS=${QAPPLICATION_CLASS}) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/src/singleapplication/LICENSE b/src/singleapplication/LICENSE new file mode 100644 index 00000000..a82e5a68 --- /dev/null +++ b/src/singleapplication/LICENSE @@ -0,0 +1,24 @@ +The MIT License (MIT) + +Copyright (c) Itay Grudev 2015 - 2020 + +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. diff --git a/src/singleapplication/README.md b/src/singleapplication/README.md new file mode 100644 index 00000000..a76bada3 --- /dev/null +++ b/src/singleapplication/README.md @@ -0,0 +1,305 @@ +SingleApplication +================= +[![CI](https://github.com/itay-grudev/SingleApplication/workflows/CI:%20Build%20Test/badge.svg)](https://github.com/itay-grudev/SingleApplication/actions) + +This is a replacement of the QtSingleApplication for `Qt5` and `Qt6`. + +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. + +You can use the library as if you use any other `QCoreApplication` derived +class: + +```cpp +#include +#include + +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. Here is how: + +```bash +git submodule add git@github.com:itay-grudev/SingleApplication.git singleapplication +``` + +**Qmake:** + +Then include the `singleapplication.pri` file in your `.pro` project file. + +```qmake +include(singleapplication/singleapplication.pri) +DEFINES += QAPPLICATION_CLASS=QApplication +``` + +**CMake:** + +Then include the subdirectory in your `CMakeLists.txt` project file. + +```cmake +set(QAPPLICATION_CLASS QApplication CACHE STRING "Inheritance class for SingleApplication") +add_subdirectory(src/third-party/singleapplication) +target_link_libraries(${PROJECT_NAME} SingleApplication::SingleApplication) +``` + + +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. + +Also don't forget to specify which `QCoreApplication` class your app is using if it +is not `QCoreApplication` as in examples above. + +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.* + +Examples +-------- + +There are three examples provided in this repository: + +* Basic example that prevents a secondary instance from starting [`examples/basic`](https://github.com/itay-grudev/SingleApplication/tree/master/examples/basic) +* An example of a graphical application raising it's parent window [`examples/calculator`](https://github.com/itay-grudev/SingleApplication/tree/master/examples/calculator) +* A console application sending the primary instance it's command line parameters [`examples/sending_arguments`](https://github.com/itay-grudev/SingleApplication/tree/master/examples/sending_arguments) + +API +--- + +### Members + +```cpp +SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 100, QString userData = QString() ) +``` + +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. Setting `userData` provides additional data that will isolate this instance from other instances that do not have the same (or any) user data set. + +*__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. Returns `true` if the message has been sent +successfully. If the message can't be sent or the function timeouts - returns `false`. + +--- + +```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. + +--- + +```cpp +QString SingleApplication::primaryUser() +``` + +Returns the username the primary instance is running as. + +--- + +```cpp +QString SingleApplication::currentUser() +``` + +Returns the username the current instance is running as. + +### 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. diff --git a/src/singleapplication/SingleApplication b/src/singleapplication/SingleApplication new file mode 100644 index 00000000..8ead1a42 --- /dev/null +++ b/src/singleapplication/SingleApplication @@ -0,0 +1 @@ +#include "singleapplication.h" diff --git a/src/singleapplication/Windows.md b/src/singleapplication/Windows.md new file mode 100644 index 00000000..13c52da0 --- /dev/null +++ b/src/singleapplication/Windows.md @@ -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.primaryPid() ) ); +} + +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 diff --git a/src/singleapplication/singleapplication.cpp b/src/singleapplication/singleapplication.cpp new file mode 100644 index 00000000..09e264ef --- /dev/null +++ b/src/singleapplication/singleapplication.cpp @@ -0,0 +1,271 @@ +// The MIT License (MIT) +// +// Copyright (c) Itay Grudev 2015 - 2020 +// +// 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 +#include +#include + +#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 allowSecondary Whether to enable secondary instance support + * @param options Optional flags to toggle specific behaviour + * @param timeout Maximum time blocking functions are allowed during app load + */ +SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary, Options options, int timeout, const QString &userData ) + : app_t( argc, argv ), d_ptr( new SingleApplicationPrivate( this ) ) +{ + Q_D( SingleApplication ); + +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) + // On Android and iOS since the library is not supported fallback to + // standard QApplication behaviour by simply returning at this point. + qWarning() << "SingleApplication is not supported on Android and iOS systems."; + return; +#endif + + // Store the current mode of the program + d->options = options; + + // Add any unique user data + if ( ! userData.isEmpty() ) + d->addAppData( userData ); + + // Generating an application ID used for identifying the shared memory + // block and QLocalServer + d->genBlockServerName(); + + // To mitigate QSharedMemory issues with large amount of processes + // attempting to attach at the same time + SingleApplicationPrivate::randomSleep(); + +#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 + if( ! d->memory->lock() ){ + qCritical() << "SingleApplication: Unable to lock memory block after create."; + abortSafely(); + } + d->initializeMemoryBlock(); + } else { + if( d->memory->error() == QSharedMemory::AlreadyExists ){ + // Attempt to attach to the memory segment + if( ! d->memory->attach() ){ + qCritical() << "SingleApplication: Unable to attach to shared memory block."; + abortSafely(); + } + if( ! d->memory->lock() ){ + qCritical() << "SingleApplication: Unable to lock memory block after attach."; + abortSafely(); + } + } else { + qCritical() << "SingleApplication: Unable to create block."; + abortSafely(); + } + } + + auto *inst = static_cast( d->memory->data() ); + QElapsedTimer time; + time.start(); + + // Make sure the shared memory block is initialised and in consistent state + while( true ){ + // If the shared memory block's checksum is valid continue + if( d->blockChecksum() == inst->checksum ) break; + + // If more than 5s have elapsed, assume the primary instance crashed and + // assume it's position + 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(); + } + + // Otherwise wait for a random period and try again. The random sleep here + // limits the probability of a collision between two racing apps and + // allows the app to initialise faster + if( ! d->memory->unlock() ){ + qDebug() << "SingleApplication: Unable to unlock memory for random wait."; + qDebug() << d->memory->errorString(); + } + SingleApplicationPrivate::randomSleep(); + if( ! d->memory->lock() ){ + qCritical() << "SingleApplication: Unable to lock memory after random wait."; + abortSafely(); + } + } + + if( inst->primary == false ){ + d->startPrimary(); + if( ! d->memory->unlock() ){ + qDebug() << "SingleApplication: Unable to unlock memory after primary start."; + qDebug() << d->memory->errorString(); + } + return; + } + + // Check if another instance can be started + if( allowSecondary ){ + d->startSecondary(); + if( d->options & Mode::SecondaryNotification ){ + d->connectToPrimary( timeout, SingleApplicationPrivate::SecondaryInstance ); + } + if( ! d->memory->unlock() ){ + qDebug() << "SingleApplication: Unable to unlock memory after secondary start."; + qDebug() << d->memory->errorString(); + } + return; + } + + if( ! d->memory->unlock() ){ + qDebug() << "SingleApplication: Unable to unlock memory at end of execution."; + qDebug() << d->memory->errorString(); + } + + d->connectToPrimary( timeout, SingleApplicationPrivate::NewInstance ); + + delete d; + + ::exit( EXIT_SUCCESS ); +} + +SingleApplication::~SingleApplication() +{ + Q_D( SingleApplication ); + delete d; +} + +/** + * Checks if the current application instance is primary. + * @return Returns true if the instance is primary, false otherwise. + */ +bool SingleApplication::isPrimary() const +{ + Q_D( const SingleApplication ); + return d->server != nullptr; +} + +/** + * Checks if the current application instance is secondary. + * @return Returns true if the instance is secondary, false otherwise. + */ +bool SingleApplication::isSecondary() const +{ + Q_D( const SingleApplication ); + return d->server == nullptr; +} + +/** + * Allows you to identify an instance by returning unique consecutive instance + * ids. It is reset when the first (primary) instance of your app starts and + * only incremented afterwards. + * @return Returns a unique instance id. + */ +quint32 SingleApplication::instanceId() const +{ + Q_D( const SingleApplication ); + return d->instanceNumber; +} + +/** + * Returns the OS PID (Process Identifier) of the process running the primary + * instance. Especially useful when SingleApplication is coupled with OS. + * specific APIs. + * @return Returns the primary instance PID. + */ +qint64 SingleApplication::primaryPid() const +{ + Q_D( const SingleApplication ); + return d->primaryPid(); +} + +/** + * Returns the username the primary instance is running as. + * @return Returns the username the primary instance is running as. + */ +QString SingleApplication::primaryUser() const +{ + Q_D( const SingleApplication ); + return d->primaryUser(); +} + +/** + * Returns the username the current instance is running as. + * @return Returns the username the current instance is running as. + */ +QString SingleApplication::currentUser() const +{ + return SingleApplicationPrivate::getUsername(); +} + +/** + * Sends message to the Primary Instance. + * @param message The message to send. + * @param timeout the maximum timeout in milliseconds for blocking functions. + * @return true if the message was sent successfuly, false otherwise. + */ +bool SingleApplication::sendMessage( const QByteArray &message, int timeout ) +{ + Q_D( SingleApplication ); + + // Nobody to connect to + if( isPrimary() ) return false; + + // Make sure the socket is connected + if( ! d->connectToPrimary( timeout, SingleApplicationPrivate::Reconnect ) ) + return false; + + return d->writeConfirmedMessage( timeout, message ); +} + +/** + * Cleans up the shared memory block and exits with a failure. + * This function halts program execution. + */ +void SingleApplication::abortSafely() +{ + Q_D( SingleApplication ); + + qCritical() << "SingleApplication: " << d->memory->error() << d->memory->errorString(); + delete d; + ::exit( EXIT_FAILURE ); +} + +QStringList SingleApplication::userData() const +{ + Q_D( const SingleApplication ); + return d->appData(); +} diff --git a/src/singleapplication/singleapplication.h b/src/singleapplication/singleapplication.h new file mode 100644 index 00000000..91cabf93 --- /dev/null +++ b/src/singleapplication/singleapplication.h @@ -0,0 +1,154 @@ +// 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 +#include + +#ifndef QAPPLICATION_CLASS + #define QAPPLICATION_CLASS QCoreApplication +#endif + +#include QT_STRINGIFY(QAPPLICATION_CLASS) + +class SingleApplicationPrivate; + +/** + * @brief The SingleApplication class handles multiple instances of the same + * Application + * @see QCoreApplication + */ +class SingleApplication : public QAPPLICATION_CLASS +{ + Q_OBJECT + + using app_t = QAPPLICATION_CLASS; + +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 milliseconds. + * @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, const QString &userData = {} ); + ~SingleApplication() override; + + /** + * @brief Returns if the instance is the primary instance + * @returns {bool} + */ + bool isPrimary() const; + + /** + * @brief Returns if the instance is a secondary instance + * @returns {bool} + */ + bool isSecondary() const; + + /** + * @brief Returns a unique identifier for the current instance + * @returns {qint32} + */ + quint32 instanceId() const; + + /** + * @brief Returns the process ID (PID) of the primary instance + * @returns {qint64} + */ + qint64 primaryPid() const; + + /** + * @brief Returns the username of the user running the primary instance + * @returns {QString} + */ + QString primaryUser() const; + + /** + * @brief Returns the username of the current user + * @returns {QString} + */ + QString currentUser() const; + + /** + * @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( const QByteArray &message, int timeout = 100 ); + + /** + * @brief Get the set user data. + * @returns {QStringList} + */ + QStringList userData() const; + +Q_SIGNALS: + void instanceStarted(); + void receivedMessage( quint32 instanceId, QByteArray message ); + +private: + SingleApplicationPrivate *d_ptr; + Q_DECLARE_PRIVATE(SingleApplication) + void abortSafely(); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(SingleApplication::Options) + +#endif // SINGLE_APPLICATION_H diff --git a/src/singleapplication/singleapplication.pri b/src/singleapplication/singleapplication.pri new file mode 100644 index 00000000..ae81f599 --- /dev/null +++ b/src/singleapplication/singleapplication.pri @@ -0,0 +1,20 @@ +QT += core network +CONFIG += c++11 + +HEADERS += $$PWD/SingleApplication \ + $$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 diff --git a/src/singleapplication/singleapplication_p.cpp b/src/singleapplication/singleapplication_p.cpp new file mode 100644 index 00000000..1a061f23 --- /dev/null +++ b/src/singleapplication/singleapplication_p.cpp @@ -0,0 +1,538 @@ +// The MIT License (MIT) +// +// Copyright (c) Itay Grudev 2015 - 2020 +// +// 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) +#include +#else +#include +#endif + +#include "singleapplication.h" +#include "singleapplication_p.h" + +#ifdef Q_OS_UNIX + #include + #include + #include +#endif + +#ifdef Q_OS_WIN + #ifndef NOMINMAX + #define NOMINMAX 1 + #endif + #include + #include +#endif + +SingleApplicationPrivate::SingleApplicationPrivate( SingleApplication *q_ptr ) + : q_ptr( q_ptr ) +{ + server = nullptr; + socket = nullptr; + memory = nullptr; + instanceNumber = 0; +} + +SingleApplicationPrivate::~SingleApplicationPrivate() +{ + if( socket != nullptr ){ + socket->close(); + delete socket; + } + + if( memory != nullptr ){ + memory->lock(); + auto *inst = static_cast(memory->data()); + if( server != nullptr ){ + server->close(); + delete server; + inst->primary = false; + inst->primaryPid = -1; + inst->primaryUser[0] = '\0'; + inst->checksum = blockChecksum(); + } + memory->unlock(); + + delete memory; + } +} + +QString SingleApplicationPrivate::getUsername() +{ +#ifdef Q_OS_WIN + wchar_t username[UNLEN + 1]; + // Specifies size of the buffer on input + DWORD usernameLength = UNLEN + 1; + if( GetUserNameW( username, &usernameLength ) ) + return QString::fromWCharArray( username ); +#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) + return QString::fromLocal8Bit( qgetenv( "USERNAME" ) ); +#else + return qEnvironmentVariable( "USERNAME" ); +#endif +#endif +#ifdef Q_OS_UNIX + QString username; + uid_t uid = geteuid(); + struct passwd *pw = getpwuid( uid ); + if( pw ) + username = QString::fromLocal8Bit( pw->pw_name ); + if ( username.isEmpty() ){ +#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) + username = QString::fromLocal8Bit( qgetenv( "USER" ) ); +#else + username = qEnvironmentVariable( "USER" ); +#endif + } + return username; +#endif +} + +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 ( ! appDataList.isEmpty() ) + appData.addData( appDataList.join( "" ).toUtf8() ); + + if( ! (options & SingleApplication::Mode::ExcludeAppVersion) ){ + appData.addData( SingleApplication::app_t::applicationVersion().toUtf8() ); + } + + if( ! (options & SingleApplication::Mode::ExcludeAppPath) ){ +#if defined(Q_OS_WIN) + appData.addData( SingleApplication::app_t::applicationFilePath().toLower().toUtf8() ); +#elif defined(Q_OS_LINUX) + // If the application is running as an AppImage then the APPIMAGE env var should be used + // instead of applicationPath() as each instance is launched with its own executable path + const QByteArray appImagePath = qgetenv( "APPIMAGE" ); + if( appImagePath.isEmpty() ){ // Not running as AppImage: use path to executable file + appData.addData( SingleApplication::app_t::applicationFilePath().toUtf8() ); + } else { // Running as AppImage: Use absolute path to AppImage file + appData.addData( appImagePath ); + }; +#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 ){ + appData.addData( getUsername().toUtf8() ); + } + + // 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() const +{ + auto *inst = static_cast( memory->data() ); + inst->primary = false; + inst->secondary = 0; + inst->primaryPid = -1; + inst->primaryUser[0] = '\0'; + inst->checksum = blockChecksum(); +} + +void SingleApplicationPrivate::startPrimary() +{ + // Reset the number of connections + auto *inst = static_cast ( memory->data() ); + + inst->primary = true; + inst->primaryPid = QCoreApplication::applicationPid(); + qstrncpy( inst->primaryUser, getUsername().toUtf8().data(), sizeof(inst->primaryUser) ); + inst->checksum = blockChecksum(); + instanceNumber = 0; + // 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 + ); +} + +void SingleApplicationPrivate::startSecondary() +{ + auto *inst = static_cast ( memory->data() ); + + inst->secondary += 1; + inst->checksum = blockChecksum(); + instanceNumber = inst->secondary; +} + +bool SingleApplicationPrivate::connectToPrimary( int msecs, ConnectionType connectionType ) +{ + QElapsedTimer time; + time.start(); + + // Connect to the Local Server of the Primary Instance if not already + // connected. + if( socket == nullptr ){ + socket = new QLocalSocket(); + } + + if( socket->state() == QLocalSocket::ConnectedState ) return true; + + if( socket->state() != QLocalSocket::ConnectedState ){ + + while( true ){ + randomSleep(); + + if( socket->state() != QLocalSocket::ConnectingState ) + socket->connectToServer( blockServerName ); + + if( socket->state() == QLocalSocket::ConnectingState ){ + socket->waitForConnected( static_cast(msecs - time.elapsed()) ); + } + + // If connected break out of the loop + if( socket->state() == QLocalSocket::ConnectedState ) break; + + // If elapsed time since start is longer than the method timeout return + if( time.elapsed() >= msecs ) return false; + } + } + + // Initialisation message according to the SingleApplication protocol + QByteArray initMsg; + QDataStream writeStream(&initMsg, QIODevice::WriteOnly); + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + writeStream.setVersion(QDataStream::Qt_5_6); +#endif + + writeStream << blockServerName.toLatin1(); + writeStream << static_cast(connectionType); + writeStream << instanceNumber; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + quint16 checksum = qChecksum(QByteArray(initMsg, static_cast(initMsg.length()))); +#else + quint16 checksum = qChecksum(initMsg.constData(), static_cast(initMsg.length())); +#endif + writeStream << checksum; + + return writeConfirmedMessage( static_cast(msecs - time.elapsed()), initMsg ); +} + +void SingleApplicationPrivate::writeAck( QLocalSocket *sock ) { + sock->putChar('\n'); +} + +bool SingleApplicationPrivate::writeConfirmedMessage (int msecs, const QByteArray &msg) +{ + QElapsedTimer time; + time.start(); + + // Frame 1: The header indicates the message length that follows + QByteArray header; + QDataStream headerStream(&header, QIODevice::WriteOnly); + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + headerStream.setVersion(QDataStream::Qt_5_6); +#endif + headerStream << static_cast ( msg.length() ); + + if( ! writeConfirmedFrame( static_cast(msecs - time.elapsed()), header )) + return false; + + // Frame 2: The message + return writeConfirmedFrame( static_cast(msecs - time.elapsed()), msg ); +} + +bool SingleApplicationPrivate::writeConfirmedFrame( int msecs, const QByteArray &msg ) +{ + socket->write( msg ); + socket->flush(); + + bool result = socket->waitForReadyRead( msecs ); // await ack byte + if (result) { + socket->read( 1 ); + return true; + } + + return false; +} + +quint16 SingleApplicationPrivate::blockChecksum() const +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + quint16 checksum = qChecksum(QByteArray(static_cast(memory->constData()), offsetof(InstancesInfo, checksum))); +#else + quint16 checksum = qChecksum(static_cast(memory->constData()), offsetof(InstancesInfo, checksum)); +#endif + return checksum; +} + +qint64 SingleApplicationPrivate::primaryPid() const +{ + qint64 pid; + + memory->lock(); + auto *inst = static_cast( memory->data() ); + pid = inst->primaryPid; + memory->unlock(); + + return pid; +} + +QString SingleApplicationPrivate::primaryUser() const +{ + QByteArray username; + + memory->lock(); + auto *inst = static_cast( memory->data() ); + username = inst->primaryUser; + memory->unlock(); + + return QString::fromUtf8( username ); +} + +/** + * @brief Executed when a connection has been made to the LocalServer + */ +void SingleApplicationPrivate::slotConnectionEstablished() +{ + QLocalSocket *nextConnSocket = server->nextPendingConnection(); + connectionMap.insert(nextConnSocket, ConnectionInfo()); + + QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, this, + [nextConnSocket, this](){ + auto &info = connectionMap[nextConnSocket]; + this->slotClientConnectionClosed( nextConnSocket, info.instanceId ); + } + ); + + QObject::connect(nextConnSocket, &QLocalSocket::disconnected, nextConnSocket, &QLocalSocket::deleteLater); + + QObject::connect(nextConnSocket, &QLocalSocket::destroyed, this, + [nextConnSocket, this](){ + connectionMap.remove(nextConnSocket); + } + ); + + QObject::connect(nextConnSocket, &QLocalSocket::readyRead, this, + [nextConnSocket, this](){ + auto &info = connectionMap[nextConnSocket]; + switch(info.stage){ + case StageInitHeader: + readMessageHeader( nextConnSocket, StageInitBody ); + break; + case StageInitBody: + readInitMessageBody(nextConnSocket); + break; + case StageConnectedHeader: + readMessageHeader( nextConnSocket, StageConnectedBody ); + break; + case StageConnectedBody: + this->slotDataAvailable( nextConnSocket, info.instanceId ); + break; + default: + break; + }; + } + ); +} + +void SingleApplicationPrivate::readMessageHeader( QLocalSocket *sock, SingleApplicationPrivate::ConnectionStage nextStage ) +{ + if (!connectionMap.contains( sock )){ + return; + } + + if( sock->bytesAvailable() < ( qint64 )sizeof( quint64 ) ){ + return; + } + + QDataStream headerStream( sock ); + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + headerStream.setVersion( QDataStream::Qt_5_6 ); +#endif + + // Read the header to know the message length + quint64 msgLen = 0; + headerStream >> msgLen; + ConnectionInfo &info = connectionMap[sock]; + info.stage = nextStage; + info.msgLen = msgLen; + + writeAck( sock ); +} + +bool SingleApplicationPrivate::isFrameComplete( QLocalSocket *sock ) +{ + if (!connectionMap.contains( sock )){ + return false; + } + + ConnectionInfo &info = connectionMap[sock]; + if( sock->bytesAvailable() < ( qint64 )info.msgLen ){ + return false; + } + + return true; +} + +void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock ) +{ + Q_Q(SingleApplication); + + if( !isFrameComplete( sock ) ) + return; + + // Read the message body + QByteArray msgBytes = sock->readAll(); + QDataStream readStream(msgBytes); + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + readStream.setVersion( QDataStream::Qt_5_6 ); +#endif + + // server name + QByteArray latin1Name; + readStream >> latin1Name; + + // connection type + ConnectionType connectionType = InvalidConnection; + quint8 connTypeVal = InvalidConnection; + readStream >> connTypeVal; + connectionType = static_cast ( connTypeVal ); + + // instance id + quint32 instanceId = 0; + readStream >> instanceId; + + // checksum + quint16 msgChecksum = 0; + readStream >> msgChecksum; + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + const quint16 actualChecksum = qChecksum(QByteArray(msgBytes, static_cast(msgBytes.length() - sizeof(quint16)))); +#else + const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast(msgBytes.length() - sizeof(quint16))); +#endif + + bool isValid = readStream.status() == QDataStream::Ok && + QLatin1String(latin1Name) == blockServerName && + msgChecksum == actualChecksum; + + if( !isValid ){ + sock->close(); + return; + } + + ConnectionInfo &info = connectionMap[sock]; + info.instanceId = instanceId; + info.stage = StageConnectedHeader; + + if( connectionType == NewInstance || + ( connectionType == SecondaryInstance && + options & SingleApplication::Mode::SecondaryNotification ) ) + { + Q_EMIT q->instanceStarted(); + } + + writeAck( sock ); +} + +void SingleApplicationPrivate::slotDataAvailable( QLocalSocket *dataSocket, quint32 instanceId ) +{ + Q_Q(SingleApplication); + + if ( !isFrameComplete( dataSocket ) ) + return; + + Q_EMIT q->receivedMessage( instanceId, dataSocket->readAll() ); + + writeAck( dataSocket ); + + ConnectionInfo &info = connectionMap[dataSocket]; + info.stage = StageConnectedHeader; +} + +void SingleApplicationPrivate::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId ) +{ + if( closedSocket->bytesAvailable() > 0 ) + slotDataAvailable( closedSocket, instanceId ); +} + +void SingleApplicationPrivate::randomSleep() +{ +#if QT_VERSION >= QT_VERSION_CHECK( 5, 10, 0 ) + QThread::msleep( QRandomGenerator::global()->bounded( 8u, 18u )); +#else + qsrand( QDateTime::currentMSecsSinceEpoch() % std::numeric_limits::max() ); + QThread::msleep( qrand() % 11 + 8); +#endif +} + +void SingleApplicationPrivate::addAppData(const QString &data) +{ + appDataList.push_back(data); +} + +QStringList SingleApplicationPrivate::appData() const +{ + return appDataList; +} diff --git a/src/singleapplication/singleapplication_p.h b/src/singleapplication/singleapplication_p.h new file mode 100644 index 00000000..58507cf3 --- /dev/null +++ b/src/singleapplication/singleapplication_p.h @@ -0,0 +1,109 @@ +// The MIT License (MIT) +// +// Copyright (c) Itay Grudev 2015 - 2020 +// +// 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 +#include +#include +#include "singleapplication.h" + +struct InstancesInfo { + bool primary; + quint32 secondary; + qint64 primaryPid; + char primaryUser[128]; + quint16 checksum; // Must be the last field +}; + +struct ConnectionInfo { + qint64 msgLen = 0; + quint32 instanceId = 0; + quint8 stage = 0; +}; + +class SingleApplicationPrivate : public QObject { +Q_OBJECT +public: + enum ConnectionType : quint8 { + InvalidConnection = 0, + NewInstance = 1, + SecondaryInstance = 2, + Reconnect = 3 + }; + enum ConnectionStage : quint8 { + StageInitHeader = 0, + StageInitBody = 1, + StageConnectedHeader = 2, + StageConnectedBody = 3, + }; + Q_DECLARE_PUBLIC(SingleApplication) + + SingleApplicationPrivate( SingleApplication *q_ptr ); + ~SingleApplicationPrivate() override; + + static QString getUsername(); + void genBlockServerName(); + void initializeMemoryBlock() const; + void startPrimary(); + void startSecondary(); + bool connectToPrimary( int msecs, ConnectionType connectionType ); + quint16 blockChecksum() const; + qint64 primaryPid() const; + QString primaryUser() const; + bool isFrameComplete(QLocalSocket *sock); + void readMessageHeader(QLocalSocket *socket, ConnectionStage nextStage); + void readInitMessageBody(QLocalSocket *socket); + void writeAck(QLocalSocket *sock); + bool writeConfirmedFrame(int msecs, const QByteArray &msg); + bool writeConfirmedMessage(int msecs, const QByteArray &msg); + static void randomSleep(); + void addAppData(const QString &data); + QStringList appData() const; + + SingleApplication *q_ptr; + QSharedMemory *memory; + QLocalSocket *socket; + QLocalServer *server; + quint32 instanceNumber; + QString blockServerName; + SingleApplication::Options options; + QMap connectionMap; + QStringList appDataList; + +public Q_SLOTS: + void slotConnectionEstablished(); + void slotDataAvailable( QLocalSocket*, quint32 ); + void slotClientConnectionClosed( QLocalSocket*, quint32 ); +}; + +#endif // SINGLEAPPLICATION_P_H diff --git a/src/tools/UBGraphicsAxes.cpp b/src/tools/UBGraphicsAxes.cpp index 9fb6be2f..279d0342 100644 --- a/src/tools/UBGraphicsAxes.cpp +++ b/src/tools/UBGraphicsAxes.cpp @@ -428,7 +428,6 @@ void UBGraphicsAxes::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) setCursor(Qt::ArrowCursor); mCloseSvgItem->setVisible(mShowButtons); mNumbersSvgItem->setVisible(mShowButtons); - UBDrawingController::drawingController()->mActiveRuler = NULL; event->accept(); update(); } diff --git a/src/tools/UBGraphicsRuler.cpp b/src/tools/UBGraphicsRuler.cpp index dd4a9e0c..d42ece6a 100644 --- a/src/tools/UBGraphicsRuler.cpp +++ b/src/tools/UBGraphicsRuler.cpp @@ -420,6 +420,9 @@ void UBGraphicsRuler::hoverEnterEvent(QGraphicsSceneHoverEvent *event) { UBStylusTool::Enum currentTool = (UBStylusTool::Enum)UBDrawingController::drawingController ()->stylusTool (); + if (UBDrawingController::drawingController()->mActiveRuler == nullptr) + UBDrawingController::drawingController()->mActiveRuler = this; + if (currentTool == UBStylusTool::Selector || currentTool == UBStylusTool::Play) { @@ -450,7 +453,6 @@ void UBGraphicsRuler::hoverEnterEvent(QGraphicsSceneHoverEvent *event) else if (UBDrawingController::drawingController()->isDrawingTool()) { setCursor(drawRulerLineCursor()); - UBDrawingController::drawingController()->mActiveRuler = this; event->accept(); } } @@ -462,7 +464,7 @@ void UBGraphicsRuler::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) mCloseSvgItem->setVisible(mShowButtons); mResizeSvgItem->setVisible(mShowButtons); mRotateSvgItem->setVisible(mShowButtons); - UBDrawingController::drawingController()->mActiveRuler = NULL; + UBDrawingController::drawingController()->mActiveRuler = nullptr; event->accept(); update(); } diff --git a/src/web/UBWebController.cpp b/src/web/UBWebController.cpp index f47bac80..eb6e65bb 100644 --- a/src/web/UBWebController.cpp +++ b/src/web/UBWebController.cpp @@ -141,10 +141,6 @@ void UBWebController::webBrowserInstance() UBApplication::app()->insertSpaceToToolbarBeforeAction(mMainWindow->webToolBar, mMainWindow->actionBoard, 32); UBApplication::app()->decorateActionMenu(mMainWindow->actionMenu); - bool showAddBookmarkButtons = UBSettings::settings()->webShowAddBookmarkButton->get().toBool(); - mMainWindow->actionBookmarks->setVisible(showAddBookmarkButtons); - mMainWindow->actionAddBookmark->setVisible(showAddBookmarkButtons); - showTabAtTop(UBSettings::settings()->appToolBarPositionedAtTop->get().toBool()); adaptToolBar(); @@ -308,7 +304,7 @@ void UBWebController::setupPalettes() UBApplication::boardController->paletteManager()->mKeyboardPalette, SLOT(onDeactivated())); #endif - connect(mMainWindow->actionWebTrapFlash, SIGNAL(triggered()), this, SLOT(trapFlash())); + connect(mMainWindow->actionCaptureWebContent, SIGNAL(triggered()), this, SLOT(trapFlash())); connect(mMainWindow->actionWebCustomCapture, SIGNAL(triggered()), this, SLOT(customCapture())); connect(mMainWindow->actionWebWindowCapture, SIGNAL(triggered()), this, SLOT(captureWindow())); connect(mMainWindow->actionWebOEmbed, SIGNAL(triggered()), this, SLOT(captureoEmbed())); diff --git a/src/web/browser/WBBrowserWindow.cpp b/src/web/browser/WBBrowserWindow.cpp index 6b7ca6b9..24f780ff 100644 --- a/src/web/browser/WBBrowserWindow.cpp +++ b/src/web/browser/WBBrowserWindow.cpp @@ -248,9 +248,6 @@ void WBBrowserWindow::setupToolBar() mWebToolBar->insertSeparator(mUniboardMainWindow->actionWebBigger); connect(mUniboardMainWindow->actionHome, SIGNAL(triggered()), this , SLOT(slotHome())); - - connect(mUniboardMainWindow->actionBookmarks, SIGNAL(triggered()), this , SLOT(bookmarks())); - connect(mUniboardMainWindow->actionAddBookmark, SIGNAL(triggered()), this , SLOT(addBookmark())); connect(mUniboardMainWindow->actionWebBigger, SIGNAL(triggered()), this , SLOT(slotViewZoomIn())); connect(mUniboardMainWindow->actionWebSmaller, SIGNAL(triggered()), this , SLOT(slotViewZoomOut())); @@ -514,18 +511,6 @@ void WBBrowserWindow::tabCurrentChanged(int index) } -void WBBrowserWindow::bookmarks() -{ - loadPage(UBSettings::settings()->webBookmarksPage->get().toString()); -} - - -void WBBrowserWindow::addBookmark() -{ - loadPage(UBSettings::settings()->webAddBookmarkUrl->get().toString() + currentTabWebView()->url().toString()); -} - - WBWebView* WBBrowserWindow::paintWidget() { return mTabWidget->currentWebView(); diff --git a/src/web/browser/WBBrowserWindow.h b/src/web/browser/WBBrowserWindow.h index 80926e3b..46959099 100644 --- a/src/web/browser/WBBrowserWindow.h +++ b/src/web/browser/WBBrowserWindow.h @@ -121,9 +121,6 @@ class WBBrowserWindow : public QWidget void tabCurrentChanged(int); - void bookmarks(); - void addBookmark(); - void showTabAtTop(bool attop); void aboutToShowBackMenu();