diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 000000000..d620b3719 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,53 @@ +image: docker:stable + +services: + - docker:stable-dind + +workflow: + rules: + - if: $CI_MERGE_REQUEST_ID + - if: $CI_COMMIT_BRANCH + +build-and-test: + script: + - > + if [ -n "$CI_MERGE_REQUEST_ID" ]; then + REPO_ARGS="--build-arg REPO_URL=$CI_MERGE_REQUEST_SOURCE_PROJECT_URL" ; + REPO_ARGS="$REPO_ARGS --build-arg REPO_BRANCH=$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME" ; + else + REPO_ARGS="--build-arg REPO_URL=$CI_REPOSITORY_URL" ; + REPO_ARGS="$REPO_ARGS --build-arg REPO_BRANCH=$CI_COMMIT_BRANCH" ; + fi ; + export REPO_ARGS ; + echo REPO_ARGS=$REPO_ARGS ; + - mkdir Dockercontext + - > + docker build -t retroshare:testing $REPO_ARGS + --file $CI_PROJECT_DIR/build_scripts/GitlabCI/gitlabCI.Dockerfile + Dockercontext + - > + docker run --name retroshare --detach --tty retroshare:testing + retroshare-service --jsonApiPort 9092 + - apk add jq + - > + docker exec retroshare + curl --verbose http://127.0.0.1:9092/rsJsonApi/version | jq + - > + docker exec retroshare + curl --verbose http://127.0.0.1:9092/rsLoginHelper/getLocations | jq + - > + docker exec retroshare + curl --verbose --data + '{ "location":{ "mLocationName":"Test 1", "mPgpName":"Test2" }, + "password":"Test 3", "caller_data":"Test 5" }' + http://127.0.0.1:9092/rsLoginHelper/createLocation | jq + - > + docker exec retroshare + curl --verbose http://127.0.0.1:9092/rsLoginHelper/getLocations | jq + - > + docker exec retroshare + curl --verbose http://127.0.0.1:9092/rsLoginHelper/isLoggedIn | jq + - > + docker exec retroshare + curl --verbose http://127.0.0.1:9092/rsMsgs/getChatLobbyList | jq + - docker container stop retroshare diff --git a/.travis.yml b/.travis.yml index 4fec84f99..cfe2eac21 100644 --- a/.travis.yml +++ b/.travis.yml @@ -57,7 +57,7 @@ before_script: - if [ $TRAVIS_OS_NAME == linux ]; then qmake; fi - > if [ $TRAVIS_OS_NAME == osx ]; then - qmake CONFIG+=rs_macos10.14 + qmake CONFIG+=rs_macos10.14 CONFIG+=c++14 INCLUDEPATH+=$(find /usr/local/Cellar/miniupnpc/*/include | head -n 1) QMAKE_LIBDIR+=$(find /usr/local/Cellar/miniupnpc/*/lib/ | head -n 1) INCLUDEPATH+=$(find /usr/local/Cellar/openssl*/*/include/ | head -n 1) diff --git a/COPYING b/COPYING new file mode 120000 index 000000000..d87028b7c --- /dev/null +++ b/COPYING @@ -0,0 +1 @@ +.reuse/dep5 \ No newline at end of file diff --git a/README.asciidoc b/README.asciidoc index 1243f0c28..734c3d083 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -5,51 +5,52 @@ RetroShare is a decentralized, private, secure, cross-platform, communication toolkit. -RetroShare provides filesharing, chat, messages, forums, channels and more. +RetroShare provides file sharing, chat, messages, forums, channels and more. .Build Status |=============================================================================== -|GNU/Linux, MacOS, (via travis-ci) | image:https://travis-ci.org/RetroShare/RetroShare.svg?branch=master[link="https://travis-ci.org/RetroShare/RetroShare"] -|Windows (via appveyor) | image:https://ci.appveyor.com/api/projects/status/github/RetroShare/RetroShare?svg=true[link="https://ci.appveyor.com/project/RetroShare58622/retroshare"] +|GNU/Linux (via Gitlab CI) | image:https://gitlab.com/RetroShare/RetroShare/badges/master/pipeline.svg[link="https://gitlab.com/RetroShare/RetroShare/-/commits/master",title="pipeline status"] +|GNU/Linux, macOS, (via Travis CI) | image:https://travis-ci.org/RetroShare/RetroShare.svg?branch=master[link="https://travis-ci.org/RetroShare/RetroShare"] +|Windows (via AppVeyor) | image:https://ci.appveyor.com/api/projects/status/github/RetroShare/RetroShare?svg=true[link="https://ci.appveyor.com/project/RetroShare58622/retroshare"] |=============================================================================== == Compilation -It is very difficult to keep a comprehensive (we support many platforms) and -updated documentation on how to build RetroShare, instead we provide scripts and -recipes that are used to create the packages for the supported platforms and -more in the `build_scripts` directory of this repository. +Short of comprehensive and updated documentation on how to build RetroShare +(for the many supported platforms it runs on), scripts and recipes are provided +in the aim of creating packages for the supported platforms (and more) +in the `build_scripts` directory of this repository. Those packaging receipts together with the continuous integration files `.travis.yml` and `appveyor.yml` are a good source of knowledge on how to compile RetroShare on different platforms. -== Using RetroShare on a headless computer with WebUI +== Using RetroShare on a headless computer with web UI -WARNING: This section is outdated need to be adapted to new WebUI +WARNING: This section is outdated need to be adapted to the new web UI -The webUI needs to be enabled as a parameter option in retroshare-service: +The web UI needs to be enabled as a parameter option to the retroshare-service: [source,bash] -------- ./retroshare-nogui --webinterface 9090 --docroot /usr/share/retroshare/webui/ -------- -The webUI is only accessible on localhost:9090. It is advised to keep it that way so that your RS +The web UI is only accessible on localhost:9090. It is advised to keep it that way so that your RS cannot be controlled using an untrusted connection. -To access your web UI from a distance, just open a SSH tunnel on it: +To access your web UI from a distance, just open a SSH tunnel to it: [source,bash] -------- distant_machine:~/ > ssh rs_host -L 9090:localhost:9090 -N -------- -"rs_host" is the machine running retroshare-nogui. Then on the distant machine, access your webUI on +"rs_host" is the machine running `retroshare-nogui`. Then on the distant machine, access your web UI on http://localhost:9090 -That also works with a retroshare GUI of course. +(This can also be done from the RetroShare GUI.) diff --git a/RetroShare.pro b/RetroShare.pro index e835e4ca2..dc590dd17 100644 --- a/RetroShare.pro +++ b/RetroShare.pro @@ -19,7 +19,7 @@ # SPDX-FileCopyrightText: Retroshare Team # SPDX-License-Identifier: LGPL-3.0-or-later -CONFIG += c++11 +CONFIG += c++14 !include("retroshare.pri"): error("Could not include file retroshare.pri") diff --git a/build_scripts/Android/README.asciidoc b/build_scripts/Android/README.asciidoc index d045281b9..3f978cfd1 100644 --- a/build_scripts/Android/README.asciidoc +++ b/build_scripts/Android/README.asciidoc @@ -1,8 +1,8 @@ += RetroShare development on Android + // SPDX-FileCopyrightText: RetroShare Team // SPDX-License-Identifier: CC-BY-SA-4.0 -Compile Retroshare for Android -============================== Compiling an application for Android is not as easy as one would imagine, expecially one like RetroShare that has a big codebase and is not well @@ -179,7 +179,130 @@ Opening RetroShare android app now should show your old profile. == Debugging with GDB -QtCreator actually support debugging only for the foreground activity, so to +If building RetroShare Android package seems tricky, setting up a functional +debugging environement for it feels like black magic. This section is meant to +help you doing it with less headache and hopefully in a reproducible way. + +Unfortunately at the time of the last update to this guide, Qt build system +strips debugging symbols from the package and from the buildroot also if you +compile with debugging enabled. Fiddling with `qmake` configurations and +variables like `CONFIG+=debug`, `CONFIG+=force_debug_info` or `QMAKE_STRIP` +either as commandline arguments or inside retroshare `.pro` and `.pri` files is +uneffective. IMHO Qt should handle this on itself so it is probably worth +reporting a bug to them. So to workaround this problem you need to fiddle a bit +with the Android NDK. In my case I always keep +Debug+ or +Release+ suffix in +my build directory name depending on what kind of build it is, so I use this +information and modify `llvm-strip` in a way that it will strip only if stripped +file path doesn't contain +Debug+. + +.Modify llvm-strip inside Android NDK +-------------------------------------------------------------------------------- +## Set ANDROID_NDK_PATH to your Android NDK installation path +export ANDROID_NDK_PATH="/opt/android-ndk/" + +## Define a convenience variable with llvm-strip path +export ANDROID_NDK_LLVM_STRIP="${ANDROID_NDK_PATH}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip" + +## If not existing yer create a backup of the original llvm-strip +[ -f "${ANDROID_NDK_LLVM_STRIP}.back" ] || + cp "${ANDROID_NDK_LLVM_STRIP}" "${ANDROID_NDK_LLVM_STRIP}.back" + +## Create a new llvm-strip that does nothing if the argument path contains Debug +cat > "${ANDROID_NDK_LLVM_STRIP}" << LLVMSTRIPTRICK +#!/bin/bash + +echo "\${2}" | grep -q Debug || + "${ANDROID_NDK_LLVM_STRIP}.back" --strip-all "\${2}" + +LLVMSTRIPTRICK + +## Eventually you can revert back simply by running +# `mv "${ANDROID_NDK_LLVM_STRIP}.back" "${ANDROID_NDK_LLVM_STRIP}"` +-------------------------------------------------------------------------------- + + +To attach to the `retroshare-service` running on Android you need also to pull a +debugging sysroot out of your device first, RetroShare sources provides an +helper script to do that. + +.Prepare debugging sysroot with helper script +[source,bash] +-------------------------------------------------------------------------------- +## Set RetroShare source path +export RS_SOURCE_DIR="${HOME}/Development/rs-develop" + +## Optionally set your device ID first available will be used, you can run +## `adb devices` to list available devices. +#export ANDROID_SERIAL="YT013PSPGK" + +## Optionally set a path where to save the debugging sysroot otherwise default +## is used. +#export DEBUG_SYSROOT="${HOME}/Builds/debug_sysroot/${ANDROID_SERIAL}/" + +## Run the helper script +${RS_SOURCE_DIR}/build_scripts/Android/pull_sysroot.sh +-------------------------------------------------------------------------------- + +.Prepare Android NDK GDB configurations +[source,bash] +-------------------------------------------------------------------------------- +## Optionally set Qt version variable consistently with your installation +export QT_VERSION="5.12.7" + +## Optionally set Qt architecture variable consistently with Android device +export QT_ARCH="arm64_v8a" + +## Optionally set Qt path variable consistently with your installation +export QT_DIR="/opt/Qt-${QT_VERSION}/${QT_VERSION}/" + +## Optionally set RetroShare buildroot path +export RS_BUILD_DIR="${HOME}/Builds/RetroShare-Android_for_${QT_ARCH}_Clang_Qt_${QT_VERSION//./_}_android_${QT_ARCH}-Debug/" + +## Optionally set gdb config file path +export GDB_CONFIGS_FILE="${HOME}/Builds/gdb_configs_${QT_ARCH}" + +## Generate Android NDK GDB configuration +${RS_SOURCE_DIR}/build_scripts/Android/generate_gdb_init_commands.sh +-------------------------------------------------------------------------------- + + +You will need to run the following steps everytime you want to debug +`retroshare-service` on Android. + +Make sure `retroshare-service` is running on your connected Android device. + +.Run GDB server on your Android device from your host console +[source,bash] +-------------------------------------------------------------------------------- +${RS_SOURCE_DIR}/build_scripts/Android/start_gdbserver.sh +-------------------------------------------------------------------------------- + + +.Run Android NDK GDB on your workstation and attach +[source,bash] +-------------------------------------------------------------------------------- +## Start NDK gdb +${ANDROID_NDK_PATH}/prebuilt/linux-x86_64/bin/gdb + +## Instruct GDB how and where to find debugging symbols +(gdb) source $GDB_CONFIGS_FILE + +## Connect to the gdbserver running on the phone +(gdb) target remote 127.0.01:5039 + +## Have fun debugging +(gdb) +-------------------------------------------------------------------------------- + + +== Debugging with Qt Creator + +WARNING: As of the last update to this guide, debugging retroshare-service +running on Android via Qt creator doesn't wrok even with all the trickery +explained in this section, you better learn how to debug with GDB reading +carefully previous section. + +Qt Creator actually support debugging only for the foreground activity, so to debug what's happening in the core extra trickery is needed. - Run the App in Debug mode from QtCreator "Start Debugging" button @@ -239,6 +362,17 @@ TIP: Some time WiFi power saving on Android mess with the GDB connection, to prevent that from appening open another +adb shell+ and live +ping+ toward your work-station running +== Embedding into other Android packages + +As showed by https://elrepo.io/[elRepo.io] developers it is possible and quite +easy to embed `retroshare-service` into other Android packages see description + +https://gitlab.com/elRepo.io/elRepo.io-android/-/blob/master/README.adoc + +And implementation details + +https://gitlab.com/elRepo.io/elRepo.io-android/-/blob/master/android/app/build.gradle + == Furter Readings @@ -254,3 +388,12 @@ your work-station running - link:https://fw4spl-org.github.io/fw4spl-blog/2015/07/27/Native-debugging-on-Android-with-QtCreator.html[] - link:https://fragglet.livejournal.com/19646.html[] - link:https://github.com/android-ndk/ndk/issues/773[How to build without using standalone toolchain?] + +== License + +Copyright (C) 2016-2020 Gioacchino Mazzurco + +Copyright (C) 2020 Asociación Civil Altermundi + + +This work is licensed under a Creative Commons Attribution-Share Alike 4.0 International License. + +image::https://i.creativecommons.org/l/by-sa/4.0/88x31.png[Creative Commons License, link=http://creativecommons.org/licenses/by-sa/4.0/] diff --git a/build_scripts/Android/generate_gdb_init_commands.sh b/build_scripts/Android/generate_gdb_init_commands.sh new file mode 100755 index 000000000..3a9883950 --- /dev/null +++ b/build_scripts/Android/generate_gdb_init_commands.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +# Script to prepare Android NDK GDB configurations to debug retroshare-service +# +# Copyright (C) 2020 Gioacchino Mazzurco +# Copyright (C) 2020 Asociación Civil Altermundi +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Affero General Public License as published by the +# Free Software Foundation, version 3. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License along +# with this program. If not, see + + +## Define default value for variable, take two arguments, $1 variable name, +## $2 default variable value, if the variable is not already define define it +## with default value. +function define_default_value() +{ + VAR_NAME="${1}" + DEFAULT_VALUE="${2}" + + [ -z "${!VAR_NAME}" ] && export ${VAR_NAME}="${DEFAULT_VALUE}" +} + +define_default_value QT_VERSION "5.12.4" +define_default_value QT_ARCH "arm64_v8a" +define_default_value QT_DIR "/opt/Qt-${QT_VERSION}/${QT_VERSION}/" +define_default_value ANDROID_SERIAL "$(adb devices | head -n 2 | tail -n 1 | awk '{print $1}')" +define_default_value RS_BUILD_DIR "${HOME}/Builds/RetroShare-Android_for_${QT_ARCH}_Clang_Qt_${QT_VERSION//./_}_android_${QT_ARCH}-Debug/" +define_default_value RS_SOURCE_DIR "${HOME}/Development/rs-develop/" +define_default_value DEBUG_SYSROOT "${HOME}/Builds/debug_sysroot/${ANDROID_SERIAL}/" +define_default_value GDB_CONFIGS_FILE "${HOME}/Builds/gdb_configs_${QT_ARCH}" + +scanDir() +{ + find "$1" -type d -not -path '*/\.git/*' | tr '\n' ':' >> $GDB_CONFIGS_FILE +} + +putSeparator() +{ + echo >> $GDB_CONFIGS_FILE + echo >> $GDB_CONFIGS_FILE +} + +echo "set sysroot ${DEBUG_SYSROOT}" > $GDB_CONFIGS_FILE +putSeparator + +echo "set auto-solib-add on" >> $GDB_CONFIGS_FILE +echo -n "set solib-search-path " >> $GDB_CONFIGS_FILE +scanDir "${RS_BUILD_DIR}" +scanDir "${DEBUG_SYSROOT}" +scanDir "${QT_DIR}/android_${QT_ARCH}/lib/" +scanDir "${QT_DIR}/android_${QT_ARCH}/plugins/" +scanDir "${QT_DIR}/android_${QT_ARCH}/qml/" +putSeparator + +echo -n "directory " >> $GDB_CONFIGS_FILE +scanDir ${RS_SOURCE_DIR}/jsonapi-generator/src +scanDir ${RS_SOURCE_DIR}/libbitdht/src +scanDir ${RS_SOURCE_DIR}/openpgpsdk/src +scanDir ${RS_SOURCE_DIR}/libretroshare/src +scanDir ${RS_SOURCE_DIR}/retroshare-service/src +scanDir ${RS_SOURCE_DIR}/supportlibs/rapidjson/include/ +scanDir ${RS_SOURCE_DIR}/supportlibs/restbed/source/ +scanDir ${RS_SOURCE_DIR}/supportlibs/udp-discovery-cpp/ +scanDir ${RS_SOURCE_DIR}/supportlibs/restbed/dependency/asio/asio/include/ +scanDir ${RS_SOURCE_DIR}/supportlibs/restbed/dependency/catch/include/ +putSeparator + +## see https://stackoverflow.com/questions/28972367/gdb-backtrace-without-stopping +echo "catch signal SIGSEGV" >> $GDB_CONFIGS_FILE +echo "commands + bt + continue + end" >> $GDB_CONFIGS_FILE +putSeparator + +echo GDB_CONFIGS_FILE=$GDB_CONFIGS_FILE diff --git a/build_scripts/Android/prepare-toolchain-clang.sh b/build_scripts/Android/prepare-toolchain-clang.sh index 295f4604f..2c64a5186 100755 --- a/build_scripts/Android/prepare-toolchain-clang.sh +++ b/build_scripts/Android/prepare-toolchain-clang.sh @@ -2,7 +2,7 @@ # Script to prepare RetroShare Android package building toolchain # -# Copyright (C) 2016-2019 Gioacchino Mazzurco +# Copyright (C) 2016-2020 Gioacchino Mazzurco # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Affero General Public License as published by the diff --git a/build_scripts/Android/pull_sysroot.sh b/build_scripts/Android/pull_sysroot.sh new file mode 100755 index 000000000..d474610e4 --- /dev/null +++ b/build_scripts/Android/pull_sysroot.sh @@ -0,0 +1,90 @@ +#!/bin/bash + +# Script to pull debugging sysroot from Android devices +# +# Copyright (C) 2020 Gioacchino Mazzurco +# Copyright (C) 2020 Asociación Civil Altermundi +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Affero General Public License as published by the +# Free Software Foundation, version 3. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License along +# with this program. If not, see + +<<"ASCIIDOC" + +Pull files from Android device to prepare a debugging sysroot +Inspired by: +https://fw4spl-org.github.io/fw4spl-blog/2015/07/27/Native-debugging-on-Android-with-QtCreator.html + +The goal is to have a local copy of the sysroot of the connected device. +For that we use the command `adb pull ` + +We will get a copy of: + +- `/system/lib/` +- `/vendor/lib/` +- `libc.so` +- `libcutils.so` + +As well as the binaries `linker` and `app_process` + +IMPORTANT: +from one device to another, the remote file `app_process` can be a binary file +or a symlink to a binary file - which is NOT ok for us. +That's so we will try to pull every known possible variants of `app_process`, +e.g. `app_process32` or `app_process_init` + +ASCIIDOC + + +## Define default value for variable, take two arguments, $1 variable name, +## $2 default variable value, if the variable is not already define define it +## with default value. +function define_default_value() +{ + VAR_NAME="${1}" + DEFAULT_VALUE="${2}" + + [ -z "${!VAR_NAME}" ] && export ${VAR_NAME}="${DEFAULT_VALUE}" +} + +define_default_value ANDROID_SERIAL "$(adb devices | head -n 2 | tail -n 1 | awk '{print $1}')" +define_default_value DEBUG_SYSROOT "${HOME}/Builds/debug_sysroot/${ANDROID_SERIAL}/" + +rm -rf "${DEBUG_SYSROOT}" + +for mDir in "/system/lib/" "/vendor/lib/"; do + mkdir -p "${DEBUG_SYSROOT}/${mDir}" + # adb pull doesn't behave like rsync dirA/ dirB/ so avoid nesting the + # directory by deleting last one before copying + rmdir "${DEBUG_SYSROOT}/${mDir}" + adb pull "${mDir}" "${DEBUG_SYSROOT}/${mDir}" +done + +# Retrieve the specific binaries - some of these adb commands will fail, since +# some files may not exist, but that's ok. + +mkdir -p "${DEBUG_SYSROOT}/system/bin/" +for mBin in "/system/bin/linker" "/system/bin/app_process" \ + "/system/bin/app_process_init" "/system/bin/app_process32" \ + "/system/bin/app_process64" ; do + adb pull "${mBin}" "${DEBUG_SYSROOT}/${mBin}" +done + +# Verify which variants of the specific binaries could be pulled +echo +echo "Found the following specific binaries:" +echo +ls -1 "${DEBUG_SYSROOT}/system/bin/"* +ls -1 "${DEBUG_SYSROOT}/system/lib/libc.so"* +ls -1 "${DEBUG_SYSROOT}/system/lib/libcutils.so"* +echo + +echo DEBUG_SYSROOT="${DEBUG_SYSROOT}" diff --git a/build_scripts/Android/start_gdbserver.sh b/build_scripts/Android/start_gdbserver.sh new file mode 100755 index 000000000..af25e3be1 --- /dev/null +++ b/build_scripts/Android/start_gdbserver.sh @@ -0,0 +1,116 @@ +#!/bin/bash + +# Script to start gdbserver on Android device and attach to retroshare-service +# +# Copyright (C) 2020 Gioacchino Mazzurco +# Copyright (C) 2020 Asociación Civil Altermundi +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Affero General Public License as published by the +# Free Software Foundation, version 3. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License along +# with this program. If not, see + +<<"ASCIIDOC" + +Start gdbserver on Android device and attach it to the retroshare service +process + +Inspired by: +https://fw4spl-org.github.io/fw4spl-blog/2015/07/27/Native-debugging-on-Android-with-QtCreator.html + +ASCIIDOC + + +## Define default value for variable, take two arguments, $1 variable name, +## $2 default variable value, if the variable is not already define define it +## with default value. +function define_default_value() +{ + VAR_NAME="${1}" + DEFAULT_VALUE="${2}" + + [ -z "${!VAR_NAME}" ] && export ${VAR_NAME}="${DEFAULT_VALUE}" +} + +define_default_value ANDROID_APK_PACKAGE "org.retroshare.service" +define_default_value ANDROID_PROCESS_NAME "org.retroshare.service:rs" +define_default_value ANDROID_INSTALL_PATH "" +define_default_value LIB_GDB_SERVER_PATH "" +define_default_value ANDROID_SERIAL "$(adb devices | head -n 2 | tail -n 1 | awk '{print $1}')" +define_default_value GDB_SERVER_PORT 5039 + +adb_ushell() +{ + adb shell run-as ${ANDROID_APK_PACKAGE} $@ +} + +[ -z "${ANDROID_INSTALL_PATH}" ] && +{ + ANDROID_INSTALL_PATH="$(adb_ushell pm path "${ANDROID_APK_PACKAGE}")" + ANDROID_INSTALL_PATH="$(dirname ${ANDROID_INSTALL_PATH#"package:"})" + [ -z "${ANDROID_INSTALL_PATH}" ] && + cat < buildinfo.txt +echo %RsBuildConfig% >> buildinfo.txt +echo %RsArchitecture% >> buildinfo.txt +echo Qt %QtVersion% >> buildinfo.txt +echo %RsCompiler% >> buildinfo.txt + call "%ToolsPath%\msys2-path.bat" "%SourcePath%" MSYS2SourcePath call "%ToolsPath%\msys2-path.bat" "%EnvMSYS2Path%" MSYS2EnvMSYS2Path -%EnvMSYS2Cmd% "qmake "%MSYS2SourcePath%/RetroShare.pro" -r -spec win32-g++ %RS_QMAKE_CONFIG%" +if "%ParamClang%"=="1" ( + %EnvMSYS2Cmd% "qmake "%MSYS2SourcePath%/RetroShare.pro" -r -spec win32-clang-g++ %RS_QMAKE_CONFIG%" +) else ( + %EnvMSYS2Cmd% "qmake "%MSYS2SourcePath%/RetroShare.pro" -r -spec win32-g++ %RS_QMAKE_CONFIG%" +) if errorlevel 1 goto error echo. @@ -63,7 +83,13 @@ echo. title Build - %SourceName%-%RsBuildConfig% [make] -%EnvMSYS2Cmd% "make -j %NUMBER_OF_PROCESSORS%" +%EnvMSYS2Cmd% "make -j %CoreCount%" +if errorlevel 1 goto error + +:: Webui +if "%ParamWebui%"=="1" ( + call "%~dp0..\tools\webui.bat" +) :error popd diff --git a/build_scripts/Windows-msys2/build/env-base.bat b/build_scripts/Windows-msys2/build/env-base.bat index 0140196ec..9d9fa5379 100644 --- a/build_scripts/Windows-msys2/build/env-base.bat +++ b/build_scripts/Windows-msys2/build/env-base.bat @@ -6,6 +6,11 @@ set ParamDebug=0 set ParamAutologin=0 set ParamPlugins=0 set ParamTor=0 +set ParamWebui=0 +set ParamClang=0 +set ParamIndexing=0 +set ParamNoupdate=0 +set CoreCount=%NUMBER_OF_PROCESSORS% set RS_QMAKE_CONFIG= :parameter_loop @@ -25,6 +30,16 @@ if "%~1" NEQ "" ( set ParamPlugins=1 ) else if "%%~a"=="tor" ( set ParamTor=1 + ) else if "%%~a"=="webui" ( + set ParamWebui=1 + ) else if "%%~a"=="singlethread" ( + set CoreCount=1 + ) else if "%%~a"=="clang" ( + set ParamClang=1 + ) else if "%%~a"=="indexing" ( + set ParamIndexing=1 + ) else if "%%~a"=="noupdate" ( + set ParamNoupdate=1 ) else if "%%~a"=="CONFIG+" ( set RS_QMAKE_CONFIG=%RS_QMAKE_CONFIG% %1 ) else ( @@ -55,6 +70,12 @@ if "%Param64%"=="1" ( set RsMSYS2Architecture=x86_64 ) +if "%ParamClang%"=="1" ( + set RsCompiler=Clang +) else ( + set RsCompiler=GCC +) + if "%RsBit%"=="" goto :usage if "%ParamRelease%"=="1" ( @@ -75,12 +96,19 @@ if "%ParamTor%"=="1" ( set RsType= ) +if "%ParamWebui%"=="1" ( + set RS_QMAKE_CONFIG=%RS_QMAKE_CONFIG% "CONFIG+=rs_jsonapi" "CONFIG+=rs_webui" +) + +if "%ParamIndexing%"=="1" ( + set RS_QMAKE_CONFIG=%RS_QMAKE_CONFIG% "CONFIG+=rs_deep_channels_index" "CONFIG+=rs_deep_files_index" "CONFIG+=rs_deep_files_index_ogg" "CONFIG+=rs_deep_files_index_flac" "CONFIG+=rs_deep_files_index_taglib" +) exit /B 0 :usage echo. -echo Usage: 32^|64 release^|debug [version autologin plugins] +echo Usage: 32^|64 release^|debug [autologin plugins webui singlethread clang indexing noupdate] ["CONFIG+=..."] echo. echo Mandatory parameter echo 32^|64 32-bit or 64-bit Version @@ -89,6 +117,12 @@ echo. echo Optional parameter (need clean when changed) echo autologin Build with autologin echo plugins Build plugins +echo webui Enable JsonAPI and pack webui files +echo singlethread Use only 1 thread for building +echo clang Use clang compiler instead of GCC +echo indexing Build with deep channel and file indexing support +echo noupdate Skip updating the libraries +echo "CONFIG+=..." Enable some extra features, you can find the almost complete list in retroshare.pri echo. echo Parameter for pack echo tor Pack tor version diff --git a/build_scripts/Windows-msys2/build/env.bat b/build_scripts/Windows-msys2/build/env.bat index d7dcdf130..bfe2afeba 100644 --- a/build_scripts/Windows-msys2/build/env.bat +++ b/build_scripts/Windows-msys2/build/env.bat @@ -16,10 +16,11 @@ if "%QtVersion%"=="" %cecho% error "Cannot get Qt version." & exit /B 1 set RsMinGWPath=%EnvMSYS2BasePath%\mingw%RsBit% -set RsBuildPath=%BuildPath%\Qt-%QtVersion%-%RsArchitecture%-%RsBuildConfig% -set RsDeployPath=%DeployPath%\Qt-%QtVersion%%RsType%-%RsArchitecture%-%RsBuildConfig% +set RsBuildPath=%BuildPath%\Qt-%QtVersion%-%RsArchitecture%-%RsCompiler%-%RsBuildConfig% +set RsDeployPath=%DeployPath%\Qt-%QtVersion%%RsType%-%RsArchitecture%-%RsCompiler%-%RsBuildConfig% set RsPackPath=%DeployPath% set RsArchiveAdd= +set RsWebuiPath=%RootPath%\%SourceName%-webui if not exist "%~dp0env-mod.bat" goto no_mod call "%~dp0env-mod.bat" diff --git a/build_scripts/Windows-msys2/build/git-log.bat b/build_scripts/Windows-msys2/build/git-log.bat index e3b42bf80..f221bc871 100644 --- a/build_scripts/Windows-msys2/build/git-log.bat +++ b/build_scripts/Windows-msys2/build/git-log.bat @@ -1,118 +1,43 @@ @echo off +setlocal enabledelayedexpansion -setlocal - -set NoAsk= -if "%~2"=="no-ask" set NoAsk=1 - -:: Initialize environment -call "%~dp0..\env.bat" -if errorlevel 1 goto error_env -call "%EnvPath%\env.bat" -if errorlevel 1 goto error_env -call "%EnvPath%\env-msys2.bat" -if errorlevel 1 goto error_env - -call "%~dp0env.bat" %* -if errorlevel 2 exit /B 2 -if errorlevel 1 goto error_env - -:: Check git executable -set GitPath= -call "%ToolsPath%\find-in-path.bat" GitPath git.exe -if "%GitPath%"=="" echo Git executable not found in PATH.& exit /B 1 - -:: Get compiled revision -set GetRsVersion=%SourcePath%\build_scripts\Windows\tools\get-rs-version.bat -if not exist "%GetRsVersion%" ( - echo File not found - echo %GetRsVersion% +if "%~2"=="" ( + echo. + echo Parameter error. + echo Usage git-log sourcepath outputfile exit /B 1 ) -call "%GetRsVersion%" RS_REVISION_STRING RsRevision -if "%RsRevision%"=="" echo Revision not found.& exit /B 1 +set logfile=%~2 +copy nul %logfile% > nul -:: Get compiled version -call "%GetRsVersion%" RS_REVISION_STRING RsRevision -if "%RsRevision%"=="" echo Revision not found.& exit /B 1 +pushd %~1 -call "%GetRsVersion%" RS_MAJOR_VERSION RsMajorVersion -if "%RsMajorVersion%"=="" echo Major version not found.& exit /B 1 - -call "%GetRsVersion%" RS_MINOR_VERSION RsMinorVersion -if "%RsMinorVersion%"=="" echo Minor version not found.& exit /B 1 - -call "%GetRsVersion%" RS_BUILD_NUMBER RsBuildNumber -if "%RsBuildNumber%"=="" echo Build number not found.& exit /B 1 - -call "%GetRsVersion%" RS_BUILD_NUMBER_ADD RsBuildNumberAdd - -set RsVersion=%RsMajorVersion%.%RsMinorVersion%.%RsBuildNumber%%RsBuildNumberAdd% - -:: Check WMIC is available -wmic.exe alias /? >nul 2>&1 || echo WMIC is not available.&& exit /B 1 - -:: Use WMIC to retrieve date in format YYYYMMDD -set RsDate= -for /f "tokens=2 delims==" %%I in ('wmic os get localdatetime /format:list') do set RsDate=%%I -set RsDate=%RsDate:~0,4%%RsDate:~4,2%%RsDate:~6,2% - -:: Get last revision -set RsLastRefFile=%BuildPath%\Qt-%QtVersion%%RsType%-%RsBuildConfig%-LastRef.txt -set RsLastRef= -if exist "%RsLastRefFile%" set /P RsLastRef=<"%RsLastRefFile%" - -if "%NoAsk%"=="1" goto no_ask_for_last_revision -if not "%RsLastRef%"=="" echo Last Revision was %RsLastRef% -set /P RsLastRefInput=Last Revision: -if "%RsLastRefInput%" NEQ "" set RsLastRef=%RsLastRefInput% -:no_ask_for_last_revision - -:: Get current revision -pushd "%SourcePath%" -call "%ToolsPath%\get-git-ref.bat" RsRef -popd - -if errorlevel 1 exit /B 1 -if "%RsRef%"=="" echo Cannot get git revision.& exit /B 1 - -echo. -echo Creating log from %RsLastRef% -echo to %RsRef% - -if "%NoAsk%"=="1" goto no_confirm -choice /M "Do you want to proceed?" -if %errorlevel%==2 exit /B 1 -:no_confirm - -if "%RsBuildConfig%" NEQ "release" ( - set RsGitLog=%DeployPath%\RetroShare-%RsVersion%-Windows-Portable-%RsDate%-%RsRevision%-Qt-%QtVersion%%RsType%-msys2%RsArchiveAdd%-%RsBuildConfig%.txt -) else ( - set RsGitLog=%DeployPath%\RetroShare-%RsVersion%-Windows-Portable-%RsDate%-%RsRevision%-Qt-%QtVersion%%RsType%-msys2%RsArchiveAdd%.txt -) - -title %SourceName%-%RsBuildConfig% [git log] - -pushd "%SourcePath%" -if "%RsLastRef%"=="" ( - git log %RsRef% >"%RsGitLog%" -) else ( - if "%RsLastRef%"=="%RsRef%" ( - git log %RsRef% --max-count=1 >"%RsGitLog%" +set last=HEAD +for /f %%t in ('git tag --sort=-taggerdate --merged ^| findstr v') do ( + echo generating changelog for !last!..%%t + echo ----------------------------------------------- >> %logfile% + if !last! neq HEAD ( + git tag -n !last! >> %logfile% ) else ( - git log %RsLastRef%..%RsRef% >"%RsGitLog%" + echo HEAD >> %logfile% ) + rem echo !last! ---^> %%t >> %logfile% + echo ----------------------------------------------- >> %logfile% + echo. >> %logfile% + git log %%t..!last! --no-merges "--pretty=format:%%h %%ai %%<(10,trunc)%%an %%s" >> %logfile% + echo. >> %logfile% + echo. >> %logfile% + set last=%%t ) + +echo generating changelog for %last% +echo ----------------------------------------------- >> %logfile% +git tag -n %last% >> %logfile% +echo ----------------------------------------------- >> %logfile% +echo. >> %logfile% +git log %last% --no-merges "--pretty=format:%%h %%ai %%<(10,trunc)%%an %%s" >> %logfile% + popd -title %COMSPEC% - -echo %RsRef%>"%RsLastRefFile%" - -exit /B %ERRORLEVEL% - -:error_env -echo Failed to initialize environment. -endlocal -exit /B 1 +endlocal enabledelayedexpansion \ No newline at end of file diff --git a/build_scripts/Windows-msys2/build/pack.bat b/build_scripts/Windows-msys2/build/pack.bat index e8c9761bc..4c12ed7c0 100644 --- a/build_scripts/Windows-msys2/build/pack.bat +++ b/build_scripts/Windows-msys2/build/pack.bat @@ -17,6 +17,16 @@ call "%~dp0env.bat" %* if errorlevel 2 exit /B 2 if errorlevel 1 goto error_env +if not "%ParamNoupdate%"=="1" ( + :: Install ntldd + %EnvMSYS2Cmd% "pacman --noconfirm --needed -S mingw-w64-%RsMSYS2Architecture%-ntldd-git" + + :: Install tor + if "%ParamTor%"=="1" ( + %EnvMSYS2Cmd% "pacman --noconfirm --needed -S mingw-w64-%RsMSYS2Architecture%-tor" + ) +) + :: Remove deploy path if exist "%RsDeployPath%" rmdir /S /Q "%RsDeployPath%" @@ -24,7 +34,7 @@ if exist "%RsDeployPath%" rmdir /S /Q "%RsDeployPath%" if not exist "%RsBuildPath%\Makefile" echo Project is not compiled.& goto error :: Get compiled revision -set GetRsVersion=%SourcePath%\build_scripts\Windows\tools\get-rs-version.bat +set GetRsVersion=%SourcePath%\build_scripts\Windows-msys2\tools\get-rs-version.bat if not exist "%GetRsVersion%" ( %cecho% error "File not found" echo %GetRsVersion% @@ -50,14 +60,6 @@ set RsDate= for /f "tokens=2 delims==" %%I in ('wmic os get localdatetime /format:list') do set RsDate=%%I set RsDate=%RsDate:~0,4%%RsDate:~4,2%%RsDate:~6,2% -if "%ParamTor%"=="1" ( - :: Check for tor executable - if not exist "%EnvDownloadPath%\tor\Tor\tor.exe" ( - %cecho% error "Tor binary not found. Please download Tor Expert Bundle from\nhttps://www.torproject.org/download/download.html.en\nand unpack to\n%EnvDownloadPath:\=\\%\\tor" - goto error - ) -) - set QtMainVersion=%QtVersion:~0,1% set QtSharePath=%RsMinGWPath%\share\qt%QtMainVersion%\ @@ -69,9 +71,9 @@ if "%QtMainVersion%"=="4" set QtMainVersion2=4 if "%QtMainVersion%"=="5" set QtMainVersion1=5 if "%RsBuildConfig%" NEQ "release" ( - set Archive=%RsPackPath%\RetroShare-%RsVersion%-Windows-Portable-%RsDate%-%RsVersion.Extra%-Qt-%QtVersion%-msys2%RsType%%RsArchiveAdd%-%RsBuildConfig%.7z + set Archive=%RsPackPath%\RetroShare-%RsVersion%-Windows-Portable-%RsDate%-%RsVersion.Extra%-%RsArchitecture%-msys2%RsType%%RsArchiveAdd%-%RsBuildConfig%.7z ) else ( - set Archive=%RsPackPath%\RetroShare-%RsVersion%-Windows-Portable-%RsDate%-%RsVersion.Extra%-Qt-%QtVersion%-msys2%RsType%%RsArchiveAdd%.7z + set Archive=%RsPackPath%\RetroShare-%RsVersion%-Windows-Portable-%RsDate%-%RsVersion.Extra%-%RsArchitecture%-msys2%RsType%%RsArchiveAdd%.7z ) if exist "%Archive%" del /Q "%Archive%" @@ -93,6 +95,7 @@ mkdir "%RsDeployPath%\qss" mkdir "%RsDeployPath%\stylesheets" mkdir "%RsDeployPath%\sounds" mkdir "%RsDeployPath%\translations" +mkdir "%RsDeployPath%\license" copy nul "%RsDeployPath%\portable" %Quite% @@ -100,16 +103,13 @@ echo copy binaries copy "%RsBuildPath%\retroshare-gui\src\%RsBuildConfig%\RetroShare*.exe" "%RsDeployPath%" %Quite% copy "%RsBuildPath%\retroshare-nogui\src\%RsBuildConfig%\retroshare*-nogui.exe" "%RsDeployPath%" %Quite% copy "%RsBuildPath%\retroshare-service\src\%RsBuildConfig%\retroshare*-service.exe" "%RsDeployPath%" %Quite% +copy "%RsBuildPath%\supportlibs\cmark\build\src\libcmark.dll" "%RsDeployPath%" %Quite% echo copy extensions for /D %%D in ("%RsBuildPath%\plugins\*") do ( call :copy_extension "%%D" "%RsDeployPath%\Data\%Extensions%" - call :copy_dependencies "%RsDeployPath%\Data\%Extensions%\%%~nxD.dll" "%RsDeployPath%" ) -echo copy dependencies -call :copy_dependencies "%RsDeployPath%\retroshare.exe" "%RsDeployPath%" - echo copy Qt DLL's copy "%RsMinGWPath%\bin\Qt%QtMainVersion1%Svg%QtMainVersion2%.dll" "%RsDeployPath%" %Quite% @@ -121,14 +121,22 @@ if "%QtMainVersion%"=="5" ( ) if exist "%QtSharePath%\plugins\styles\qwindowsvistastyle.dll" ( - echo Copy styles + echo copy styles mkdir "%RsDeployPath%\styles" %Quite% copy "%QtSharePath%\plugins\styles\qwindowsvistastyle.dll" "%RsDeployPath%\styles" %Quite% ) copy "%QtSharePath%\plugins\imageformats\*.dll" "%RsDeployPath%\imageformats" %Quite% del /Q "%RsDeployPath%\imageformats\*d?.dll" %Quite% -for %%D in ("%RsDeployPath%\imageformats\*.dll") do ( + +if "%ParamTor%"=="1" ( + echo copy tor + copy "%RsMinGWPath%\bin\tor.exe" "%RsDeployPath%" %Quite% + copy "%RsMinGWPath%\bin\tor-gencert.exe" "%RsDeployPath%" %Quite% +) + +echo copy dependencies +for /R "%RsDeployPath%" %%D in (*.dll, *.exe) do ( call :copy_dependencies "%%D" "%RsDeployPath%" ) @@ -144,6 +152,9 @@ rmdir /S /Q "%RsDeployPath%\stylesheets\__MACOSX__Bubble" %Quite% echo copy sounds xcopy /S "%SourcePath%\retroshare-gui\src\sounds" "%RsDeployPath%\sounds" %Quite% +echo copy license +xcopy /S "%SourcePath%\retroshare-gui\src\license" "%RsDeployPath%\license" %Quite% + echo copy translation copy "%SourcePath%\retroshare-gui\src\translations\qt_tr.qm" "%RsDeployPath%\translations" %Quite% copy "%QtSharePath%\translations\qt_*.qm" "%RsDeployPath%\translations" %Quite% @@ -158,18 +169,21 @@ if "%QtMainVersion%"=="5" ( echo copy bdboot.txt copy "%SourcePath%\libbitdht\src\bitdht\bdboot.txt" "%RsDeployPath%" %Quite% -echo copy changelog.txt -copy "%SourcePath%\retroshare-gui\src\changelog.txt" "%RsDeployPath%" %Quite% +echo generate changelog.txt +call call "%~dp0\git-log.bat" "%SourcePath%" "%RsDeployPath%\changelog.txt" -if exist "%SourcePath%\libresapi\src\webui" ( - echo copy webui - mkdir "%RsDeployPath%\webui" - xcopy /S "%SourcePath%\libresapi\src\webui" "%RsDeployPath%\webui" %Quite% -) +echo copy buildinfo.txt +copy "%RsBuildPath%\buildinfo.txt" "%RsDeployPath%" %Quite% -if "%ParamTor%"=="1" ( - echo copy tor - echo n | copy /-y "%EnvDownloadPath%\tor\Tor\*.*" "%RsDeployPath%" %Quite% +if "%ParamWebui%"=="1" ( + if exist "%RsWebuiPath%\webui" ( + echo copy webui + mkdir "%RsDeployPath%\webui" + xcopy /S "%RsWebuiPath%\webui" "%RsDeployPath%\webui" %Quite% + ) else ( + %cecho% error "Webui is enabled, but no webui data found at %RsWebuiPath%\webui" + goto error + ) ) rem pack files @@ -204,14 +218,11 @@ if exist "%~1\%RsBuildConfig%\%~n1.dll" ( goto :EOF :copy_dependencies -set CopyDependenciesCopiedSomething=0 -for /F "usebackq" %%A in (`%ToolsPath%\depends.bat list %1`) do ( +for /F "usebackq" %%A in (`%ToolsPath%\depends.bat %1`) do ( if not exist "%~2\%%A" ( if exist "%RsMinGWPath%\bin\%%A" ( - set CopyDependenciesCopiedSomething=1 copy "%RsMinGWPath%\bin\%%A" %2 %Quite% ) ) ) -if "%CopyDependenciesCopiedSomething%"=="1" goto copy_dependencies goto :EOF diff --git a/build_scripts/Windows-msys2/env/env.bat b/build_scripts/Windows-msys2/env/env.bat index 810715ad2..1d2f9c2e4 100644 --- a/build_scripts/Windows-msys2/env/env.bat +++ b/build_scripts/Windows-msys2/env/env.bat @@ -7,9 +7,7 @@ set EnvToolsPath=%EnvRootPath%\tools set EnvTempPath=%EnvRootPath%\tmp set EnvDownloadPath=%EnvRootPath%\download -set EnvWgetExe=%EnvToolsPath%\wget.exe set EnvSevenZipExe=%EnvToolsPath%\7z.exe -set EnvDependsExe=%EnvToolsPath%\depends.exe set EnvCEchoExe=%EnvToolsPath%\cecho.exe set cecho=call "%ToolsPath%\cecho.bat" diff --git a/build_scripts/Windows-msys2/env/tools/prepare-msys2.bat b/build_scripts/Windows-msys2/env/tools/prepare-msys2.bat index ddf8b00a6..2f85c42be 100644 --- a/build_scripts/Windows-msys2/env/tools/prepare-msys2.bat +++ b/build_scripts/Windows-msys2/env/tools/prepare-msys2.bat @@ -25,7 +25,7 @@ if exist "%EnvMSYS2Path%\msys%MSYS2Base%\usr\bin\pacman.exe" ( ) ) -set MSYS2Install=msys2-base-%MSYS2Architecture%-20180531.tar.xz +set MSYS2Install=msys2-base-%MSYS2Architecture%-20200720.tar.xz set MSYS2Url=http://sourceforge.net/projects/msys2/files/Base/%MSYS2Architecture%/%MSYS2Install%/download %cecho% info "Remove previous MSYS2 version" diff --git a/build_scripts/Windows-msys2/env/tools/prepare-tools.bat b/build_scripts/Windows-msys2/env/tools/prepare-tools.bat index d2014a7f1..c6b14a787 100644 --- a/build_scripts/Windows-msys2/env/tools/prepare-tools.bat +++ b/build_scripts/Windows-msys2/env/tools/prepare-tools.bat @@ -6,22 +6,6 @@ set CEchoUrl=https://github.com/lordmulder/cecho/releases/download/2015-10-10/ce set CEchoInstall=cecho.2015-10-10.zip set SevenZipUrl=https://sourceforge.net/projects/sevenzip/files/7-Zip/18.05/7z1805.msi/download set SevenZipInstall=7z1805.msi -set WgetUrl=https://eternallybored.org/misc/wget/1.19.4/32/wget.exe -set WgetInstall=wget.exe -set DependsUrl=http://www.dependencywalker.com/depends22_x86.zip -set DependsInstall=depends22_x86.zip -set SigcheckInstall=Sigcheck.zip -set SigcheckUrl=https://download.sysinternals.com/files/%SigcheckInstall% - -if not exist "%EnvToolsPath%\wget.exe" ( - echo Download Wget installation - - if not exist "%EnvDownloadPath%\%WgetInstall%" call "%ToolsPath%\winhttpjs.bat" %WgetUrl% -saveTo "%EnvDownloadPath%\%WgetInstall%" - if not exist "%EnvDownloadPath%\%WgetInstall%" %cecho% error "Cannot download Wget installation" & goto error - - echo Copy Wget - copy "%EnvDownloadPath%\wget.exe" "%EnvToolsPath%" -) if not exist "%EnvToolsPath%\7z.exe" ( call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%" @@ -56,32 +40,6 @@ if not exist "%EnvToolsPath%\cecho.exe" ( call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%" ) -if not exist "%EnvToolsPath%\depends.exe" ( - call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%" - mkdir "%EnvTempPath%" - - %cecho% info "Download Dependency Walker installation" - - if not exist "%EnvDownloadPath%\%DependsInstall%" call "%ToolsPath%\winhttpjs.bat" %DependsUrl% -saveTo "%EnvDownloadPath%\%DependsInstall%" - if not exist "%EnvDownloadPath%\%DependsInstall%" %cecho% error "Cannot download Dependendy Walker installation" & goto error - - %cecho% info "Unpack Dependency Walker" - "%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%DependsInstall%" - copy "%EnvTempPath%\*" "%EnvToolsPath%" - - call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%" -) - -if not exist "%EnvToolsPath%\sigcheck.exe" ( - %cecho% info "Download Sigcheck installation" - - if not exist "%EnvDownloadPath%\%SigcheckInstall%" call "%ToolsPath%\download-file.bat" "%SigcheckUrl%" "%EnvDownloadPath%\%SigcheckInstall%" - if not exist "%EnvDownloadPath%\%SigcheckInstall%" %cecho% error "Cannot download Sigcheck installation" & goto error - - %cecho% info "Unpack Sigcheck" - "%EnvSevenZipExe%" x -o"%EnvToolsPath%" "%EnvDownloadPath%\%SigcheckInstall%" sigcheck.exe -) - :exit endlocal exit /B 0 diff --git a/build_scripts/Windows-msys2/installer/HeaderImage.bmp b/build_scripts/Windows-msys2/installer/HeaderImage.bmp new file mode 100644 index 000000000..5c55265cd Binary files /dev/null and b/build_scripts/Windows-msys2/installer/HeaderImage.bmp differ diff --git a/build_scripts/Windows-msys2/installer/HeaderImageEmpty.bmp b/build_scripts/Windows-msys2/installer/HeaderImageEmpty.bmp new file mode 100644 index 000000000..f0f0f1b32 Binary files /dev/null and b/build_scripts/Windows-msys2/installer/HeaderImageEmpty.bmp differ diff --git a/build_scripts/Windows-msys2/installer/ifexist.nsh b/build_scripts/Windows-msys2/installer/ifexist.nsh new file mode 100644 index 000000000..3f391c898 --- /dev/null +++ b/build_scripts/Windows-msys2/installer/ifexist.nsh @@ -0,0 +1,15 @@ +; See http://nsis.sourceforge.net/Check_if_a_file_exists_at_compile_time for documentation +!macro !defineifexist _VAR_NAME _FILE_NAME + !tempfile _TEMPFILE + !ifdef NSIS_WIN32_MAKENSIS + ; Windows - cmd.exe + !system 'if exist "${_FILE_NAME}" echo !define ${_VAR_NAME} > "${_TEMPFILE}"' + !else + ; Posix - sh + !system 'if [ -e "${_FILE_NAME}" ]; then echo "!define ${_VAR_NAME}" > "${_TEMPFILE}"; fi' + !endif + !include '${_TEMPFILE}' + !delfile '${_TEMPFILE}' + !undef _TEMPFILE +!macroend +!define !defineifexist "!insertmacro !defineifexist" \ No newline at end of file diff --git a/build_scripts/Windows-msys2/installer/lang/ca_ES.nsh b/build_scripts/Windows-msys2/installer/lang/ca_ES.nsh new file mode 100644 index 000000000..acb11b5a4 --- /dev/null +++ b/build_scripts/Windows-msys2/installer/lang/ca_ES.nsh @@ -0,0 +1,29 @@ +!insertmacro LANG_STRING Section_Main "${APPNAME}" +!insertmacro LANG_STRING Section_Main_Desc "Instal·la ${APPNAME} i els components necessaris." +!insertmacro LANG_STRING Section_Data "Pells" +!insertmacro LANG_STRING Section_Data_Desc "Instal·la pells." +!insertmacro LANG_STRING Section_Shortcuts "Icones d'accés directe" +!insertmacro LANG_STRING Section_Shortcuts_Desc "Afegir icones d'accés directe." +!insertmacro LANG_STRING Section_StartMenu "Icona del menú d'inici" +!insertmacro LANG_STRING Section_StartMenu_Desc "Afegir icona en el menú d'inici" +!insertmacro LANG_STRING Section_Desktop "Icona d'escriptori" +!insertmacro LANG_STRING Section_Desktop_Desc "Afegir icona a l'escriptori" +!insertmacro LANG_STRING Section_QuickLaunch "Icona de la barra ràpida d'accés" +!insertmacro LANG_STRING Section_QuickLaunch_Desc "Afegir icona a la barra ràpida d'accés" +!insertmacro LANG_STRING Section_Plugins "Complements opcionals" +!insertmacro LANG_STRING Section_Plugins_Desc "Components opcionals per afegir funcionalitat." +!insertmacro LANG_STRING Section_Plugin_FeedReader "LectorFonts" +!insertmacro LANG_STRING Section_Plugin_FeedReader_Desc "Instal·lar complement LectorFonts." +!insertmacro LANG_STRING Section_Plugin_LinksCloud "NúvolEnllaços" +!insertmacro LANG_STRING Section_Plugin_LinksCloud_Desc "Instal·lar complement NúvolEnllaços." +!insertmacro LANG_STRING Section_Plugin_VOIP "VeuIP" +!insertmacro LANG_STRING Section_Plugin_VOIP_Desc "Instal·lar complement VeuIP." +!insertmacro LANG_STRING Section_AutoStart "Posada en marxa automàtica" +!insertmacro LANG_STRING Section_AutoStart_Desc "Autoengegar al arrencar." +!insertmacro LANG_STRING Page_InstallMode "Mode instal·lació" +!insertmacro LANG_STRING Page_InstallMode_Desc "Escull com vols instal·lar ${APPNAME}." +!insertmacro LANG_STRING Page_InstallMode_Standard "Instal·lació e&stàndard" +!insertmacro LANG_STRING Page_InstallMode_Standard_Desc "Instal·la ${APPNAME} per la sessió d'usuari actual." +!insertmacro LANG_STRING Page_InstallMode_Portable "Instal·lació &portable." +!insertmacro LANG_STRING Page_InstallMode_Portable_Desc "En mode portable les dades de configuració s'emmagatzemen a la carpeta d'aplicació i no s'escriu informació al registre del sistema." +!insertmacro LANG_STRING Link_Uninstall "Desinstal·lar." diff --git a/build_scripts/Windows-msys2/installer/lang/de.nsh b/build_scripts/Windows-msys2/installer/lang/de.nsh new file mode 100644 index 000000000..c5323f523 --- /dev/null +++ b/build_scripts/Windows-msys2/installer/lang/de.nsh @@ -0,0 +1,29 @@ +!insertmacro LANG_STRING Section_Main "${APPNAME}" +!insertmacro LANG_STRING Section_Main_Desc "Installiert ${APPNAME} und die benötigten Komponenten." +!insertmacro LANG_STRING Section_Data "Skins" +!insertmacro LANG_STRING Section_Data_Desc "Skins installieren." +!insertmacro LANG_STRING Section_Shortcuts "Verknüpfungssymbole" +!insertmacro LANG_STRING Section_Shortcuts_Desc "Verküpfungssymbole hinzufügen." +!insertmacro LANG_STRING Section_StartMenu "Startmenüsymbol" +!insertmacro LANG_STRING Section_StartMenu_Desc "Fügt Symbol zum Startmenü hinzu." +!insertmacro LANG_STRING Section_Desktop "Desktopsymbol" +!insertmacro LANG_STRING Section_Desktop_Desc "Fügt Symbol zum Desktop hinzu." +!insertmacro LANG_STRING Section_QuickLaunch "Schnellstartsymbol" +!insertmacro LANG_STRING Section_QuickLaunch_Desc "Fügt Symbol zur Schnellstartleiste hinzu." +!insertmacro LANG_STRING Section_Plugins "Optionale Plug-ins" +!insertmacro LANG_STRING Section_Plugins_Desc "Optionale Plug-ins zum Erweitern der Funktionalität." +!insertmacro LANG_STRING Section_Plugin_FeedReader "FeedReader" +!insertmacro LANG_STRING Section_Plugin_FeedReader_Desc "Installiert das Plug-in Feedreader." +!insertmacro LANG_STRING Section_Plugin_LinksCloud "Verknüpfungswolke" +!insertmacro LANG_STRING Section_Plugin_LinksCloud_Desc "Installiert das Plug-in Verknüpfungswolke." +!insertmacro LANG_STRING Section_Plugin_VOIP "VOIP" +!insertmacro LANG_STRING Section_Plugin_VOIP_Desc "Installiert das Plug-in VOIP" +!insertmacro LANG_STRING Section_AutoStart "Automatischer Programmstart" +!insertmacro LANG_STRING Section_AutoStart_Desc "Beim Start automatisch ausführen." +!insertmacro LANG_STRING Page_InstallMode "Installationsmodus" +!insertmacro LANG_STRING Page_InstallMode_Desc "Wähle wie du ${APPNAME} installieren willst." +!insertmacro LANG_STRING Page_InstallMode_Standard "&Standardinstallation" +!insertmacro LANG_STRING Page_InstallMode_Standard_Desc "Installiere ${APPNAME} für den derzeitigen Benutzer dieses Geräts." +!insertmacro LANG_STRING Page_InstallMode_Portable "&Portable Installation" +!insertmacro LANG_STRING Page_InstallMode_Portable_Desc "Im portablen Modus werden alle Konfigurationsdaten im Verzeichnis der Anwendung gespeichert und keine Informationen in der Registry abgelegt." +!insertmacro LANG_STRING Link_Uninstall "Deinstallieren" diff --git a/build_scripts/Windows-msys2/installer/lang/en.nsh b/build_scripts/Windows-msys2/installer/lang/en.nsh new file mode 100644 index 000000000..a1bbc783c --- /dev/null +++ b/build_scripts/Windows-msys2/installer/lang/en.nsh @@ -0,0 +1,29 @@ +!insertmacro LANG_STRING Section_Main "${APPNAME}" +!insertmacro LANG_STRING Section_Main_Desc "Installs ${APPNAME} and required components." +!insertmacro LANG_STRING Section_Data "Skins" +!insertmacro LANG_STRING Section_Data_Desc "Installs skins." +!insertmacro LANG_STRING Section_Shortcuts "Shortcut icons" +!insertmacro LANG_STRING Section_Shortcuts_Desc "Adds shortcut icons." +!insertmacro LANG_STRING Section_StartMenu "Start Menu icon" +!insertmacro LANG_STRING Section_StartMenu_Desc "Adds icon to start menu." +!insertmacro LANG_STRING Section_Desktop "Desktop icon" +!insertmacro LANG_STRING Section_Desktop_Desc "Adds icon to desktop." +!insertmacro LANG_STRING Section_QuickLaunch "Quick Launch icon" +!insertmacro LANG_STRING Section_QuickLaunch_Desc "Adds icon to Quick Launch toolbar." +!insertmacro LANG_STRING Section_Plugins "Optional plugins" +!insertmacro LANG_STRING Section_Plugins_Desc "Optional plugins to extend functionality." +!insertmacro LANG_STRING Section_Plugin_FeedReader "FeedReader" +!insertmacro LANG_STRING Section_Plugin_FeedReader_Desc "Installs plugin FeedReader." +!insertmacro LANG_STRING Section_Plugin_VOIP "VOIP" +!insertmacro LANG_STRING Section_Plugin_VOIP_Desc "Installs plugin VOIP." +;!insertmacro LANG_STRING Section_Link "File Association" +;!insertmacro LANG_STRING Section_Link_Desc "Associate ${APPNAME} with .pqi file extension." +!insertmacro LANG_STRING Section_AutoStart "Auto Startup" +!insertmacro LANG_STRING Section_AutoStart_Desc "Auto-Run at startup." +!insertmacro LANG_STRING Page_InstallMode "Installation Mode" +!insertmacro LANG_STRING Page_InstallMode_Desc "Choose how you want to install ${APPNAME}." +!insertmacro LANG_STRING Page_InstallMode_Standard "&Standard installation" +!insertmacro LANG_STRING Page_InstallMode_Standard_Desc "Install ${APPNAME} for the current user of this machine." +!insertmacro LANG_STRING Page_InstallMode_Portable "&Portable installation" +!insertmacro LANG_STRING Page_InstallMode_Portable_Desc "In portable mode all configuration data is stored in the application folder and no information is written to the registry." +!insertmacro LANG_STRING Link_Uninstall "Uninstall" \ No newline at end of file diff --git a/build_scripts/Windows-msys2/installer/lang/es.nsh b/build_scripts/Windows-msys2/installer/lang/es.nsh new file mode 100644 index 000000000..68900ec62 --- /dev/null +++ b/build_scripts/Windows-msys2/installer/lang/es.nsh @@ -0,0 +1,29 @@ +!insertmacro LANG_STRING Section_Main "${APPNAME}" +!insertmacro LANG_STRING Section_Main_Desc "Instala ${APPNAME} y los componentes requeridos." +!insertmacro LANG_STRING Section_Data "Coberturas (skins)" +!insertmacro LANG_STRING Section_Data_Desc "Instalar coberturas" +!insertmacro LANG_STRING Section_Shortcuts "Iconos de accesos directos" +!insertmacro LANG_STRING Section_Shortcuts_Desc "Añade iconos de accesos directos." +!insertmacro LANG_STRING Section_StartMenu "Icono de menú de inicio" +!insertmacro LANG_STRING Section_StartMenu_Desc "Añade icono al menú de inicio." +!insertmacro LANG_STRING Section_Desktop "Icono del escritorio" +!insertmacro LANG_STRING Section_Desktop_Desc "Añade icono al escritorio" +!insertmacro LANG_STRING Section_QuickLaunch "Icono de inicio rápido" +!insertmacro LANG_STRING Section_QuickLaunch_Desc "Añade icono a la Barra de Inicio Rápido" +!insertmacro LANG_STRING Section_Plugins "Complementos opcionales" +!insertmacro LANG_STRING Section_Plugins_Desc "Complementos opcionales para expandir la funcionalidad." +!insertmacro LANG_STRING Section_Plugin_FeedReader "FeedReader" +!insertmacro LANG_STRING Section_Plugin_FeedReader_Desc "Instala el complemento FeedReader." +!insertmacro LANG_STRING Section_Plugin_LinksCloud "LinksCloud" +!insertmacro LANG_STRING Section_Plugin_LinksCloud_Desc "Instala el complemento LinksCloud." +!insertmacro LANG_STRING Section_Plugin_VOIP "VOIP" +!insertmacro LANG_STRING Section_Plugin_VOIP_Desc "Instala el complemento VOIP" +!insertmacro LANG_STRING Section_AutoStart "Auto iniciar" +!insertmacro LANG_STRING Section_AutoStart_Desc "Auto-ejecutar al incio." +!insertmacro LANG_STRING Page_InstallMode "Modo de instalación" +!insertmacro LANG_STRING Page_InstallMode_Desc "Elija cómo quiere instalar ${APPNAME}." +!insertmacro LANG_STRING Page_InstallMode_Standard "Instalación &Estándar" +!insertmacro LANG_STRING Page_InstallMode_Standard_Desc "Instalar ${APPNAME} para el usuario actual de esta máquina." +!insertmacro LANG_STRING Page_InstallMode_Portable "Instalación &Portable" +!insertmacro LANG_STRING Page_InstallMode_Portable_Desc "En modo portable, todos los datos de configuración se almacenan en la carpeta de la aplicación y no se escribe ninguna información en el registro." +!insertmacro LANG_STRING Link_Uninstall "Desinstalar" diff --git a/build_scripts/Windows-msys2/installer/lang/fr.nsh b/build_scripts/Windows-msys2/installer/lang/fr.nsh new file mode 100644 index 000000000..d4c797833 --- /dev/null +++ b/build_scripts/Windows-msys2/installer/lang/fr.nsh @@ -0,0 +1,29 @@ +!insertmacro LANG_STRING Section_Main "${APPNAME}" +!insertmacro LANG_STRING Section_Main_Desc "Installe ${APPNAME} et les composants requis." +!insertmacro LANG_STRING Section_Data "Habillages" +!insertmacro LANG_STRING Section_Data_Desc "Installe des habillages." +!insertmacro LANG_STRING Section_Shortcuts "Icônes de raccourci" +!insertmacro LANG_STRING Section_Shortcuts_Desc "Ajoute les icônes de raccourci." +!insertmacro LANG_STRING Section_StartMenu "Icône de menu démarrage" +!insertmacro LANG_STRING Section_StartMenu_Desc "Ajoute l'icône au menu démarrer." +!insertmacro LANG_STRING Section_Desktop "Icônes de bureau" +!insertmacro LANG_STRING Section_Desktop_Desc "Ajoute l'icône sur le bureau." +!insertmacro LANG_STRING Section_QuickLaunch "Icône de lancement rapide" +!insertmacro LANG_STRING Section_QuickLaunch_Desc "Ajoute une icône à la barre d'outils de lancement rapide." +!insertmacro LANG_STRING Section_Plugins "Plug-ins optionnels" +!insertmacro LANG_STRING Section_Plugins_Desc "Plug-ins optionnels destinés à étendre les fonctionnalités." +!insertmacro LANG_STRING Section_Plugin_FeedReader "FeedReader" +!insertmacro LANG_STRING Section_Plugin_FeedReader_Desc "Installe le plug-in FeedReader." +!insertmacro LANG_STRING Section_Plugin_LinksCloud "LinksCloud" +!insertmacro LANG_STRING Section_Plugin_LinksCloud_Desc "Installe le plug-in LinksCloud." +!insertmacro LANG_STRING Section_Plugin_VOIP "VOIP" +!insertmacro LANG_STRING Section_Plugin_VOIP_Desc "Installe le plug-in VOIP." +!insertmacro LANG_STRING Section_AutoStart "Démarrage automatique" +!insertmacro LANG_STRING Section_AutoStart_Desc "Démarrage automatique au démarrage." +!insertmacro LANG_STRING Page_InstallMode "Mode d'installation" +!insertmacro LANG_STRING Page_InstallMode_Desc "Choisissez comment vous voulez installer ${APPNAME}." +!insertmacro LANG_STRING Page_InstallMode_Standard "&Installation standard" +!insertmacro LANG_STRING Page_InstallMode_Standard_Desc "Installer ${APPNAME} pour l'utilisateur actuel de cette machine." +!insertmacro LANG_STRING Page_InstallMode_Portable "&Installation portable" +!insertmacro LANG_STRING Page_InstallMode_Portable_Desc "En mode portable toutes les données de configuration sont stockées dans le dossier de l'application et aucune information n'est écrite dans la base de registre." +!insertmacro LANG_STRING Link_Uninstall "Désinstaller" diff --git a/build_scripts/Windows-msys2/installer/lang/pl.nsh b/build_scripts/Windows-msys2/installer/lang/pl.nsh new file mode 100644 index 000000000..9f6d4305e --- /dev/null +++ b/build_scripts/Windows-msys2/installer/lang/pl.nsh @@ -0,0 +1,29 @@ +!insertmacro LANG_STRING Section_Main "${APPNAME}" +!insertmacro LANG_STRING Section_Main_Desc "Instaluje ${APPNAME} oraz wymagane komponenty." +!insertmacro LANG_STRING Section_Data "Skórki" +!insertmacro LANG_STRING Section_Data_Desc "Instaluje skórki." +!insertmacro LANG_STRING Section_Shortcuts "Ikony skrótów" +!insertmacro LANG_STRING Section_Shortcuts_Desc "Dodaje skróty ikon." +!insertmacro LANG_STRING Section_StartMenu "Start Menu icon" +!insertmacro LANG_STRING Section_StartMenu_Desc "Dodaje ikonę do menu start." +!insertmacro LANG_STRING Section_Desktop "Ikona na pulpicie" +!insertmacro LANG_STRING Section_Desktop_Desc "Dodawanie ikony na pulpicie" +!insertmacro LANG_STRING Section_QuickLaunch "Ikona szybkiego uruchamiania" +!insertmacro LANG_STRING Section_QuickLaunch_Desc "Dodaje ikonę do paska Szybkiego Uruchamiania." +!insertmacro LANG_STRING Section_Plugins "Wtyczki opcjonalne" +!insertmacro LANG_STRING Section_Plugins_Desc "Wtyczki opcjonalne rozszerzające funkcjonalność." +!insertmacro LANG_STRING Section_Plugin_FeedReader "FeedReader" +!insertmacro LANG_STRING Section_Plugin_FeedReader_Desc "Instaluje wtyczki FeedReader" +!insertmacro LANG_STRING Section_Plugin_LinksCloud "LinksCloud" +!insertmacro LANG_STRING Section_Plugin_LinksCloud_Desc "Instaluje wtyczki LinksCloud." +!insertmacro LANG_STRING Section_Plugin_VOIP "VOIP" +!insertmacro LANG_STRING Section_Plugin_VOIP_Desc "Instaluje wtyczki VOIP." +!insertmacro LANG_STRING Section_AutoStart "Auto Startup" +!insertmacro LANG_STRING Section_AutoStart_Desc "Automatyczne uruchamianie przy starcie." +!insertmacro LANG_STRING Page_InstallMode "Tryb instalacji" +!insertmacro LANG_STRING Page_InstallMode_Desc "Wybierz jak chcesz zainstalować ${APPNAME}." +!insertmacro LANG_STRING Page_InstallMode_Standard "&Standardowa instalacja" +!insertmacro LANG_STRING Page_InstallMode_Standard_Desc "Zainstaluj ${APPNAME} dla bieżącego użytkownika tej maszyny." +!insertmacro LANG_STRING Page_InstallMode_Portable "&Portable installation" +!insertmacro LANG_STRING Page_InstallMode_Portable_Desc "W trybie przenośnym wszystkie dane konfiguracyjne są przechowywane w folderze aplikacji i nie są zapisywane w rejestrze." +!insertmacro LANG_STRING Link_Uninstall "Odinstaluj" diff --git a/build_scripts/Windows-msys2/installer/lang/ru.nsh b/build_scripts/Windows-msys2/installer/lang/ru.nsh new file mode 100644 index 000000000..09c578cb8 --- /dev/null +++ b/build_scripts/Windows-msys2/installer/lang/ru.nsh @@ -0,0 +1,29 @@ +!insertmacro LANG_STRING Section_Main "${APPNAME}" +!insertmacro LANG_STRING Section_Main_Desc "Установка ${APPNAME} и необходимых компонентов." +!insertmacro LANG_STRING Section_Data "Оболочки" +!insertmacro LANG_STRING Section_Data_Desc "Установка оболочек." +!insertmacro LANG_STRING Section_Shortcuts "Ярлыки" +!insertmacro LANG_STRING Section_Shortcuts_Desc "Добавление ярлыков." +!insertmacro LANG_STRING Section_StartMenu "Ярлык в меню Пуск" +!insertmacro LANG_STRING Section_StartMenu_Desc "Добавление ярлыка в меню Пуск." +!insertmacro LANG_STRING Section_Desktop "Ярлык на рабочем столе" +!insertmacro LANG_STRING Section_Desktop_Desc "Добавление ярлыка на рабочий стол." +!insertmacro LANG_STRING Section_QuickLaunch "Ярлык в панели быстрого запуска" +!insertmacro LANG_STRING Section_QuickLaunch_Desc "Добавление ярлыка на панель быстрого запуска." +!insertmacro LANG_STRING Section_Plugins "Дополнительные плагины" +!insertmacro LANG_STRING Section_Plugins_Desc "Дополнительные плагины для расширения функциональности." +!insertmacro LANG_STRING Section_Plugin_FeedReader "FeedReader – RSS-агрегатор" +!insertmacro LANG_STRING Section_Plugin_FeedReader_Desc "Установка плагина FeedReader." +!insertmacro LANG_STRING Section_Plugin_LinksCloud "LinksCloud – Облако ссылок" +!insertmacro LANG_STRING Section_Plugin_LinksCloud_Desc "Установка плагина LinksCloud." +!insertmacro LANG_STRING Section_Plugin_VOIP "VoIP" +!insertmacro LANG_STRING Section_Plugin_VOIP_Desc "Установка плагина VoIP." +!insertmacro LANG_STRING Section_AutoStart "Автозапуск" +!insertmacro LANG_STRING Section_AutoStart_Desc "Автозапуск при загрузке системы." +!insertmacro LANG_STRING Page_InstallMode "Режим установки" +!insertmacro LANG_STRING Page_InstallMode_Desc "Выберите метод установки ${APPNAME}." +!insertmacro LANG_STRING Page_InstallMode_Standard "Стандартная установка" +!insertmacro LANG_STRING Page_InstallMode_Standard_Desc "Установка ${APPNAME} для текущего пользователя компьютера." +!insertmacro LANG_STRING Page_InstallMode_Portable "Портативная установка" +!insertmacro LANG_STRING Page_InstallMode_Portable_Desc "В режиме портативной установки все конфигурационные файлы сохраняются в папку приложения и в системный реестр не записывается никакой информации." +!insertmacro LANG_STRING Link_Uninstall "Удаление программы" diff --git a/build_scripts/Windows-msys2/installer/lang/tr.nsh b/build_scripts/Windows-msys2/installer/lang/tr.nsh new file mode 100644 index 000000000..f893ac076 --- /dev/null +++ b/build_scripts/Windows-msys2/installer/lang/tr.nsh @@ -0,0 +1,29 @@ +!insertmacro LANG_STRING Section_Main "${APPNAME}" +!insertmacro LANG_STRING Section_Main_Desc "${APPNAME} ve gerekli bileşenleri kurar." +!insertmacro LANG_STRING Section_Data "Temalar" +!insertmacro LANG_STRING Section_Data_Desc "Tema yükleyin." +!insertmacro LANG_STRING Section_Shortcuts "Kısayol simgeleri" +!insertmacro LANG_STRING Section_Shortcuts_Desc "Kısayol simgesi ekleyin." +!insertmacro LANG_STRING Section_StartMenu "Başlat Menüsü simgesi" +!insertmacro LANG_STRING Section_StartMenu_Desc "Başlat menüsüne simge ekleyin." +!insertmacro LANG_STRING Section_Desktop "Masaüstü simgesi" +!insertmacro LANG_STRING Section_Desktop_Desc "Masaüstüne simge ekleyin." +!insertmacro LANG_STRING Section_QuickLaunch "Hızlı Başlat simgesi" +!insertmacro LANG_STRING Section_QuickLaunch_Desc "Hızlı Başlat araç çubuğuna simge ekleyin." +!insertmacro LANG_STRING Section_Plugins "İsteğe bağlı eklentiler" +!insertmacro LANG_STRING Section_Plugins_Desc "İşlevselliği artırmak için isteğe bağlı eklentiler." +!insertmacro LANG_STRING Section_Plugin_FeedReader "AkışOkuyucu" +!insertmacro LANG_STRING Section_Plugin_FeedReader_Desc "AkışOkuyucu eklentisini yükleyin." +!insertmacro LANG_STRING Section_Plugin_LinksCloud "BağlantıBulutu" +!insertmacro LANG_STRING Section_Plugin_LinksCloud_Desc "BağlantıBulutu eklentisini yükleyin." +!insertmacro LANG_STRING Section_Plugin_VOIP "VOIP" +!insertmacro LANG_STRING Section_Plugin_VOIP_Desc "VOIP eklentisini yükleyin." +!insertmacro LANG_STRING Section_AutoStart "Otomatik Başlat" +!insertmacro LANG_STRING Section_AutoStart_Desc "Açılışta otomatik başlatın." +!insertmacro LANG_STRING Page_InstallMode "Kurulum Yöntemi" +!insertmacro LANG_STRING Page_InstallMode_Desc "${APPNAME} kurulum şeklini seçin" +!insertmacro LANG_STRING Page_InstallMode_Standard "&Normal kurulum" +!insertmacro LANG_STRING Page_InstallMode_Standard_Desc "${APPNAME} yalnızca şimdiki kullanıcı için kurulsun." +!insertmacro LANG_STRING Page_InstallMode_Portable "&Taşınabilir kurulum" +!insertmacro LANG_STRING Page_InstallMode_Portable_Desc "Taşınabilir kipte tüm ayarlar uygulama klasörüne depolanır ve kayıt defterine hiçbir bilgi yazılmaz." +!insertmacro LANG_STRING Link_Uninstall "Kaldırın" diff --git a/build_scripts/Windows-msys2/installer/lang/ts/ca_ES.ts b/build_scripts/Windows-msys2/installer/lang/ts/ca_ES.ts new file mode 100644 index 000000000..add40b909 --- /dev/null +++ b/build_scripts/Windows-msys2/installer/lang/ts/ca_ES.ts @@ -0,0 +1,205 @@ + + + Section_Main + + ${APPNAME} + ${APPNAME} + + + + Section_Main_Desc + + Installs ${APPNAME} and required components. + Instal·la ${APPNAME} i els components necessaris. + + + + Section_Data + + Skins + Pells + + + + Section_Data_Desc + + Installs skins. + Instal·la pells. + + + + Section_Shortcuts + + Shortcut icons + Icones d'accés directe + + + + Section_Shortcuts_Desc + + Adds shortcut icons. + Afegir icones d'accés directe. + + + + Section_StartMenu + + Start Menu icon + Icona del menú d'inici + + + + Section_StartMenu_Desc + + Adds icon to start menu. + Afegir icona en el menú d'inici + + + + Section_Desktop + + Desktop icon + Icona d'escriptori + + + + Section_Desktop_Desc + + Adds icon to desktop. + Afegir icona a l'escriptori + + + + Section_QuickLaunch + + Quick Launch icon + Icona de la barra ràpida d'accés + + + + Section_QuickLaunch_Desc + + Adds icon to Quick Launch toolbar. + Afegir icona a la barra ràpida d'accés + + + + Section_Plugins + + Optional plugins + Complements opcionals + + + + Section_Plugins_Desc + + Optional plugins to extend functionality. + Components opcionals per afegir funcionalitat. + + + + Section_Plugin_FeedReader + + FeedReader + LectorFonts + + + + Section_Plugin_FeedReader_Desc + + Installs plugin FeedReader. + Instal·lar complement LectorFonts. + + + + Section_Plugin_LinksCloud + + LinksCloud + NúvolEnllaços + + + + Section_Plugin_LinksCloud_Desc + + Installs plugin LinksCloud. + Instal·lar complement NúvolEnllaços. + + + + Section_Plugin_VOIP + + VOIP + VeuIP + + + + Section_Plugin_VOIP_Desc + + Installs plugin VOIP. + Instal·lar complement VeuIP. + + + + Section_AutoStart + + Auto Startup + Posada en marxa automàtica + + + + Section_AutoStart_Desc + + Auto-Run at startup. + Autoengegar al arrencar. + + + + Page_InstallMode + + Installation Mode + Mode instal·lació + + + + Page_InstallMode_Desc + + Choose how you want to install ${APPNAME}. + Escull com vols instal·lar ${APPNAME}. + + + + Page_InstallMode_Standard + + &Standard installation + Instal·lació e&stàndard + + + + Page_InstallMode_Standard_Desc + + Install ${APPNAME} for the current user of this machine. + Instal·la ${APPNAME} per la sessió d'usuari actual. + + + + Page_InstallMode_Portable + + &Portable installation + Instal·lació &portable. + + + + Page_InstallMode_Portable_Desc + + In portable mode all configuration data is stored in the application folder and no information is written to the registry. + En mode portable les dades de configuració s'emmagatzemen a la carpeta d'aplicació i no s'escriu informació al registre del sistema. + + + + Link_Uninstall + + Uninstall + Desinstal·lar. + + + \ No newline at end of file diff --git a/build_scripts/Windows-msys2/installer/lang/ts/convert_from_ts.bat b/build_scripts/Windows-msys2/installer/lang/ts/convert_from_ts.bat new file mode 100644 index 000000000..d514256e1 --- /dev/null +++ b/build_scripts/Windows-msys2/installer/lang/ts/convert_from_ts.bat @@ -0,0 +1,27 @@ +@echo off + +:: Very simple conversion from *.ts to *.nsh using xslt + +pushd "%~dp0" + +if "%1"=="" ( + for %%F in (*.ts) do if "%%F" NEQ "en.ts" call :convert %%~nF + goto :exit +) + +call :convert %1 + +:exit +popd + +goto :EOF + +:convert +if not exist "%~1.ts" ( + echo File "%~1.ts" not found. + goto :EOF +) + +echo %~1 + +"%~dp0xsltproc.exe" --output "%~dp0..\%~1.nsh" "%~dp0convert_from_ts.xsl" "%~1.ts" diff --git a/build_scripts/Windows-msys2/installer/lang/ts/convert_from_ts.xsl b/build_scripts/Windows-msys2/installer/lang/ts/convert_from_ts.xsl new file mode 100644 index 000000000..a376fe691 --- /dev/null +++ b/build_scripts/Windows-msys2/installer/lang/ts/convert_from_ts.xsl @@ -0,0 +1,20 @@ + + + + + + + + !insertmacro LANG_STRING + + " + + + + + " + + + + \ No newline at end of file diff --git a/build_scripts/Windows-msys2/installer/lang/ts/convert_to_ts.bat b/build_scripts/Windows-msys2/installer/lang/ts/convert_to_ts.bat new file mode 100644 index 000000000..9235d6bab --- /dev/null +++ b/build_scripts/Windows-msys2/installer/lang/ts/convert_to_ts.bat @@ -0,0 +1,50 @@ +@echo off + +:: Very simple conversion from *.nsh to *.ts + +setlocal + +set Language=en +if "%1" NEQ "" set Language=%1 + +set InputFile=%~dp0..\%Language%.nsh +set OutputFile=%~dp0%Language%.ts + +if not exist "%InputFile%" ( + echo File %InputFile% not found. + goto :exit +) + +echo ^ >"%OutputFile%" +echo ^ >>"%OutputFile%" +echo ^ >>"%OutputFile%" + +for /F "tokens=1,2,3,*" %%A in (%InputFile%) do if "%%A"=="!insertmacro" call :context %%C %%D + +echo ^ >>"%OutputFile%" + +:exit +endlocal +goto :EOF + +:context + +setlocal EnableDelayedExpansion + +:: Simple replace of & to & +set Text=%2 +set Text=%Text:&=^&% +set Text=%Text:~1,-1% + +echo !Text! + +echo ^ >>"%OutputFile%" +echo ^%~1^ >>"%OutputFile%" +echo ^ >>"%OutputFile%" +echo ^!Text!^ >>"%OutputFile%" +echo ^^ >>"%OutputFile%" +echo ^ >>"%OutputFile%" +echo ^ >>"%OutputFile%" + +endlocal +goto :EOF diff --git a/build_scripts/Windows-msys2/installer/lang/ts/de.ts b/build_scripts/Windows-msys2/installer/lang/ts/de.ts new file mode 100644 index 000000000..4469c6818 --- /dev/null +++ b/build_scripts/Windows-msys2/installer/lang/ts/de.ts @@ -0,0 +1,205 @@ + + + Section_Main + + ${APPNAME} + ${APPNAME} + + + + Section_Main_Desc + + Installs ${APPNAME} and required components. + Installiert ${APPNAME} und die benötigten Komponenten. + + + + Section_Data + + Skins + Skins + + + + Section_Data_Desc + + Installs skins. + Skins installieren. + + + + Section_Shortcuts + + Shortcut icons + Verknüpfungssymbole + + + + Section_Shortcuts_Desc + + Adds shortcut icons. + Verküpfungssymbole hinzufügen. + + + + Section_StartMenu + + Start Menu icon + Startmenüsymbol + + + + Section_StartMenu_Desc + + Adds icon to start menu. + Fügt Symbol zum Startmenü hinzu. + + + + Section_Desktop + + Desktop icon + Desktopsymbol + + + + Section_Desktop_Desc + + Adds icon to desktop. + Fügt Symbol zum Desktop hinzu. + + + + Section_QuickLaunch + + Quick Launch icon + Schnellstartsymbol + + + + Section_QuickLaunch_Desc + + Adds icon to Quick Launch toolbar. + Fügt Symbol zur Schnellstartleiste hinzu. + + + + Section_Plugins + + Optional plugins + Optionale Plug-ins + + + + Section_Plugins_Desc + + Optional plugins to extend functionality. + Optionale Plug-ins zum Erweitern der Funktionalität. + + + + Section_Plugin_FeedReader + + FeedReader + FeedReader + + + + Section_Plugin_FeedReader_Desc + + Installs plugin FeedReader. + Installiert das Plug-in Feedreader. + + + + Section_Plugin_LinksCloud + + LinksCloud + Verknüpfungswolke + + + + Section_Plugin_LinksCloud_Desc + + Installs plugin LinksCloud. + Installiert das Plug-in Verknüpfungswolke. + + + + Section_Plugin_VOIP + + VOIP + VOIP + + + + Section_Plugin_VOIP_Desc + + Installs plugin VOIP. + Installiert das Plug-in VOIP + + + + Section_AutoStart + + Auto Startup + Automatischer Programmstart + + + + Section_AutoStart_Desc + + Auto-Run at startup. + Beim Start automatisch ausführen. + + + + Page_InstallMode + + Installation Mode + Installationsmodus + + + + Page_InstallMode_Desc + + Choose how you want to install ${APPNAME}. + Wähle wie du ${APPNAME} installieren willst. + + + + Page_InstallMode_Standard + + &Standard installation + &Standardinstallation + + + + Page_InstallMode_Standard_Desc + + Install ${APPNAME} for the current user of this machine. + Installiere ${APPNAME} für den derzeitigen Benutzer dieses Geräts. + + + + Page_InstallMode_Portable + + &Portable installation + &Portable Installation + + + + Page_InstallMode_Portable_Desc + + In portable mode all configuration data is stored in the application folder and no information is written to the registry. + Im portablen Modus werden alle Konfigurationsdaten im Verzeichnis der Anwendung gespeichert und keine Informationen in der Registry abgelegt. + + + + Link_Uninstall + + Uninstall + Deinstallieren + + + \ No newline at end of file diff --git a/build_scripts/Windows-msys2/installer/lang/ts/en.ts b/build_scripts/Windows-msys2/installer/lang/ts/en.ts new file mode 100644 index 000000000..800157426 --- /dev/null +++ b/build_scripts/Windows-msys2/installer/lang/ts/en.ts @@ -0,0 +1,186 @@ + + + + + Section_Main_Desc + + Installs ${APPNAME} and required components. + + + + + Section_Data + + Skins + + + + + Section_Data_Desc + + Installs skins. + + + + + Section_Shortcuts + + Shortcut icons + + + + + Section_Shortcuts_Desc + + Adds shortcut icons. + + + + + Section_StartMenu + + Start Menu icon + + + + + Section_StartMenu_Desc + + Adds icon to start menu. + + + + + Section_Desktop + + Desktop icon + + + + + Section_Desktop_Desc + + Adds icon to desktop. + + + + + Section_QuickLaunch + + Quick Launch icon + + + + + Section_QuickLaunch_Desc + + Adds icon to Quick Launch toolbar. + + + + + Section_Plugins + + Optional plugins + + + + + Section_Plugins_Desc + + Optional plugins to extend functionality. + + + + + Section_Plugin_FeedReader + + FeedReader + + + + + Section_Plugin_FeedReader_Desc + + Installs plugin FeedReader. + + + + + Section_Plugin_VOIP + + VOIP + + + + + Section_Plugin_VOIP_Desc + + Installs plugin VOIP. + + + + + Section_AutoStart + + Auto Startup + + + + + Section_AutoStart_Desc + + Auto-Run at startup. + + + + + Page_InstallMode + + Installation Mode + + + + + Page_InstallMode_Desc + + Choose how you want to install ${APPNAME}. + + + + + Page_InstallMode_Standard + + &Standard installation + + + + + Page_InstallMode_Standard_Desc + + Install ${APPNAME} for the current user of this machine. + + + + + Page_InstallMode_Portable + + &Portable installation + + + + + Page_InstallMode_Portable_Desc + + In portable mode all configuration data is stored in the application folder and no information is written to the registry. + + + + + Link_Uninstall + + Uninstall + + + + diff --git a/build_scripts/Windows-msys2/installer/lang/ts/es.ts b/build_scripts/Windows-msys2/installer/lang/ts/es.ts new file mode 100644 index 000000000..dab920294 --- /dev/null +++ b/build_scripts/Windows-msys2/installer/lang/ts/es.ts @@ -0,0 +1,205 @@ + + + Section_Main + + ${APPNAME} + ${APPNAME} + + + + Section_Main_Desc + + Installs ${APPNAME} and required components. + Instala ${APPNAME} y los componentes requeridos. + + + + Section_Data + + Skins + Coberturas (skins) + + + + Section_Data_Desc + + Installs skins. + Instalar coberturas + + + + Section_Shortcuts + + Shortcut icons + Iconos de accesos directos + + + + Section_Shortcuts_Desc + + Adds shortcut icons. + Añade iconos de accesos directos. + + + + Section_StartMenu + + Start Menu icon + Icono de menú de inicio + + + + Section_StartMenu_Desc + + Adds icon to start menu. + Añade icono al menú de inicio. + + + + Section_Desktop + + Desktop icon + Icono del escritorio + + + + Section_Desktop_Desc + + Adds icon to desktop. + Añade icono al escritorio + + + + Section_QuickLaunch + + Quick Launch icon + Icono de inicio rápido + + + + Section_QuickLaunch_Desc + + Adds icon to Quick Launch toolbar. + Añade icono a la Barra de Inicio Rápido + + + + Section_Plugins + + Optional plugins + Complementos opcionales + + + + Section_Plugins_Desc + + Optional plugins to extend functionality. + Complementos opcionales para expandir la funcionalidad. + + + + Section_Plugin_FeedReader + + FeedReader + FeedReader + + + + Section_Plugin_FeedReader_Desc + + Installs plugin FeedReader. + Instala el complemento FeedReader. + + + + Section_Plugin_LinksCloud + + LinksCloud + LinksCloud + + + + Section_Plugin_LinksCloud_Desc + + Installs plugin LinksCloud. + Instala el complemento LinksCloud. + + + + Section_Plugin_VOIP + + VOIP + VOIP + + + + Section_Plugin_VOIP_Desc + + Installs plugin VOIP. + Instala el complemento VOIP + + + + Section_AutoStart + + Auto Startup + Auto iniciar + + + + Section_AutoStart_Desc + + Auto-Run at startup. + Auto-ejecutar al incio. + + + + Page_InstallMode + + Installation Mode + Modo de instalación + + + + Page_InstallMode_Desc + + Choose how you want to install ${APPNAME}. + Elija cómo quiere instalar ${APPNAME}. + + + + Page_InstallMode_Standard + + &Standard installation + Instalación &Estándar + + + + Page_InstallMode_Standard_Desc + + Install ${APPNAME} for the current user of this machine. + Instalar ${APPNAME} para el usuario actual de esta máquina. + + + + Page_InstallMode_Portable + + &Portable installation + Instalación &Portable + + + + Page_InstallMode_Portable_Desc + + In portable mode all configuration data is stored in the application folder and no information is written to the registry. + En modo portable, todos los datos de configuración se almacenan en la carpeta de la aplicación y no se escribe ninguna información en el registro. + + + + Link_Uninstall + + Uninstall + Desinstalar + + + \ No newline at end of file diff --git a/build_scripts/Windows-msys2/installer/lang/ts/fr.ts b/build_scripts/Windows-msys2/installer/lang/ts/fr.ts new file mode 100644 index 000000000..6ba710f7d --- /dev/null +++ b/build_scripts/Windows-msys2/installer/lang/ts/fr.ts @@ -0,0 +1,205 @@ + + + Section_Main + + ${APPNAME} + ${APPNAME} + + + + Section_Main_Desc + + Installs ${APPNAME} and required components. + Installe ${APPNAME} et les composants requis. + + + + Section_Data + + Skins + Habillages + + + + Section_Data_Desc + + Installs skins. + Installe des habillages. + + + + Section_Shortcuts + + Shortcut icons + Icônes de raccourci + + + + Section_Shortcuts_Desc + + Adds shortcut icons. + Ajoute les icônes de raccourci. + + + + Section_StartMenu + + Start Menu icon + Icône de menu démarrage + + + + Section_StartMenu_Desc + + Adds icon to start menu. + Ajoute l'icône au menu démarrer. + + + + Section_Desktop + + Desktop icon + Icônes de bureau + + + + Section_Desktop_Desc + + Adds icon to desktop. + Ajoute l'icône sur le bureau. + + + + Section_QuickLaunch + + Quick Launch icon + Icône de lancement rapide + + + + Section_QuickLaunch_Desc + + Adds icon to Quick Launch toolbar. + Ajoute une icône à la barre d'outils de lancement rapide. + + + + Section_Plugins + + Optional plugins + Plug-ins optionnels + + + + Section_Plugins_Desc + + Optional plugins to extend functionality. + Plug-ins optionnels destinés à étendre les fonctionnalités. + + + + Section_Plugin_FeedReader + + FeedReader + FeedReader + + + + Section_Plugin_FeedReader_Desc + + Installs plugin FeedReader. + Installe le plug-in FeedReader. + + + + Section_Plugin_LinksCloud + + LinksCloud + LinksCloud + + + + Section_Plugin_LinksCloud_Desc + + Installs plugin LinksCloud. + Installe le plug-in LinksCloud. + + + + Section_Plugin_VOIP + + VOIP + VOIP + + + + Section_Plugin_VOIP_Desc + + Installs plugin VOIP. + Installe le plug-in VOIP. + + + + Section_AutoStart + + Auto Startup + Démarrage automatique + + + + Section_AutoStart_Desc + + Auto-Run at startup. + Démarrage automatique au démarrage. + + + + Page_InstallMode + + Installation Mode + Mode d'installation + + + + Page_InstallMode_Desc + + Choose how you want to install ${APPNAME}. + Choisissez comment vous voulez installer ${APPNAME}. + + + + Page_InstallMode_Standard + + &Standard installation + &Installation standard + + + + Page_InstallMode_Standard_Desc + + Install ${APPNAME} for the current user of this machine. + Installer ${APPNAME} pour l'utilisateur actuel de cette machine. + + + + Page_InstallMode_Portable + + &Portable installation + &Installation portable + + + + Page_InstallMode_Portable_Desc + + In portable mode all configuration data is stored in the application folder and no information is written to the registry. + En mode portable toutes les données de configuration sont stockées dans le dossier de l'application et aucune information n'est écrite dans la base de registre. + + + + Link_Uninstall + + Uninstall + Désinstaller + + + \ No newline at end of file diff --git a/build_scripts/Windows-msys2/installer/lang/ts/pl.ts b/build_scripts/Windows-msys2/installer/lang/ts/pl.ts new file mode 100644 index 000000000..deb56164f --- /dev/null +++ b/build_scripts/Windows-msys2/installer/lang/ts/pl.ts @@ -0,0 +1,205 @@ + + + Section_Main + + ${APPNAME} + ${APPNAME} + + + + Section_Main_Desc + + Installs ${APPNAME} and required components. + Instaluje ${APPNAME} oraz wymagane komponenty. + + + + Section_Data + + Skins + Skórki + + + + Section_Data_Desc + + Installs skins. + Instaluje skórki. + + + + Section_Shortcuts + + Shortcut icons + Ikony skrótów + + + + Section_Shortcuts_Desc + + Adds shortcut icons. + Dodaje skróty ikon. + + + + Section_StartMenu + + Start Menu icon + + + + + Section_StartMenu_Desc + + Adds icon to start menu. + Dodaje ikonę do menu start. + + + + Section_Desktop + + Desktop icon + Ikona na pulpicie + + + + Section_Desktop_Desc + + Adds icon to desktop. + Dodawanie ikony na pulpicie + + + + Section_QuickLaunch + + Quick Launch icon + Ikona szybkiego uruchamiania + + + + Section_QuickLaunch_Desc + + Adds icon to Quick Launch toolbar. + Dodaje ikonę do paska Szybkiego Uruchamiania. + + + + Section_Plugins + + Optional plugins + Wtyczki opcjonalne + + + + Section_Plugins_Desc + + Optional plugins to extend functionality. + Wtyczki opcjonalne rozszerzające funkcjonalność. + + + + Section_Plugin_FeedReader + + FeedReader + FeedReader + + + + Section_Plugin_FeedReader_Desc + + Installs plugin FeedReader. + Instaluje wtyczki FeedReader + + + + Section_Plugin_LinksCloud + + LinksCloud + + + + + Section_Plugin_LinksCloud_Desc + + Installs plugin LinksCloud. + Instaluje wtyczki LinksCloud. + + + + Section_Plugin_VOIP + + VOIP + VOIP + + + + Section_Plugin_VOIP_Desc + + Installs plugin VOIP. + Instaluje wtyczki VOIP. + + + + Section_AutoStart + + Auto Startup + + + + + Section_AutoStart_Desc + + Auto-Run at startup. + Automatyczne uruchamianie przy starcie. + + + + Page_InstallMode + + Installation Mode + Tryb instalacji + + + + Page_InstallMode_Desc + + Choose how you want to install ${APPNAME}. + Wybierz jak chcesz zainstalować ${APPNAME}. + + + + Page_InstallMode_Standard + + &Standard installation + &Standardowa instalacja + + + + Page_InstallMode_Standard_Desc + + Install ${APPNAME} for the current user of this machine. + Zainstaluj ${APPNAME} dla bieżącego użytkownika tej maszyny. + + + + Page_InstallMode_Portable + + &Portable installation + + + + + Page_InstallMode_Portable_Desc + + In portable mode all configuration data is stored in the application folder and no information is written to the registry. + W trybie przenośnym wszystkie dane konfiguracyjne są przechowywane w folderze aplikacji i nie są zapisywane w rejestrze. + + + + Link_Uninstall + + Uninstall + Odinstaluj + + + \ No newline at end of file diff --git a/build_scripts/Windows-msys2/installer/lang/ts/ru.ts b/build_scripts/Windows-msys2/installer/lang/ts/ru.ts new file mode 100644 index 000000000..3c29f3955 --- /dev/null +++ b/build_scripts/Windows-msys2/installer/lang/ts/ru.ts @@ -0,0 +1,205 @@ + + + Section_Main + + ${APPNAME} + ${APPNAME} + + + + Section_Main_Desc + + Installs ${APPNAME} and required components. + Установка ${APPNAME} и необходимых компонентов. + + + + Section_Data + + Skins + Оболочки + + + + Section_Data_Desc + + Installs skins. + Установка оболочек. + + + + Section_Shortcuts + + Shortcut icons + Ярлыки + + + + Section_Shortcuts_Desc + + Adds shortcut icons. + Добавление ярлыков. + + + + Section_StartMenu + + Start Menu icon + Ярлык в меню Пуск + + + + Section_StartMenu_Desc + + Adds icon to start menu. + Добавление ярлыка в меню Пуск. + + + + Section_Desktop + + Desktop icon + Ярлык на рабочем столе + + + + Section_Desktop_Desc + + Adds icon to desktop. + Добавление ярлыка на рабочий стол. + + + + Section_QuickLaunch + + Quick Launch icon + Ярлык в панели быстрого запуска + + + + Section_QuickLaunch_Desc + + Adds icon to Quick Launch toolbar. + Добавление ярлыка на панель быстрого запуска. + + + + Section_Plugins + + Optional plugins + Дополнительные плагины + + + + Section_Plugins_Desc + + Optional plugins to extend functionality. + Дополнительные плагины для расширения функциональности. + + + + Section_Plugin_FeedReader + + FeedReader + FeedReader – RSS-агрегатор + + + + Section_Plugin_FeedReader_Desc + + Installs plugin FeedReader. + Установка плагина FeedReader. + + + + Section_Plugin_LinksCloud + + LinksCloud + LinksCloud – Облако ссылок + + + + Section_Plugin_LinksCloud_Desc + + Installs plugin LinksCloud. + Установка плагина LinksCloud. + + + + Section_Plugin_VOIP + + VOIP + VoIP + + + + Section_Plugin_VOIP_Desc + + Installs plugin VOIP. + Установка плагина VoIP. + + + + Section_AutoStart + + Auto Startup + Автозапуск + + + + Section_AutoStart_Desc + + Auto-Run at startup. + Автозапуск при загрузке системы. + + + + Page_InstallMode + + Installation Mode + Режим установки + + + + Page_InstallMode_Desc + + Choose how you want to install ${APPNAME}. + Выберите метод установки ${APPNAME}. + + + + Page_InstallMode_Standard + + &Standard installation + Стандартная установка + + + + Page_InstallMode_Standard_Desc + + Install ${APPNAME} for the current user of this machine. + Установка ${APPNAME} для текущего пользователя компьютера. + + + + Page_InstallMode_Portable + + &Portable installation + Портативная установка + + + + Page_InstallMode_Portable_Desc + + In portable mode all configuration data is stored in the application folder and no information is written to the registry. + В режиме портативной установки все конфигурационные файлы сохраняются в папку приложения и в системный реестр не записывается никакой информации. + + + + Link_Uninstall + + Uninstall + Удаление программы + + + \ No newline at end of file diff --git a/build_scripts/Windows-msys2/installer/lang/ts/tr.ts b/build_scripts/Windows-msys2/installer/lang/ts/tr.ts new file mode 100644 index 000000000..d67961d59 --- /dev/null +++ b/build_scripts/Windows-msys2/installer/lang/ts/tr.ts @@ -0,0 +1,205 @@ + + + Section_Main + + ${APPNAME} + ${APPNAME} + + + + Section_Main_Desc + + Installs ${APPNAME} and required components. + ${APPNAME} ve gerekli bileşenleri kurar. + + + + Section_Data + + Skins + Temalar + + + + Section_Data_Desc + + Installs skins. + Tema yükleyin. + + + + Section_Shortcuts + + Shortcut icons + Kısayol simgeleri + + + + Section_Shortcuts_Desc + + Adds shortcut icons. + Kısayol simgesi ekleyin. + + + + Section_StartMenu + + Start Menu icon + Başlat Menüsü simgesi + + + + Section_StartMenu_Desc + + Adds icon to start menu. + Başlat menüsüne simge ekleyin. + + + + Section_Desktop + + Desktop icon + Masaüstü simgesi + + + + Section_Desktop_Desc + + Adds icon to desktop. + Masaüstüne simge ekleyin. + + + + Section_QuickLaunch + + Quick Launch icon + Hızlı Başlat simgesi + + + + Section_QuickLaunch_Desc + + Adds icon to Quick Launch toolbar. + Hızlı Başlat araç çubuğuna simge ekleyin. + + + + Section_Plugins + + Optional plugins + İsteğe bağlı eklentiler + + + + Section_Plugins_Desc + + Optional plugins to extend functionality. + İşlevselliği artırmak için isteğe bağlı eklentiler. + + + + Section_Plugin_FeedReader + + FeedReader + AkışOkuyucu + + + + Section_Plugin_FeedReader_Desc + + Installs plugin FeedReader. + AkışOkuyucu eklentisini yükleyin. + + + + Section_Plugin_LinksCloud + + LinksCloud + BağlantıBulutu + + + + Section_Plugin_LinksCloud_Desc + + Installs plugin LinksCloud. + BağlantıBulutu eklentisini yükleyin. + + + + Section_Plugin_VOIP + + VOIP + VOIP + + + + Section_Plugin_VOIP_Desc + + Installs plugin VOIP. + VOIP eklentisini yükleyin. + + + + Section_AutoStart + + Auto Startup + Otomatik Başlat + + + + Section_AutoStart_Desc + + Auto-Run at startup. + Açılışta otomatik başlatın. + + + + Page_InstallMode + + Installation Mode + Kurulum Yöntemi + + + + Page_InstallMode_Desc + + Choose how you want to install ${APPNAME}. + ${APPNAME} kurulum şeklini seçin + + + + Page_InstallMode_Standard + + &Standard installation + &Normal kurulum + + + + Page_InstallMode_Standard_Desc + + Install ${APPNAME} for the current user of this machine. + ${APPNAME} yalnızca şimdiki kullanıcı için kurulsun. + + + + Page_InstallMode_Portable + + &Portable installation + &Taşınabilir kurulum + + + + Page_InstallMode_Portable_Desc + + In portable mode all configuration data is stored in the application folder and no information is written to the registry. + Taşınabilir kipte tüm ayarlar uygulama klasörüne depolanır ve kayıt defterine hiçbir bilgi yazılmaz. + + + + Link_Uninstall + + Uninstall + Kaldırın + + + \ No newline at end of file diff --git a/build_scripts/Windows-msys2/installer/lang/ts/xsltproc.exe b/build_scripts/Windows-msys2/installer/lang/ts/xsltproc.exe new file mode 100644 index 000000000..79c250892 Binary files /dev/null and b/build_scripts/Windows-msys2/installer/lang/ts/xsltproc.exe differ diff --git a/build_scripts/Windows-msys2/installer/lang/ts/zh_CN.ts b/build_scripts/Windows-msys2/installer/lang/ts/zh_CN.ts new file mode 100644 index 000000000..30c992601 --- /dev/null +++ b/build_scripts/Windows-msys2/installer/lang/ts/zh_CN.ts @@ -0,0 +1,205 @@ + + + Section_Main + + ${APPNAME} + + + + + Section_Main_Desc + + Installs ${APPNAME} and required components. + + + + + Section_Data + + Skins + 皮肤 + + + + Section_Data_Desc + + Installs skins. + 安装皮肤 + + + + Section_Shortcuts + + Shortcut icons + 快捷方式图标 + + + + Section_Shortcuts_Desc + + Adds shortcut icons. + 添加快捷方式图标 + + + + Section_StartMenu + + Start Menu icon + 开始菜单图标 + + + + Section_StartMenu_Desc + + Adds icon to start menu. + 添加图标至开始菜单 + + + + Section_Desktop + + Desktop icon + 桌面图标 + + + + Section_Desktop_Desc + + Adds icon to desktop. + 添加图标至桌面 + + + + Section_QuickLaunch + + Quick Launch icon + 快速启动栏图标 + + + + Section_QuickLaunch_Desc + + Adds icon to Quick Launch toolbar. + 添加图标至快速启动栏 + + + + Section_Plugins + + Optional plugins + + + + + Section_Plugins_Desc + + Optional plugins to extend functionality. + + + + + Section_Plugin_FeedReader + + FeedReader + RSS订阅 + + + + Section_Plugin_FeedReader_Desc + + Installs plugin FeedReader. + 安装RSS订阅插件 + + + + Section_Plugin_LinksCloud + + LinksCloud + LinksCloud + + + + Section_Plugin_LinksCloud_Desc + + Installs plugin LinksCloud. + 安装插件LinksCloud + + + + Section_Plugin_VOIP + + VOIP + 语音 + + + + Section_Plugin_VOIP_Desc + + Installs plugin VOIP. + 安装语音插件 + + + + Section_AutoStart + + Auto Startup + + + + + Section_AutoStart_Desc + + Auto-Run at startup. + + + + + Page_InstallMode + + Installation Mode + 安装模式 + + + + Page_InstallMode_Desc + + Choose how you want to install ${APPNAME}. + + + + + Page_InstallMode_Standard + + &Standard installation + + + + + Page_InstallMode_Standard_Desc + + Install ${APPNAME} for the current user of this machine. + + + + + Page_InstallMode_Portable + + &Portable installation + + + + + Page_InstallMode_Portable_Desc + + In portable mode all configuration data is stored in the application folder and no information is written to the registry. + + + + + Link_Uninstall + + Uninstall + 卸载 + + + \ No newline at end of file diff --git a/build_scripts/Windows-msys2/installer/lang/zh_CN.nsh b/build_scripts/Windows-msys2/installer/lang/zh_CN.nsh new file mode 100644 index 000000000..1075f7a1a --- /dev/null +++ b/build_scripts/Windows-msys2/installer/lang/zh_CN.nsh @@ -0,0 +1,29 @@ +!insertmacro LANG_STRING Section_Main "${APPNAME}" +!insertmacro LANG_STRING Section_Main_Desc "Installs ${APPNAME} and required components." +!insertmacro LANG_STRING Section_Data "皮肤" +!insertmacro LANG_STRING Section_Data_Desc "安装皮肤" +!insertmacro LANG_STRING Section_Shortcuts "快捷方式图标" +!insertmacro LANG_STRING Section_Shortcuts_Desc "添加快捷方式图标" +!insertmacro LANG_STRING Section_StartMenu "开始菜单图标" +!insertmacro LANG_STRING Section_StartMenu_Desc "添加图标至开始菜单" +!insertmacro LANG_STRING Section_Desktop "桌面图标" +!insertmacro LANG_STRING Section_Desktop_Desc "添加图标至桌面" +!insertmacro LANG_STRING Section_QuickLaunch "快速启动栏图标" +!insertmacro LANG_STRING Section_QuickLaunch_Desc "添加图标至快速启动栏" +!insertmacro LANG_STRING Section_Plugins "Optional plugins" +!insertmacro LANG_STRING Section_Plugins_Desc "Optional plugins to extend functionality." +!insertmacro LANG_STRING Section_Plugin_FeedReader "RSS订阅" +!insertmacro LANG_STRING Section_Plugin_FeedReader_Desc "安装RSS订阅插件" +!insertmacro LANG_STRING Section_Plugin_LinksCloud "LinksCloud" +!insertmacro LANG_STRING Section_Plugin_LinksCloud_Desc "安装插件LinksCloud" +!insertmacro LANG_STRING Section_Plugin_VOIP "语音" +!insertmacro LANG_STRING Section_Plugin_VOIP_Desc "安装语音插件" +!insertmacro LANG_STRING Section_AutoStart "Auto Startup" +!insertmacro LANG_STRING Section_AutoStart_Desc "Auto-Run at startup." +!insertmacro LANG_STRING Page_InstallMode "安装模式" +!insertmacro LANG_STRING Page_InstallMode_Desc "Choose how you want to install ${APPNAME}." +!insertmacro LANG_STRING Page_InstallMode_Standard "&Standard installation" +!insertmacro LANG_STRING Page_InstallMode_Standard_Desc "Install ${APPNAME} for the current user of this machine." +!insertmacro LANG_STRING Page_InstallMode_Portable "&Portable installation" +!insertmacro LANG_STRING Page_InstallMode_Portable_Desc "In portable mode all configuration data is stored in the application folder and no information is written to the registry." +!insertmacro LANG_STRING Link_Uninstall "卸载" diff --git a/build_scripts/Windows/installer/retroshare-Qt4.nsi b/build_scripts/Windows-msys2/installer/retroshare-Qt5.nsi similarity index 79% rename from build_scripts/Windows/installer/retroshare-Qt4.nsi rename to build_scripts/Windows-msys2/installer/retroshare-Qt5.nsi index cb6f69c74..b30300634 100644 --- a/build_scripts/Windows/installer/retroshare-Qt4.nsi +++ b/build_scripts/Windows-msys2/installer/retroshare-Qt5.nsi @@ -1,481 +1,445 @@ -; Script generated with the Venis Install Wizard & modified by defnax -; Reworked by Thunder - -!include ifexist.nsh - -# Needed defines -;!define REVISION "" -;!define RELEASEDIR "" -;!define QTDIR "" -;!define MINGWDIR "" - -# Optional defines -;!define OUTDIR "" - -# Check needed defines -!ifndef RELEASEDIR -!error "RELEASEDIR is not defined" -!endif -!ifndef QTDIR -!error "QTDIR is not defined" -!endif -!ifndef MINGWDIR -!error "MINGWDIR is not defined" -!endif - -# Check optional defines -!ifdef OUTDIR -!define OUTDIR_ "${OUTDIR}\" -!else -!define OUTDIR "" -!define OUTDIR_ "" -!endif - -!ifndef INSTALLERADD -!define INSTALLERADD "" -!endif - -# Source directory -!define SOURCEDIR "..\..\.." - -# Get version from executable -!GetDllVersion "${RELEASEDIR}\retroshare-gui\src\release\retroshare.exe" VERSION_ -!define VERSION ${VERSION_1}.${VERSION_2}.${VERSION_3} -;!define REVISION ${VERSION_4} - -# Get version of Qt -!GetDllVersion "${QTDIR}\bin\QtCore4.dll" QTVERSION_ -!define QTVERSION ${QTVERSION_1}.${QTVERSION_2}.${QTVERSION_3} - -# Check version -!ifndef REVISION -!error "REVISION is not defined" -!endif - -# Date -!define /date Date "%Y%m%d" - -# Application name and version -!define APPNAME "RetroShare" -!define APPNAMEANDVERSION "${APPNAME} ${VERSION}" -!define PUBLISHER "RetroShare Team" - -# Install path -!define INSTDIR_NORMAL "$ProgramFiles\${APPNAME}" -!define INSTDIR_PORTABLE "$Desktop\${APPNAME}" - -!define DATADIR_NORMAL "$APPDATA\${APPNAME}" -!define DATADIR_PORTABLE "$INSTDIR\Data" - -# Main Install settings -Name "${APPNAMEANDVERSION}" -InstallDirRegKey HKLM "Software\${APPNAME}" "" -OutFile "${OUTDIR_}RetroShare-${VERSION}-${Date}-${REVISION}-Qt-${QTVERSION}${INSTALLERADD}-setup.exe" -BrandingText "${APPNAMEANDVERSION}" -RequestExecutionlevel highest -# Use compression -SetCompressor /SOLID LZMA - -# Global variables -Var PortableMode -Var InstDirNormal -Var InstDirPortable -Var DataDir -Var StyleSheetDir - -# Modern interface settings -!include Sections.nsh -!include nsDialogs.nsh -!include "MUI.nsh" - -# Interface Settings -!define MUI_ABORTWARNING -!define MUI_HEADERIMAGE -!define MUI_HEADERIMAGE_BITMAP "${SOURCEDIR}\build_scripts\Windows\installer\HeaderImage.bmp" -;!define MUI_WELCOMEFINISHPAGE_BITMAP "...bmp" - -# MUI defines -!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\orange-install.ico" -!define MUI_FINISHPAGE_NOAUTOCLOSE -!define MUI_LICENSEPAGE_RADIOBUTTONS -!define MUI_COMPONENTSPAGE_SMALLDESC -!define MUI_FINISHPAGE_LINK "Visit the RetroShare forum for the latest news and support" -!define MUI_FINISHPAGE_LINK_LOCATION "http://retroshare.sourceforge.net/forum/" -!define MUI_FINISHPAGE_RUN "$INSTDIR\retroshare.exe" -!define MUI_FINISHPAGE_SHOWREADME $INSTDIR\changelog.txt -!define MUI_FINISHPAGE_SHOWREADME_TEXT changelog.txt -!define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED -!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\orange-uninstall.ico" -!define MUI_UNFINISHPAGE_NOAUTOCLOSE -;!define MUI_LANGDLL_REGISTRY_ROOT HKLM -;!define MUI_LANGDLL_REGISTRY_KEY ${REGKEY} -;!define MUI_LANGDLL_REGISTRY_VALUENAME InstallerLanguage - -# Defines the un-/installer logo of RetroShare -!insertmacro MUI_DEFAULT MUI_WELCOMEFINISHPAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Wizard\orange.bmp" -!insertmacro MUI_DEFAULT MUI_UNWELCOMEFINISHPAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Wizard\orange-uninstall.bmp" - -# Installer pages -!insertmacro MUI_PAGE_WELCOME -!insertmacro MUI_PAGE_LICENSE "$(myLicenseData)" -Page Custom PortableModePageCreate PortableModePageLeave -!define MUI_PAGE_CUSTOMFUNCTION_LEAVE dir_leave -!insertmacro MUI_PAGE_DIRECTORY -!insertmacro MUI_PAGE_COMPONENTS -!insertmacro MUI_PAGE_INSTFILES -!insertmacro MUI_PAGE_FINISH -!insertmacro MUI_UNPAGE_CONFIRM -!insertmacro MUI_UNPAGE_INSTFILES - -# Set languages (first is default language) -!insertmacro MUI_RESERVEFILE_LANGDLL - -# Installer languages -!define MUI_LANGDLL_ALLLANGUAGES - -# Translations -!macro LANG_LOAD LANGUAGE LANGCODE LANGID LICENCEFILE - !insertmacro MUI_LANGUAGE "${LANGUAGE}" -; !verbose off - !define LANG "${LANGUAGE}" - !include "lang\${LANGCODE}.nsh" - LangString LANGUAGEID "${LANG_${LANG}}" "1031" - LicenseLangString myLicenseData ${LANGCODE} ${LICENCEFILE} -; !verbose on - !undef LANG -!macroend - -!macro LANG_STRING NAME VALUE - LangString "${NAME}" "${LANG_${LANG}}" "${VALUE}" -!macroend - -!insertmacro LANG_LOAD "English" "en" "1033" "${SOURCEDIR}\retroshare-gui\src\license\license.txt" -!insertmacro LANG_LOAD "French" "fr" "1036" "${SOURCEDIR}\retroshare-gui\src\license\license-FR.txt" -!insertmacro LANG_LOAD "German" "de" "1031" "${SOURCEDIR}\retroshare-gui\src\license\license-GER.txt" -!insertmacro LANG_LOAD "Turkish" "tr" "1055" "${SOURCEDIR}\retroshare-gui\src\license\license-TR.txt" -!insertmacro LANG_LOAD "SimpChinese" "zh_CN" "2052" "${SOURCEDIR}\retroshare-gui\src\license\license.txt" -!insertmacro LANG_LOAD "Polish" "pl" "1045" "${SOURCEDIR}\retroshare-gui\src\license\license.txt" -!insertmacro LANG_LOAD "Spanish" "es" "1034" "${SOURCEDIR}\retroshare-gui\src\license\license.txt" -!insertmacro LANG_LOAD "Russian" "ru" "1049" "${SOURCEDIR}\retroshare-gui\src\license\license.txt" -!insertmacro LANG_LOAD "Catalan" "ca_ES" "1027" "${SOURCEDIR}\retroshare-gui\src\license\license.txt" - -LicenseData $(myLicenseData) - -# Main binaries -Section $(Section_Main) Section_Main - ;Set Section required - SectionIn RO - - ; Set Section properties - SetOverwrite on - - ; Clears previous error logs -; Delete "$INSTDIR\*.log" - - ; Main binaries - SetOutPath "$INSTDIR" - File /oname=retroshare.exe "${RELEASEDIR}\retroshare-gui\src\release\retroshare.exe" - File /oname=retroshare-nogui.exe "${RELEASEDIR}\retroshare-nogui\src\release\retroshare-nogui.exe" - - ; Qt binaries - File "${QTDIR}\bin\QtCore4.dll" - File "${QTDIR}\bin\QtGui4.dll" - File "${QTDIR}\bin\QtMultimedia4.dll" - File "${QTDIR}\bin\QtNetwork4.dll" - File "${QTDIR}\bin\QtSvg4.dll" - File "${QTDIR}\bin\QtXml4.dll" - - ; MinGW binaries - File "${MINGWDIR}\bin\libstdc++-6.dll" - File "${MINGWDIR}\bin\libgcc_s_dw2-1.dll" - File "${MINGWDIR}\bin\libwinpthread-1.dll" - - ; External binaries - File "${EXTERNAL_LIB_DIR}\bin\miniupnpc.dll" - File "${EXTERNAL_LIB_DIR}\bin\libeay32.dll" - File "${EXTERNAL_LIB_DIR}\bin\ssleay32.dll" - - ; Other files - File "${SOURCEDIR}\retroshare-gui\src\changelog.txt" - File "${SOURCEDIR}\libbitdht\src\bitdht\bdboot.txt" - - ; Image formats - SetOutPath "$INSTDIR\imageformats" - File /r "${QTDIR}\plugins\imageformats\qgif4.dll" - File /r "${QTDIR}\plugins\imageformats\qico4.dll" - File /r "${QTDIR}\plugins\imageformats\qjpeg4.dll" - File /r "${QTDIR}\plugins\imageformats\qmng4.dll" - File /r "${QTDIR}\plugins\imageformats\qsvg4.dll" - File /r "${QTDIR}\plugins\imageformats\qtga4.dll" - File /r "${QTDIR}\plugins\imageformats\qtiff4.dll" - - ; Sounds - SetOutPath "$INSTDIR\sounds" - File /r "${SOURCEDIR}\retroshare-gui\src\sounds\*.*" - - ; Translations - SetOutPath "$INSTDIR\translations" - File /r "${SOURCEDIR}\retroshare-gui\src\translations\*.qm" - File /r "${QTDIR}\translations\qt_*.qm" - - ; WebUI - SetOutPath "$INSTDIR\webui" - File /r "${SOURCEDIR}\libresapi\src\webui\*.*" - - ; License - SetOutPath "$INSTDIR\license" - File /r "${SOURCEDIR}\retroshare-gui\src\license\*.*" -SectionEnd - -# Plugins -${!defineifexist} PLUGIN_FEEDREADER_EXISTS "${RELEASEDIR}\plugins\FeedReader\release\FeedReader.dll" -${!defineifexist} PLUGIN_VOIP_EXISTS "${RELEASEDIR}\plugins\VOIP\release\VOIP.dll" - -!ifdef PLUGIN_FEEDREADER_EXISTS -!define /ifndef PLUGIN_EXISTS -!endif -!ifdef PLUGIN_VOIP_EXISTS -!define /ifndef PLUGIN_EXISTS -!endif - -!ifdef PLUGIN_EXISTS - SectionGroup $(Section_Plugins) Section_Plugins - !ifdef PLUGIN_FEEDREADER_EXISTS - Section $(Section_Plugin_FeedReader) Section_Plugin_FeedReader - SetOutPath "$DataDir\extensions6" - File "${RELEASEDIR}\plugins\FeedReader\release\FeedReader.dll" - SectionEnd - !endif - - !ifdef PLUGIN_VOIP_EXISTS - Section $(Section_Plugin_VOIP) Section_Plugin_VOIP - SetOutPath "$DataDir\extensions6" - File "${RELEASEDIR}\plugins\VOIP\release\VOIP.dll" - SectionEnd - !endif - SectionGroupEnd -!endif - -# Data (Styles) -Section $(Section_Data) Section_Data - ; Set Section properties - SetOverwrite on - - ; Chat style - SetOutPath "$StyleSheetDir\stylesheets\Bubble" - File /r "${SOURCEDIR}\retroshare-gui\src\gui\qss\chat\Bubble\*.*" - SetOutPath "$StyleSheetDir\stylesheets\Bubble_Compact" - File /r "${SOURCEDIR}\retroshare-gui\src\gui\qss\chat\Bubble_Compact\*.*" - - ; Stylesheets - SetOutPath "$INSTDIR\qss" - File /r "${SOURCEDIR}\retroshare-gui\src\qss\*.*" -SectionEnd - -;Section $(Section_Link) Section_Link - ; Delete any existing keys - - ; Write the file association -; WriteRegStr HKCR .pqi "" retroshare -; WriteRegStr HKCR retroshare "" "PQI File" -; WriteRegBin HKCR retroshare EditFlags 00000100 -; WriteRegStr HKCR "retroshare\shell" "" open -; WriteRegStr HKCR "retroshare\shell\open\command" "" `"$INSTDIR\retroshare.exe" "%1"` -;SectionEnd - -# Shortcuts -SectionGroup $(Section_Shortcuts) Section_Shortcuts -Section $(Section_StartMenu) Section_StartMenu - SetOutPath "$INSTDIR" - CreateDirectory "$SMPROGRAMS\${APPNAME}" - CreateShortCut "$SMPROGRAMS\${APPNAME}\$(Link_Uninstall).lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0 - CreateShortCut "$SMPROGRAMS\${APPNAME}\${APPNAME}.lnk" "$INSTDIR\retroshare.exe" "" "$INSTDIR\retroshare.exe" 0 -SectionEnd - -Section $(Section_Desktop) Section_Desktop - CreateShortCut "$DESKTOP\${APPNAME}.lnk" "$INSTDIR\retroshare.exe" "" "$INSTDIR\retroshare.exe" 0 -SectionEnd - -Section $(Section_QuickLaunch) Section_QuickLaunch - CreateShortCut "$QUICKLAUNCH\${APPNAME}.lnk" "$INSTDIR\retroshare.exe" "" "$INSTDIR\retroshare.exe" 0 -SectionEnd -SectionGroupEnd - -Section $(Section_AutoStart) Section_AutoStart - WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "RetroShare" "$INSTDIR\retroshare.exe -m" -SectionEnd - -;Section $(Section_AutoStart) Section_AutoStart -; CreateShortCut "$SMSTARTUP\${APPNAME}.lnk" "$INSTDIR\retroshare.exe" "" "$INSTDIR\retroshare.exe -m" 0 -;SectionEnd - -Section -FinishSection - ${If} $PortableMode = 0 - WriteRegStr HKLM "Software\${APPNAME}" "" "$INSTDIR" - WriteRegStr HKLM "Software\${APPNAME}" "Version" "${VERSION}" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayName" "${APPNAME}" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayVersion" "${VERSION}" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayIcon" "$INSTDIR\retroshare.exe" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "Publisher" "${PUBLISHER}" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "NoModify" "1" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "NoRepair" "1" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "UninstallString" "$INSTDIR\uninstall.exe" - WriteUninstaller "$INSTDIR\uninstall.exe" - ${Else} - ; Create the file the application uses to detect portable mode - FileOpen $0 "$INSTDIR\portable" w - FileClose $0 - ${EndIf} -SectionEnd - -# Descriptions -!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN - !insertmacro MUI_DESCRIPTION_TEXT ${Section_Main} $(Section_Main_Desc) - !insertmacro MUI_DESCRIPTION_TEXT ${Section_Data} $(Section_Data_Desc) - !insertmacro MUI_DESCRIPTION_TEXT ${Section_Shortcuts} $(Section_Shortcuts_Desc) - !insertmacro MUI_DESCRIPTION_TEXT ${Section_StartMenu} $(Section_StartMenu_Desc) - !insertmacro MUI_DESCRIPTION_TEXT ${Section_Desktop} $(Section_Desktop_Desc) - !insertmacro MUI_DESCRIPTION_TEXT ${Section_QuickLaunch} $(Section_QuickLaunch_Desc) - !insertmacro MUI_DESCRIPTION_TEXT ${Section_Plugins} $(Section_Plugins_Desc) - !insertmacro MUI_DESCRIPTION_TEXT ${Section_Plugin_FeedReader} $(Section_Plugin_FeedReader_Desc) - !insertmacro MUI_DESCRIPTION_TEXT ${Section_Plugin_VOIP} $(Section_Plugin_VOIP_Desc) -; !insertmacro MUI_DESCRIPTION_TEXT ${Section_Link} $(Section_Link_Desc) - !insertmacro MUI_DESCRIPTION_TEXT ${Section_AutoStart} $(Section_AutoStart_Desc) -!insertmacro MUI_FUNCTION_DESCRIPTION_END - -# Uninstall -Section "Uninstall" - ; Remove file association registry keys -; DeleteRegKey HKCR .pqi - DeleteRegKey HKCR RetroShare - - ; Remove program/uninstall regsitry keys - DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" - DeleteRegKey HKLM SOFTWARE\${APPNAME} - - DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "RetroShare" - - ; Remove shortcuts, if any - Delete "$SMPROGRAMS\${APPNAME}\*.*" - - ; Remove desktop shortcut - Delete "$DESKTOP\${APPNAME}.lnk" - - ; Remove Quicklaunch shortcut - Delete "$QUICKLAUNCH\${APPNAME}.lnk" - - ; Remove Autstart - Delete "$SMSTARTUP\${APPNAME}.lnk" - - ; Remove directories used - RMDir "$SMPROGRAMS\${APPNAME}" - RMDir /r "$INSTDIR" - - ; Don't remove the directory, otherwise - ; we lose the XPGP keys. - ; Should make this an option though... - RMDir /r "${DATADIR_NORMAL}\extensions6" - RMDir /r "${DATADIR_NORMAL}\stylesheets" -SectionEnd - -Function .onInit - StrCpy $InstDirNormal "${INSTDIR_NORMAL}" - StrCpy $InstDirPortable "${INSTDIR_PORTABLE}" - - StrCpy $PortableMode 0 - StrCpy $INSTDIR "$InstDirNormal" - StrCpy $DataDir "${DATADIR_NORMAL}" - - InitPluginsDir - Push $R1 - File /oname=$PLUGINSDIR\spltmp.bmp "${SOURCEDIR}\retroshare-gui\src\gui\images\logo\logo_splash.png" - advsplash::show 1200 1000 1000 -1 $PLUGINSDIR\spltmp - Pop $R1 - Pop $R1 - !insertmacro MUI_LANGDLL_DISPLAY -FunctionEnd - -# Installation mode - -Function RequireAdmin - UserInfo::GetAccountType - Pop $8 - ${If} $8 != "admin" - MessageBox MB_ICONSTOP "You need administrator rights to install ${APPNAME}" - SetErrorLevel 740 ;ERROR_ELEVATION_REQUIRED - Abort - ${EndIf} -FunctionEnd - -Function SetModeDestinationFromInstdir - ${If} $PortableMode = 0 - StrCpy $InstDirNormal $INSTDIR - ${Else} - StrCpy $InstDirPortable $INSTDIR - ${EndIf} -FunctionEnd - -Function PortableModePageCreate - Call SetModeDestinationFromInstdir ; If the user clicks BACK on the directory page we will remember their mode specific directory - !insertmacro MUI_HEADER_TEXT $(Page_InstallMode) $(Page_InstallMode_Desc) - nsDialogs::Create 1018 - Pop $0 - ${NSD_CreateRadioButton} 5u 25u -10u 8u $(Page_InstallMode_Standard) - Pop $1 - ${NSD_CreateLabel} 18u 40u -10u 24u $(Page_InstallMode_Standard_Desc) - Pop $0 - ${NSD_CreateRadioButton} 5u 75u -10u 8u $(Page_InstallMode_Portable) - Pop $2 - ${NSD_CreateLabel} 18u 90u -10u 24u $(Page_InstallMode_Portable_Desc) - Pop $0 - ${If} $PortableMode = 0 - SendMessage $1 ${BM_SETCHECK} ${BST_CHECKED} 0 - ${Else} - SendMessage $2 ${BM_SETCHECK} ${BST_CHECKED} 0 - ${EndIf} - nsDialogs::Show -FunctionEnd - -Function PortableModePageLeave - ${NSD_GetState} $1 $0 - ${If} $0 <> ${BST_UNCHECKED} - StrCpy $PortableMode 0 - StrCpy $INSTDIR $InstDirNormal - Call RequireAdmin - ; Enable sections - SectionSetText ${Section_Shortcuts} $(Section_Shortcuts) - SectionSetText ${Section_StartMenu} $(Section_StartMenu) - SectionSetText ${Section_Desktop} $(Section_Desktop) - SectionSetText ${Section_QuickLaunch} $(Section_QuickLaunch) - SectionSetText ${Section_AutoStart} $(Section_AutoStart) - !insertmacro SelectSection ${Section_Shortcuts} - !insertmacro SelectSection ${Section_AutoStart} - !insertmacro SelectSection ${Section_StartMenu} - !insertmacro SelectSection ${Section_Desktop} - !insertmacro SelectSection ${Section_QuickLaunch} - ${Else} - StrCpy $PortableMode 1 - StrCpy $INSTDIR $InstDirPortable - ; Disable sections - !insertmacro UnselectSection ${Section_Shortcuts} - !insertmacro UnselectSection ${Section_AutoStart} - !insertmacro UnselectSection ${Section_StartMenu} - !insertmacro UnselectSection ${Section_Desktop} - !insertmacro UnselectSection ${Section_QuickLaunch} - SectionSetText ${Section_Shortcuts} "" - SectionSetText ${Section_StartMenu} "" - SectionSetText ${Section_Desktop} "" - SectionSetText ${Section_QuickLaunch} "" - SectionSetText ${Section_AutoStart} "" - ${EndIf} -FunctionEnd - -Function dir_leave - ${If} $PortableMode = 0 - StrCpy $DataDir "${DATADIR_NORMAL}" - StrCpy $StyleSheetDir $DataDir - ${Else} - StrCpy $DataDir "${DATADIR_PORTABLE}" - StrCpy $StyleSheetDir $INSTDIR - ${EndIf} -FunctionEnd +; Script generated with the Venis Install Wizard & modified by defnax +; Reworked by Thunder +; Adapted to msys2 and 64 bit by anmo + +!include ifexist.nsh +!include x64.nsh + +# Needed defines +;!define REVISION "" +;!define DEPLOYDIR "" +;!define ARCHITECTURE "" + +# Optional defines +;!define OUTDIR "" +;!define INSTALLERADD "" + +# Check needed defines +!ifndef DEPLOYDIR +!error "DEPLOYDIR is not defined" +!endif +!ifndef ARCHITECTURE +!error "ARCHITECTURE is not defined" +!endif + +# Check optional defines +!ifdef OUTDIR +!define OUTDIR_ "${OUTDIR}\" +!else +!define OUTDIR "" +!define OUTDIR_ "" +!endif + +!ifndef INSTALLERADD +!define INSTALLERADD "" +!endif + +# Source directory +!define SOURCEDIR "..\..\.." + +# Get version from executable +!GetDllVersion "${DEPLOYDIR}\retroshare.exe" VERSION_ +!define VERSION ${VERSION_1}.${VERSION_2}.${VERSION_3} +;!define REVISION ${VERSION_4} + +# Check version +!ifndef REVISION +!error "REVISION is not defined" +!endif + +# Date +!define /date Date "%Y%m%d" + +# Detect tor +${!defineifexist} TOR_EXISTS "${DEPLOYDIR}\tor.exe" +!ifdef TOR_EXISTS +!define RSTYPE "-tor" +!else +!define RSTYPE "" +!endif + +# Application name and version +!define APPNAME "RetroShare" +!define APPNAMEANDVERSION "${APPNAME} ${VERSION}" +!define PUBLISHER "RetroShare Team" + +# Install path +!define INSTDIR_PORTABLE "$Desktop\${APPNAME}" + +!define DATADIR_NORMAL "$APPDATA\${APPNAME}" +!define DATADIR_PORTABLE "$INSTDIR\Data" + +# Main Install settings +Name "${APPNAMEANDVERSION}" +InstallDirRegKey HKLM "Software\${APPNAME}" "" +OutFile "${OUTDIR_}RetroShare-${VERSION}-${Date}-${REVISION}-${ARCHITECTURE}${RSTYPE}${INSTALLERADD}-setup.exe" +BrandingText "${APPNAMEANDVERSION}" +RequestExecutionlevel highest +# Use compression +SetCompressor /SOLID LZMA + +# Global variables +Var PortableMode +Var InstDirNormal +Var InstDirPortable +Var DataDir +Var StyleSheetDir + +# Modern interface settings +!include Sections.nsh +!include nsDialogs.nsh +!include "MUI.nsh" + +# Interface Settings +!define MUI_ABORTWARNING +!define MUI_HEADERIMAGE +!define MUI_HEADERIMAGE_BITMAP "${SOURCEDIR}\build_scripts\Windows-msys2\installer\HeaderImage.bmp" +;!define MUI_WELCOMEFINISHPAGE_BITMAP "...bmp" + +# MUI defines +!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\orange-install.ico" +!define MUI_FINISHPAGE_NOAUTOCLOSE +!define MUI_LICENSEPAGE_RADIOBUTTONS +!define MUI_COMPONENTSPAGE_SMALLDESC +!define MUI_FINISHPAGE_LINK "Visit the RetroShare forum for the latest news and support" +!define MUI_FINISHPAGE_LINK_LOCATION "http://retroshare.sourceforge.net/forum/" +!define MUI_FINISHPAGE_RUN "$INSTDIR\retroshare.exe" +!define MUI_FINISHPAGE_SHOWREADME $INSTDIR\changelog.txt +!define MUI_FINISHPAGE_SHOWREADME_TEXT changelog.txt +!define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED +!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\orange-uninstall.ico" +!define MUI_UNFINISHPAGE_NOAUTOCLOSE +;!define MUI_LANGDLL_REGISTRY_ROOT HKLM +;!define MUI_LANGDLL_REGISTRY_KEY ${REGKEY} +;!define MUI_LANGDLL_REGISTRY_VALUENAME InstallerLanguage + +# Defines the un-/installer logo of RetroShare +!insertmacro MUI_DEFAULT MUI_WELCOMEFINISHPAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Wizard\orange.bmp" +!insertmacro MUI_DEFAULT MUI_UNWELCOMEFINISHPAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Wizard\orange-uninstall.bmp" + +# Installer pages +!insertmacro MUI_PAGE_WELCOME +!insertmacro MUI_PAGE_LICENSE "$(myLicenseData)" +Page Custom PortableModePageCreate PortableModePageLeave +!define MUI_PAGE_CUSTOMFUNCTION_LEAVE dir_leave +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_COMPONENTS +!insertmacro MUI_PAGE_INSTFILES +!insertmacro MUI_PAGE_FINISH +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES + +# Set languages (first is default language) +!insertmacro MUI_RESERVEFILE_LANGDLL + +# Installer languages +!define MUI_LANGDLL_ALLLANGUAGES + +# Translations +!macro LANG_LOAD LANGUAGE LANGCODE LANGID LICENCEFILE + !insertmacro MUI_LANGUAGE "${LANGUAGE}" +; !verbose off + !define LANG "${LANGUAGE}" + !include "lang\${LANGCODE}.nsh" + LangString LANGUAGEID "${LANG_${LANG}}" "1031" + LicenseLangString myLicenseData ${LANGCODE} ${LICENCEFILE} +; !verbose on + !undef LANG +!macroend + +!macro LANG_STRING NAME VALUE + LangString "${NAME}" "${LANG_${LANG}}" "${VALUE}" +!macroend + +!insertmacro LANG_LOAD "English" "en" "1033" "${SOURCEDIR}\retroshare-gui\src\license\license.txt" +!insertmacro LANG_LOAD "French" "fr" "1036" "${SOURCEDIR}\retroshare-gui\src\license\license-FR.txt" +!insertmacro LANG_LOAD "German" "de" "1031" "${SOURCEDIR}\retroshare-gui\src\license\license-GER.txt" +!insertmacro LANG_LOAD "Turkish" "tr" "1055" "${SOURCEDIR}\retroshare-gui\src\license\license-TR.txt" +!insertmacro LANG_LOAD "SimpChinese" "zh_CN" "2052" "${SOURCEDIR}\retroshare-gui\src\license\license.txt" +!insertmacro LANG_LOAD "Polish" "pl" "1045" "${SOURCEDIR}\retroshare-gui\src\license\license.txt" +!insertmacro LANG_LOAD "Spanish" "es" "1034" "${SOURCEDIR}\retroshare-gui\src\license\license.txt" +!insertmacro LANG_LOAD "Russian" "ru" "1049" "${SOURCEDIR}\retroshare-gui\src\license\license.txt" +!insertmacro LANG_LOAD "Catalan" "ca_ES" "1027" "${SOURCEDIR}\retroshare-gui\src\license\license.txt" + +LicenseData $(myLicenseData) + +# Main binaries +Section $(Section_Main) Section_Main + ;Set Section required + SectionIn RO + + ; Set Section properties + SetOverwrite on + + ; Clears previous error logs +; Delete "$INSTDIR\*.log" + + SetOutPath "$INSTDIR" + File /r /x Data /x stylesheets /x qss /x portable "${DEPLOYDIR}\*.*" +SectionEnd + +# Plugins +${!defineifexist} PLUGIN_FEEDREADER_EXISTS "${DEPLOYDIR}\Data\extensions6\FeedReader.dll" +${!defineifexist} PLUGIN_VOIP_EXISTS "${DEPLOYDIR}\Data\extensions6\VOIP.dll" + +!ifdef PLUGIN_FEEDREADER_EXISTS +!define /ifndef PLUGIN_EXISTS +!endif +!ifdef PLUGIN_VOIP_EXISTS +!define /ifndef PLUGIN_EXISTS +!endif + +!ifdef PLUGIN_EXISTS + SectionGroup $(Section_Plugins) Section_Plugins + !ifdef PLUGIN_FEEDREADER_EXISTS + Section $(Section_Plugin_FeedReader) Section_Plugin_FeedReader + SetOutPath "$DataDir\extensions6" + File "${DEPLOYDIR}\Data\extensions6\FeedReader.dll" + SectionEnd + !endif + + !ifdef PLUGIN_VOIP_EXISTS + Section $(Section_Plugin_VOIP) Section_Plugin_VOIP + SetOutPath "$DataDir\extensions6" + File "${DEPLOYDIR}\Data\extensions6\VOIP.dll" + SectionEnd + !endif + SectionGroupEnd +!endif + +# Data (Styles) +Section $(Section_Data) Section_Data + ; Set Section properties + SetOverwrite on + + ; Chat style + SetOutPath "$StyleSheetDir\stylesheets" + File /r "${DEPLOYDIR}\stylesheets\*.*" + + ; Stylesheets + SetOutPath "$INSTDIR\qss" + File /r "${DEPLOYDIR}\qss\*.*" +SectionEnd + +;Section $(Section_Link) Section_Link + ; Delete any existing keys + + ; Write the file association +; WriteRegStr HKCR .pqi "" retroshare +; WriteRegStr HKCR retroshare "" "PQI File" +; WriteRegBin HKCR retroshare EditFlags 00000100 +; WriteRegStr HKCR "retroshare\shell" "" open +; WriteRegStr HKCR "retroshare\shell\open\command" "" `"$INSTDIR\retroshare.exe" "%1"` +;SectionEnd + +# Shortcuts +SectionGroup $(Section_Shortcuts) Section_Shortcuts +Section $(Section_StartMenu) Section_StartMenu + SetOutPath "$INSTDIR" + CreateDirectory "$SMPROGRAMS\${APPNAME}" + CreateShortCut "$SMPROGRAMS\${APPNAME}\$(Link_Uninstall).lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0 + CreateShortCut "$SMPROGRAMS\${APPNAME}\${APPNAME}.lnk" "$INSTDIR\retroshare.exe" "" "$INSTDIR\retroshare.exe" 0 +SectionEnd + +Section $(Section_Desktop) Section_Desktop + CreateShortCut "$DESKTOP\${APPNAME}.lnk" "$INSTDIR\retroshare.exe" "" "$INSTDIR\retroshare.exe" 0 +SectionEnd + +Section $(Section_QuickLaunch) Section_QuickLaunch + CreateShortCut "$QUICKLAUNCH\${APPNAME}.lnk" "$INSTDIR\retroshare.exe" "" "$INSTDIR\retroshare.exe" 0 +SectionEnd +SectionGroupEnd + +Section $(Section_AutoStart) Section_AutoStart + WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "RetroShare" "$INSTDIR\retroshare.exe -m" +SectionEnd + +;Section $(Section_AutoStart) Section_AutoStart +; CreateShortCut "$SMSTARTUP\${APPNAME}.lnk" "$INSTDIR\retroshare.exe" "" "$INSTDIR\retroshare.exe -m" 0 +;SectionEnd + +Section -FinishSection + ${If} $PortableMode = 0 + WriteRegStr HKLM "Software\${APPNAME}" "" "$INSTDIR" + WriteRegStr HKLM "Software\${APPNAME}" "Version" "${VERSION}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayName" "${APPNAME}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayVersion" "${VERSION}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayIcon" "$INSTDIR\retroshare.exe" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "Publisher" "${PUBLISHER}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "NoModify" "1" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "NoRepair" "1" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "UninstallString" "$INSTDIR\uninstall.exe" + WriteUninstaller "$INSTDIR\uninstall.exe" + ${Else} + ; Create the file the application uses to detect portable mode + FileOpen $0 "$INSTDIR\portable" w + FileClose $0 + ${EndIf} +SectionEnd + +# Descriptions +!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN + !insertmacro MUI_DESCRIPTION_TEXT ${Section_Main} $(Section_Main_Desc) + !insertmacro MUI_DESCRIPTION_TEXT ${Section_Data} $(Section_Data_Desc) + !insertmacro MUI_DESCRIPTION_TEXT ${Section_Shortcuts} $(Section_Shortcuts_Desc) + !insertmacro MUI_DESCRIPTION_TEXT ${Section_StartMenu} $(Section_StartMenu_Desc) + !insertmacro MUI_DESCRIPTION_TEXT ${Section_Desktop} $(Section_Desktop_Desc) + !insertmacro MUI_DESCRIPTION_TEXT ${Section_QuickLaunch} $(Section_QuickLaunch_Desc) + !insertmacro MUI_DESCRIPTION_TEXT ${Section_Plugins} $(Section_Plugins_Desc) + !insertmacro MUI_DESCRIPTION_TEXT ${Section_Plugin_FeedReader} $(Section_Plugin_FeedReader_Desc) + !insertmacro MUI_DESCRIPTION_TEXT ${Section_Plugin_VOIP} $(Section_Plugin_VOIP_Desc) +; !insertmacro MUI_DESCRIPTION_TEXT ${Section_Link} $(Section_Link_Desc) + !insertmacro MUI_DESCRIPTION_TEXT ${Section_AutoStart} $(Section_AutoStart_Desc) +!insertmacro MUI_FUNCTION_DESCRIPTION_END + +# Uninstall +Section "Uninstall" + ; Remove file association registry keys +; DeleteRegKey HKCR .pqi + DeleteRegKey HKCR RetroShare + + ; Remove program/uninstall regsitry keys + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" + DeleteRegKey HKLM SOFTWARE\${APPNAME} + + DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "RetroShare" + + ; Remove shortcuts, if any + Delete "$SMPROGRAMS\${APPNAME}\*.*" + + ; Remove desktop shortcut + Delete "$DESKTOP\${APPNAME}.lnk" + + ; Remove Quicklaunch shortcut + Delete "$QUICKLAUNCH\${APPNAME}.lnk" + + ; Remove Autstart + Delete "$SMSTARTUP\${APPNAME}.lnk" + + ; Remove directories used + RMDir "$SMPROGRAMS\${APPNAME}" + RMDir /r "$INSTDIR" + + ; Don't remove the directory, otherwise + ; we lose the XPGP keys. + ; Should make this an option though... + RMDir /r "${DATADIR_NORMAL}\extensions6" + RMDir /r "${DATADIR_NORMAL}\stylesheets" +SectionEnd + +Function .onInit +; source: https://stackoverflow.com/questions/19374453/nsis-installer-define-installer-and-system-x32-x64 + ${If} ${RunningX64} + ${If} ${ARCHITECTURE} == "x64" + StrCpy $InstDirNormal "$PROGRAMFILES64\${APPNAME}" + ${Else} + StrCpy $InstDirNormal "$PROGRAMFILES32\${APPNAME}" + ${Endif} + ${Else} + ${If} ${ARCHITECTURE} == "x64" + MessageBox MB_ICONSTOP "You cannot install the 64 bit version on a 32 bit system!" + Quit + ${Else} + StrCpy $InstDirNormal "$PROGRAMFILES\${APPNAME}" + ${Endif} + ${EndIf} + + StrCpy $InstDirPortable "${INSTDIR_PORTABLE}" + + StrCpy $PortableMode 0 + StrCpy $INSTDIR "$InstDirNormal" + StrCpy $DataDir "${DATADIR_NORMAL}" + + InitPluginsDir + Push $R1 + File /oname=$PLUGINSDIR\spltmp.bmp "${SOURCEDIR}\retroshare-gui\src\gui\images\logo\logo_splash.png" + advsplash::show 1200 1000 1000 -1 $PLUGINSDIR\spltmp + Pop $R1 + Pop $R1 + !insertmacro MUI_LANGDLL_DISPLAY +FunctionEnd + +# Installation mode + +Function RequireAdmin + UserInfo::GetAccountType + Pop $8 + ${If} $8 != "admin" + MessageBox MB_ICONSTOP "You need administrator rights to install ${APPNAME}" + SetErrorLevel 740 ;ERROR_ELEVATION_REQUIRED + Abort + ${EndIf} +FunctionEnd + +Function SetModeDestinationFromInstdir + ${If} $PortableMode = 0 + StrCpy $InstDirNormal $INSTDIR + ${Else} + StrCpy $InstDirPortable $INSTDIR + ${EndIf} +FunctionEnd + +Function PortableModePageCreate + Call SetModeDestinationFromInstdir ; If the user clicks BACK on the directory page we will remember their mode specific directory + !insertmacro MUI_HEADER_TEXT $(Page_InstallMode) $(Page_InstallMode_Desc) + nsDialogs::Create 1018 + Pop $0 + ${NSD_CreateRadioButton} 5u 25u -10u 8u $(Page_InstallMode_Standard) + Pop $1 + ${NSD_CreateLabel} 18u 40u -10u 24u $(Page_InstallMode_Standard_Desc) + Pop $0 + ${NSD_CreateRadioButton} 5u 75u -10u 8u $(Page_InstallMode_Portable) + Pop $2 + ${NSD_CreateLabel} 18u 90u -10u 24u $(Page_InstallMode_Portable_Desc) + Pop $0 + ${If} $PortableMode = 0 + SendMessage $1 ${BM_SETCHECK} ${BST_CHECKED} 0 + ${Else} + SendMessage $2 ${BM_SETCHECK} ${BST_CHECKED} 0 + ${EndIf} + nsDialogs::Show +FunctionEnd + +Function PortableModePageLeave + ${NSD_GetState} $1 $0 + ${If} $0 <> ${BST_UNCHECKED} + StrCpy $PortableMode 0 + StrCpy $INSTDIR $InstDirNormal + Call RequireAdmin + ; Enable sections + SectionSetText ${Section_Shortcuts} $(Section_Shortcuts) + SectionSetText ${Section_StartMenu} $(Section_StartMenu) + SectionSetText ${Section_Desktop} $(Section_Desktop) + SectionSetText ${Section_QuickLaunch} $(Section_QuickLaunch) + SectionSetText ${Section_AutoStart} $(Section_AutoStart) + !insertmacro SelectSection ${Section_Shortcuts} + !insertmacro SelectSection ${Section_AutoStart} + !insertmacro SelectSection ${Section_StartMenu} + !insertmacro SelectSection ${Section_Desktop} + !insertmacro SelectSection ${Section_QuickLaunch} + ${Else} + StrCpy $PortableMode 1 + StrCpy $INSTDIR $InstDirPortable + ; Disable sections + !insertmacro UnselectSection ${Section_Shortcuts} + !insertmacro UnselectSection ${Section_AutoStart} + !insertmacro UnselectSection ${Section_StartMenu} + !insertmacro UnselectSection ${Section_Desktop} + !insertmacro UnselectSection ${Section_QuickLaunch} + SectionSetText ${Section_Shortcuts} "" + SectionSetText ${Section_StartMenu} "" + SectionSetText ${Section_Desktop} "" + SectionSetText ${Section_QuickLaunch} "" + SectionSetText ${Section_AutoStart} "" + ${EndIf} +FunctionEnd + +Function dir_leave + ${If} $PortableMode = 0 + StrCpy $DataDir "${DATADIR_NORMAL}" + StrCpy $StyleSheetDir $DataDir + ${Else} + StrCpy $DataDir "${DATADIR_PORTABLE}" + StrCpy $StyleSheetDir $INSTDIR + ${EndIf} +FunctionEnd diff --git a/build_scripts/Windows-msys2/readme.md b/build_scripts/Windows-msys2/readme.md new file mode 100644 index 000000000..5582ec451 --- /dev/null +++ b/build_scripts/Windows-msys2/readme.md @@ -0,0 +1,91 @@ +# Compilation on Windows + +The preferred build method on Windows is by using MSYS2 which is a collection +of packages providing unix-like tools to build native Windows software. + +This guide contains information about how to setup your build environment in an automated way by scripts. +If you prefer to setup your environment manually, check this guide: +[WindowsMSys2_InstallGuide.md](WindowsMSys2_InstallGuide.md) + +Setting up the build environment automatically on a 32 bit OS is not possible anymore. +You can download an older 32 bit [MSYS2 installer](https://sourceforge.net/projects/msys2/files/Base/i686/msys2-base-i686-20180531.tar.xz/download) and follow the manual setup instructions. +Building 32 bit RetroShare from the 64 bit build environment is still possible. + +You have to clone this repository (with [git for windows](https://gitforwindows.org/)) to a local folder, then start it in a terminal. + + +## Automatic building + +Run the following script: + + \build_scripts\Windows-msys2\build.bat + +It will install all necessary tools to build RetrosShare, and build it with the default configuration. + +After the script is finished, you can find the resulting .7z package here: + + -msys2\deploy\ + +## Advanced building + +You can specify extra build options if you use the scripts under: + + \build_scripts\Windows-msys2\build\ + +You have to specify the build options after each command. + +Run the scripts in this order: +1. build.bat: builds RetroShare +2. pack.bat: makes a .7z archive with all the needed files to run the program +3. build-installer: makes a self extracting installer (optional) + +### Build options + +**Always delete the build artifacts folder if you enable or disable extra features after the build command: <sourcefolder>-msys2\deploy\builds** + +* Mandatory + * 32 or 64: 32 or 64 bit version + * release or debug: normally you would like to use the release option +* Extra features (optional) + * autologin: enable autologin + * plugins: build plugins + * webui: enable remoting features and pack webui files + * indexing: build with deep channel and file indexing support + * tor: include tor in the package + * "CONFIG+=..." enable other extra compile time features, you can find the almost complete list in file *<sourcefolder>\retroshare.pri* +* For fixing compile problems (optional) + * singlethread: use only 1 thread for building, slow but useful if you don't find the error message in the console + * clang: use clang compiler instead of GCC + * noupdate: skip the msys2 update step, sometimes some msys2 packages are broken, you can manually switch back to the older package, and this option will prevent updating to the broken version again + +Example: + +```batch +build.bat 64 release autologin webui "CONFIG+=rs_use_native_dialogs" "CONFIG+=rs_gui_cmark" +pack.bat 64 release autologin webui tor +build-installer.bat 64 release autologin +``` + +## Troubleshooting +* Run the command again, sometimes it works the second time, specially if it complains about *restbed* during building +* Delete the build artifacts: *<sourcefolder>-msys2\deploy\builds* +* Update msys2 manually: + 1. Open the terminal: *<sourcefolder>-msys2\msys2\msys64\msys2.exe* + 2. pacman -Sy + 3. pacman -Su + 4. Close the terminal + 5. Jump to 1. until it doesn't find more updates +* Start with a clean path environment variable, run *<sourcefolder>\build_scripts\Windows-msys2\start-clean-env.bat*, you will get a terminal with cleaned path + +### Errors during MSYS2 update +MSYS2 developers recently introduced some breaking changes. +If you get PGP related errors during updating the system from pacman, then follow [their guide](https://www.msys2.org/news/#2020-06-29-new-packagers) to resolve the problems. + +## Updating webui + +The scripts don't update the webui source code automatically once it is checked out. +You have to do it manually with your favourite git client. + +You can find the webui source code here: + + -webui diff --git a/build_scripts/Windows-msys2/start-clean-env.bat b/build_scripts/Windows-msys2/start-clean-env.bat new file mode 100644 index 000000000..1c6d46295 --- /dev/null +++ b/build_scripts/Windows-msys2/start-clean-env.bat @@ -0,0 +1,7 @@ +setlocal + +set path=C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0 +cd build\ +cmd + +endlocal diff --git a/build_scripts/Windows-msys2/tools/depends.bat b/build_scripts/Windows-msys2/tools/depends.bat index a5295ccb1..a92fd3364 100644 --- a/build_scripts/Windows-msys2/tools/depends.bat +++ b/build_scripts/Windows-msys2/tools/depends.bat @@ -1,40 +1,22 @@ :: Usage: -:: call depends.bat [list^|missing] file +:: call depends.bat file -if "%2"=="" ( - echo Usage: %~nx0 [list^|missing] File +if "%1"=="" ( + echo Usage: %~nx0 File exit /B 1 ) setlocal +pushd %~dp1 -if not exist "%EnvDependsExe%" echo depends.exe not found in %EnvToolsPath%.& exit /B 1 +%EnvMSYS2Cmd% "ntldd --recursive $0 | cut -f1 -d"=" | awk '{$1=$1};1'" %~nx1 > %~sdp0depends.tmp -set CutPath= -call "%ToolsPath%\find-in-path.bat" CutPath cut.exe -if "%CutPath%"=="" echo cut.exe not found in PATH.& exit /B 1 - -start /wait "" "%EnvDependsExe%" /c /oc:"%~dp0depends.tmp" %2 -if "%1"=="missing" ( - cut.exe --delimiter=, --fields=1,2 "%~dp0depends.tmp" >"%~dp0depends1.tmp" - for /F "tokens=1,2 delims=," %%A in (%~sdp0depends1.tmp) do ( - if "%%A"=="?" ( - echo %%~B - ) - ) -) - -if "%1"=="list" ( - cut.exe --delimiter=, --fields=2 "%~dp0depends.tmp" >"%~dp0depends1.tmp" - for /F "tokens=1 delims=," %%A in (%~sdp0depends1.tmp) do ( - if "%%A" NEQ "Module" ( - echo %%~A - ) - ) +for /F %%A in (%~sdp0depends.tmp) do ( + echo %%~A ) if exist "%~dp0depends.tmp" del /Q "%~dp0depends.tmp" -if exist "%~dp0depends1.tmp" del /Q "%~dp0depends1.tmp" +popd endlocal exit /B 0 \ No newline at end of file diff --git a/build_scripts/Windows-msys2/tools/download-file.bat b/build_scripts/Windows-msys2/tools/download-file.bat index 62720c393..dbcecdcbb 100644 --- a/build_scripts/Windows-msys2/tools/download-file.bat +++ b/build_scripts/Windows-msys2/tools/download-file.bat @@ -7,7 +7,6 @@ if "%~2"=="" ( exit /B 1 ) -::"%EnvCurlExe%" -L -k "%~1" -o "%~2" -"%EnvWgetExe%" --no-check-certificate --continue "%~1" --output-document="%~2" +powershell -NoLogo -NoProfile -Command (New-Object System.Net.WebClient).DownloadFile(\""%~1\"", \""%~2\"") exit /B %ERRORLEVEL% diff --git a/build_scripts/Windows-msys2/tools/get-rs-version.bat b/build_scripts/Windows-msys2/tools/get-rs-version.bat index 2398baf93..cc4e53ff8 100644 --- a/build_scripts/Windows-msys2/tools/get-rs-version.bat +++ b/build_scripts/Windows-msys2/tools/get-rs-version.bat @@ -27,7 +27,7 @@ set VersionMinor= set VersionMini= set VersionExtra= -for /F "tokens=1,2,3,* delims=.-" %%A in ('%EnvToolsPath%\sigcheck.exe -nobanner -n %Executable%') do ( +for /F "USEBACKQ tokens=1,2,3,* delims=.-" %%A in (`powershell -NoLogo -NoProfile -Command ^(Get-Item "%Executable%"^).VersionInfo.FileVersion`) do ( set VersionMajor=%%A set VersionMinor=%%B set VersionMini=%%C diff --git a/build_scripts/Windows-msys2/tools/webui.bat b/build_scripts/Windows-msys2/tools/webui.bat new file mode 100644 index 000000000..b7af01e53 --- /dev/null +++ b/build_scripts/Windows-msys2/tools/webui.bat @@ -0,0 +1,22 @@ +setlocal + +echo. +echo === webui +echo. +title Build webui + +if not exist "%RsWebuiPath%" ( + echo Checking out webui source into %RsWebuiPath% + if not "%ParamNoupdate%"=="1" ( + %EnvMSYS2Cmd% "pacman --noconfirm --needed -S git" + ) + git clone https://github.com/RetroShare/RSNewWebUI.git "%RsWebuiPath%" +) else ( + echo Webui source found at %RsWebuiPath% +) + +pushd "%RsWebuiPath%\webui-src\make-src" +%EnvMSYS2Cmd% "sh build.sh" +popd + +endlocal \ No newline at end of file diff --git a/build_scripts/Windows-msys2/tools/winhttpjs.bat b/build_scripts/Windows-msys2/tools/winhttpjs.bat deleted file mode 100644 index cdea152eb..000000000 --- a/build_scripts/Windows-msys2/tools/winhttpjs.bat +++ /dev/null @@ -1,584 +0,0 @@ -@if (@X) == (@Y) @end /* JScript comment - @echo off - - rem :: the first argument is the script name as it will be used for proper help message - cscript //E:JScript //nologo "%~f0" "%~nx0" %* - - exit /b %errorlevel% - -@if (@X)==(@Y) @end JScript comment */ - -// used resources - -// update 12.10.15 -// osvikvi(https://github.com/osvikvi) has nodited that the -password option is not set , so this is fixed - -//https://msdn.microsoft.com/en-us/library/windows/desktop/aa384058(v=vs.85).aspx -//https://msdn.microsoft.com/en-us/library/windows/desktop/aa384055(v=vs.85).aspx -//https://msdn.microsoft.com/en-us/library/windows/desktop/aa384059(v=vs.85).aspx - -// global variables and constants - - -// ---------------------------------- -// -- asynch requests not included -- -// ---------------------------------- - - -//todo - save responceStream instead of responceBody !! -//todo - set all winthttp options ->//https://msdn.microsoft.com/en-us/library/windows/desktop/aa384108(v=vs.85).aspx -//todo - log all options -//todo - improve help message . eventual verbose option - - -var ARGS = WScript.Arguments; -var scriptName = ARGS.Item(0); - -var url = ""; -var saveTo = ""; - -var user = 0; -var pass = 0; - -var proxy = 0; -var bypass = 0; -var proxy_user = 0; -var proxy_pass = 0; - -var certificate = 0; - -var force = true; - -var body = ""; - -//ActiveX objects -var WinHTTPObj = new ActiveXObject("WinHttp.WinHttpRequest.5.1"); -var FileSystemObj = new ActiveXObject("Scripting.FileSystemObject"); -var AdoDBObj = new ActiveXObject("ADODB.Stream"); - -// HttpRequest SetCredentials flags. -var proxy_settings = 0; - -// -HTTPREQUEST_SETCREDENTIALS_FOR_SERVER = 0; -HTTPREQUEST_SETCREDENTIALS_FOR_PROXY = 1; - -//timeouts and their default values -var RESOLVE_TIMEOUT = 0; -var CONNECT_TIMEOUT = 90000; -var SEND_TIMEOUT = 90000; -var RECEIVE_TIMEOUT = 90000; - -//HttpRequestMethod -var http_method = 'GET'; - -//header -var header_file = ""; - -//report -var reportfile = ""; - -//test-this: -var use_stream = false; - -//autologon policy -var autologon_policy = 1; //0,1,2 - - -//headers will be stored as multi-dimensional array -var headers = []; - -//user-agent -var ua = ""; - -//escape URL -var escape = false; - -function printHelp() { - WScript.Echo(scriptName + " - sends HTTP request and saves the request body as a file and/or a report of the sent request"); - WScript.Echo(scriptName + " url [-force yes|no] [-user username -password password] [-proxy proxyserver:port] [-bypass bypass_list]"); - WScript.Echo(" [-proxyuser proxy_username -proxypassword proxy_password] [-certificate certificateString]"); - WScript.Echo(" [-method GET|POST|PATCH|DELETE|HEAD|OPTIONS|CONNECT]"); - WScript.Echo(" [-saveTo file] - to print response to console use con"); - - WScript.Echo(" [-sendTimeout int(milliseconds)]"); - WScript.Echo(" [-resolveTimeout int(milliseconds)]"); - WScript.Echo(" [-connectTimeout int(milliseconds)]"); - WScript.Echo(" [-receiveTimeout int(milliseconds)]"); - - WScript.Echo(" [-autologonPolicy 1|2|3]"); - WScript.Echo(" [-proxySettings 1|2|3] (https://msdn.microsoft.com/en-us/library/windows/desktop/aa384059(v=vs.85).aspx)"); - - //header - WScript.Echo(" [-headers-file header_file]"); - //reportfile - WScript.Echo(" [-reportfile reportfile]"); - WScript.Echo(" [-ua user-agent]"); - WScript.Echo(" [-ua-file user-agent-file]"); - - WScript.Echo(" [-escape yes|no]"); - - WScript.Echo(" [-body body-string]"); - WScript.Echo(" [-body-file body-file]"); - - WScript.Echo("-force - decide to not or to overwrite if the local files exists"); - - WScript.Echo("proxyserver:port - the proxy server"); - WScript.Echo("bypass- bypass list"); - WScript.Echo("proxy_user , proxy_password - credentials for proxy server"); - WScript.Echo("user , password - credentials for the server"); - WScript.Echo("certificate - location of SSL certificate"); - WScript.Echo("method - what HTTP method will be used.Default is GET"); - WScript.Echo("saveTo - save the responce as binary file"); - WScript.Echo(" "); - WScript.Echo("Header file should contain key:value pairs.Lines starting with \"#\" will be ignored."); - WScript.Echo("value should NOT be enclosed with quotes"); - WScript.Echo(" "); - WScript.Echo("Examples:"); - - WScript.Echo(scriptName + " http://somelink.com/somefile.zip -saveTo c:\\somefile.zip -certificate \"LOCAL_MACHINE\\Personal\\My Middle-Tier Certificate\""); - WScript.Echo(scriptName + " http://somelink.com/something.html -method POST -certificate \"LOCAL_MACHINE\\Personal\\My Middle-Tier Certificate\" -header c:\\header_file -reportfile c:\\reportfile.txt"); - WScript.Echo(scriptName + "\"http://somelink\" -method POST -header hdrs.txt -reportfile reportfile2.txt -saveTo responsefile2 -ua \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36\" -body-file some.json"); - -} - -function parseArgs() { - // - if (ARGS.Length < 2) { - WScript.Echo("insufficient arguments"); - printHelp(); - WScript.Quit(43); - } - // !!! - url = ARGS.Item(1); - // !!! - if (ARGS.Length % 2 != 0) { - WScript.Echo("illegal arguments"); - printHelp(); - WScript.Quit(44); - } - - for (var i = 2; i < ARGS.Length - 1; i = i + 2) { - var arg = ARGS.Item(i).toLowerCase(); - var next = ARGS.Item(i + 1); - - - try { - switch (arg) { // the try-catch is set mainly because of the parseInts - case "-force": - if (next == "no") { - force = false; - } - break; - case "-escape": - if (next == "yes") { - escape = true; - } - break; - case "-saveto": - saveTo = next; - break; - case "-user": - case "-u": - user = next; - break; - case "-pass": - case "-password": - case "-p": - pass = next; - break; - case "-proxy": - proxy = next; - break; - case "-bypass": - bypass = next; - break; - case "-proxyuser": - case "-pu": - proxy_user = next; - break; - case "-proxypassword": - case "-pp": - proxy_pass = next; - break; - case "-ua": - ua = next; - break; - case "-ua-file": - ua = readFile(next); - break; - case "-body": - body = next; - break; - case "-usestream": - //WScript.Echo("~~"); - if (next.toLowerCase() === "yes") { - use_stream = true - }; - break; - case "-body-file": - body = readFile(next); - break; - case "-certificate": - certificate = next; - break; - case "-method": - switch (next.toLowerCase()) { - case "post": - http_method = 'POST'; - break; - case "get": - http_method = 'GET'; - break; - case "head": - http_method = 'HEAD'; - break; - case "put": - http_method = 'PUT'; - break; - case "options": - http_method = 'OPTIONS'; - break; - case "connect": - http_method = 'CONNECT'; - break; - case "patch": - http_method = 'PATCH'; - break; - case "delete": - http_method = 'DELETE'; - break; - default: - WScript.Echo("Invalid http method passed " + next); - WScript.Echo("possible values are GET,POST,DELETE,PUT,CONNECT,PATCH,HEAD,OPTIONS"); - WScript.Quit(1326); - break; - } - break; - case "-headers-file": - case "-header": - headers = readPropFile(next); - break; - case "-reportfile": - reportfile = next; - break; - //timeouts - case "-sendtimeout": - SEND_TIMEOUT = parseInt(next); - break; - case "-connecttimeout": - CONNECT_TIMEOUT = parseint(next); - break; - case "-resolvetimeout": - RESOLVE_TIMEOUT = parseInt(next); - break; - case "-receivetimeout": - RECEIVE_TIMEOUT = parseInt(next); - break; - - case "-autologonpolicy": - autologon_policy = parseInt(next); - if (autologon_policy > 2 || autologon_policy < 0) { - WScript.Echo("out of autologon policy range"); - WScript.Quit(87); - }; - break; - case "-proxysettings": - proxy_settings = parseInt(next); - if (proxy_settings > 2 || proxy_settings < 0) { - WScript.Echo("out of proxy settings range"); - WScript.Quit(87); - }; - break; - default: - WScript.Echo("Invalid command line switch: " + arg); - WScript.Quit(1405); - break; - } - } catch (err) { - WScript.Echo(err.message); - WScript.Quit(1348); - } - } -} - -stripTrailingSlash = function(path) { - while (path.substr(path.length - 1, path.length) == '\\') { - path = path.substr(0, path.length - 1); - } - return path; -} - -function existsItem(path) { - return FileSystemObj.FolderExists(path) || FileSystemObj.FileExists(path); -} - -function deleteItem(path) { - if (FileSystemObj.FileExists(path)) { - FileSystemObj.DeleteFile(path); - return true; - } else if (FileSystemObj.FolderExists(path)) { - FileSystemObj.DeleteFolder(stripTrailingSlash(path)); - return true; - } else { - return false; - } -} - -//------------------------------- -//---------------------- -//---------- -//----- -//-- -function request(url) { - - try { - - WinHTTPObj.Open(http_method, url, false); - if (proxy != 0 && bypass != 0) { - WinHTTPObj.SetProxy(proxy_settings, proxy, bypass); - } - - if (proxy != 0) { - WinHTTPObj.SetProxy(proxy_settings, proxy); - } - - if (user != 0 && pass != 0) { - WinHTTPObj.SetCredentials(user, pass, HTTPREQUEST_SETCREDENTIALS_FOR_SERVER); - } - - if (proxy_user != 0 && proxy_pass != 0) { - WinHTTPObj.SetCredentials(proxy_user, proxy_pass, HTTPREQUEST_SETCREDENTIALS_FOR_PROXY); - } - - if (certificate != 0) { - WinHTTPObj.SetClientCertificate(certificate); - } - - //set autologin policy - WinHTTPObj.SetAutoLogonPolicy(autologon_policy); - //set timeouts - WinHTTPObj.SetTimeouts(RESOLVE_TIMEOUT, CONNECT_TIMEOUT, SEND_TIMEOUT, RECEIVE_TIMEOUT); - - if (headers.length !== 0) { - WScript.Echo("Sending with headers:"); - for (var i = 0; i < headers.length; i++) { - WinHTTPObj.SetRequestHeader(headers[i][0], headers[i][1]); - WScript.Echo(headers[i][0] + ":" + headers[i][1]); - } - WScript.Echo(""); - } - - if (ua !== "") { - //user-agent option from: - //WinHttpRequestOption enumeration - // other options can be added like bellow - //https://msdn.microsoft.com/en-us/library/windows/desktop/aa384108(v=vs.85).aspx - WinHTTPObj.Option(0) = ua; - } - if (escape) { - WinHTTPObj.Option(3) = true; - } - if (trim(body) === "") { - WinHTTPObj.Send(); - } else { - WinHTTPObj.Send(body); - } - - var status = WinHTTPObj.Status - } catch (err) { - WScript.Echo(err.message); - WScript.Quit(666); - } - - //////////////////////// - // report // - //////////////////////// - - if (reportfile != "") { - - //var report_string=""; - var n = "\r\n"; - var report_string = "Status:" + n; - report_string = report_string + " " + WinHTTPObj.Status; - report_string = report_string + " " + WinHTTPObj.StatusText + n; - report_string = report_string + " " + n; - report_string = report_string + "Response:" + n; - report_string = report_string + WinHTTPObj.ResponseText + n; - report_string = report_string + " " + n; - report_string = report_string + "Headers:" + n; - report_string = report_string + WinHTTPObj.GetAllResponseHeaders() + n; - - WinHttpRequestOption_UserAgentString = 0; // Name of the user agent - WinHttpRequestOption_URL = 1; // Current URL - WinHttpRequestOption_URLCodePage = 2; // Code page - WinHttpRequestOption_EscapePercentInURL = 3; // Convert percents - // in the URL - // rest of the options can be seen and eventually added using this as reference - // https://msdn.microsoft.com/en-us/library/windows/desktop/aa384108(v=vs.85).aspx - - report_string = report_string + "URL:" + n; - report_string = report_string + WinHTTPObj.Option(WinHttpRequestOption_URL) + n; - - report_string = report_string + "URL Code Page:" + n; - report_string = report_string + WinHTTPObj.Option(WinHttpRequestOption_URLCodePage) + n; - - report_string = report_string + "User Agent:" + n; - report_string = report_string + WinHTTPObj.Option(WinHttpRequestOption_UserAgentString) + n; - - report_string = report_string + "Escapped URL:" + n; - report_string = report_string + WinHTTPObj.Option(WinHttpRequestOption_EscapePercentInURL) + n; - - prepareateFile(force, reportfile); - - WScript.Echo("Writing report to " + reportfile); - - writeFile(reportfile, report_string); - - } - - switch (status) { - case 200: - WScript.Echo("Status: 200 OK"); - break; - default: - WScript.Echo("Status: " + status); - WScript.Echo("Status was not OK. More info -> https://en.wikipedia.org/wiki/List_of_HTTP_status_codes"); - } - - //if as binary - if (saveTo.toLowerCase() === "con") { - WScript.Echo(WinHTTPObj.ResponseText); - } - if (saveTo !== "" && saveTo.toLowerCase() !== "con") { - prepareateFile(force, saveTo); - try { - - if (use_stream) { - writeBinFile(saveTo, WinHTTPObj.ResponseStream); - } else { - writeBinFile(saveTo, WinHTTPObj.ResponseBody); - } - - } catch (err) { - WScript.Echo("Failed to save the file as binary.Attempt to save it as text"); - AdoDBObj.Close(); - prepareateFile(true, saveTo); - writeFile(saveTo, WinHTTPObj.ResponseText); - } - } - WScript.Quit(status); -} - -//-- -//----- -//---------- -//---------------------- -//------------------------------- - -function prepareateFile(force, file) { - if (force && existsItem(file)) { - if (!deleteItem(file)) { - WScript.Echo("Unable to delete " + file); - WScript.Quit(8); - } - } else if (existsItem(file)) { - WScript.Echo("Item " + file + " already exist"); - WScript.Quit(9); - } -} - -function writeBinFile(fileName, data) { - AdoDBObj.Type = 1; - AdoDBObj.Open(); - AdoDBObj.Position = 0; - AdoDBObj.Write(data); - AdoDBObj.SaveToFile(fileName, 2); - AdoDBObj.Close(); -} - -function writeFile(fileName, data) { - AdoDBObj.Type = 2; - AdoDBObj.CharSet = "iso-8859-1"; - AdoDBObj.Open(); - AdoDBObj.Position = 0; - AdoDBObj.WriteText(data); - AdoDBObj.SaveToFile(fileName, 2); - AdoDBObj.Close(); -} - - -function readFile(fileName) { - //check existence - try { - if (!FileSystemObj.FileExists(fileName)) { - WScript.Echo("file " + fileName + " does not exist!"); - WScript.Quit(13); - } - if (FileSystemObj.GetFile(fileName).Size === 0) { - return ""; - } - var fileR = FileSystemObj.OpenTextFile(fileName, 1); - var content = fileR.ReadAll(); - fileR.Close(); - return content; - } catch (err) { - WScript.Echo("Error while reading file: " + fileName); - WScript.Echo(err.message); - WScript.Echo("Will return empty string"); - return ""; - } -} - -function readPropFile(fileName) { - //check existence - resultArray = []; - if (!FileSystemObj.FileExists(fileName)) { - WScript.Echo("(headers)file " + fileName + " does not exist!"); - WScript.Quit(15); - } - if (FileSystemObj.GetFile(fileName).Size === 0) { - return resultArray; - } - var fileR = FileSystemObj.OpenTextFile(fileName, 1); - var line = ""; - var k = ""; - var v = ""; - var lineN = 0; - var index = 0; - try { - WScript.Echo("parsing headers form " + fileName + " property file "); - while (!fileR.AtEndOfStream) { - line = fileR.ReadLine(); - lineN++; - index = line.indexOf(":"); - if (line.indexOf("#") === 0 || trim(line) === "") { - continue; - } - if (index === -1 || index === line.length - 1 || index === 0) { - WScript.Echo("Invalid line " + lineN); - WScript.Quit(93); - } - k = trim(line.substring(0, index)); - v = trim(line.substring(index + 1, line.length)); - resultArray.push([k, v]); - } - fileR.Close(); - return resultArray; - } catch (err) { - WScript.Echo("Error while reading headers file: " + fileName); - WScript.Echo(err.message); - WScript.Echo("Will return empty array"); - return resultArray; - } -} - -function trim(str) { - return str.replace(/^\s+/, '').replace(/\s+$/, ''); -} - -function main() { - parseArgs(); - request(url); -} -main(); diff --git a/build_scripts/Windows/build-libs/Makefile b/build_scripts/Windows/build-libs/Makefile old mode 100755 new mode 100644 index 697d4c286..c76269d1e --- a/build_scripts/Windows/build-libs/Makefile +++ b/build_scripts/Windows/build-libs/Makefile @@ -1,15 +1,15 @@ ZLIB_VERSION=1.2.3 BZIP2_VERSION=1.0.6 MINIUPNPC_VERSION=2.0 -OPENSSL_VERSION=1.0.2n +OPENSSL_VERSION=1.1.1g SPEEX_VERSION=1.2.0 SPEEXDSP_VERSION=1.2rc3 -OPENCV_VERSION=3.4.1 +OPENCV_VERSION=3.4.11 LIBXML2_VERSION=2.9.7 LIBXSLT_VERSION=1.1.32 CURL_VERSION=7.58.0 -TCL_VERSION=8.6.2 -SQLCIPHER_VERSION=2.2.1 +TCL_VERSION=8.6.10 +SQLCIPHER_VERSION=4.4.0 LIBMICROHTTPD_VERSION=0.9.59 FFMPEG_VERSION=3.4 RAPIDJSON_VERSION=1.1.0 @@ -74,7 +74,7 @@ $(BUILD_PATH)/zlib-$(ZLIB_VERSION): $(DOWNLOAD_PATH)/zlib-$(ZLIB_VERSION).tar.gz bzip2: $(BUILD_PATH)/bzip2-$(BZIP2_VERSION) $(DOWNLOAD_PATH)/bzip2-$(BZIP2_VERSION).tar.gz: - wget http://bzip.org/$(BZIP2_VERSION)/bzip2-$(BZIP2_VERSION).tar.gz -O $(DOWNLOAD_PATH)/bzip2-$(BZIP2_VERSION).tar.gz + wget https://sourceforge.net/projects/bzip2/files/bzip2-$(BZIP2_VERSION).tar.gz/download -O $(DOWNLOAD_PATH)/bzip2-$(BZIP2_VERSION).tar.gz $(BUILD_PATH)/bzip2-$(BZIP2_VERSION): $(DOWNLOAD_PATH)/bzip2-$(BZIP2_VERSION).tar.gz # prepare @@ -102,7 +102,7 @@ $(BUILD_PATH)/miniupnpc-$(MINIUPNPC_VERSION): $(DOWNLOAD_PATH)/miniupnpc-$(MINIU rm -r -f $(BUILD_PATH)/miniupnpc-* tar xvf $(DOWNLOAD_PATH)/miniupnpc-$(MINIUPNPC_VERSION).tar.gz # build - cd miniupnpc-$(MINIUPNPC_VERSION) && export CC=gcc && make -f Makefile.mingw init libminiupnpc.a miniupnpc.dll + cd miniupnpc-$(MINIUPNPC_VERSION) && export CC=gcc && export PATH=.:$$PATH && make -f Makefile.mingw init libminiupnpc.a miniupnpc.dll # copy files mkdir -p $(BUILD_PATH)/miniupnpc-$(MINIUPNPC_VERSION).tmp/include/miniupnpc cp miniupnpc-$(MINIUPNPC_VERSION)/*.h $(BUILD_PATH)/miniupnpc-$(MINIUPNPC_VERSION).tmp/include/miniupnpc/ @@ -132,8 +132,11 @@ $(BUILD_PATH)/openssl-$(OPENSSL_VERSION): $(DOWNLOAD_PATH)/openssl-$(OPENSSL_VER mkdir -p $(BUILD_PATH)/openssl-$(OPENSSL_VERSION).tmp/include/openssl cp openssl-$(OPENSSL_VERSION)/include/openssl/*.h $(BUILD_PATH)/openssl-$(OPENSSL_VERSION).tmp/include/openssl/ mkdir -p $(BUILD_PATH)/openssl-$(OPENSSL_VERSION).tmp/bin - cp openssl-$(OPENSSL_VERSION)/libeay32.dll $(BUILD_PATH)/openssl-$(OPENSSL_VERSION).tmp/bin/ - cp openssl-$(OPENSSL_VERSION)/ssleay32.dll $(BUILD_PATH)/openssl-$(OPENSSL_VERSION).tmp/bin/ + if [ $(MSYSTEM) = "MINGW32" ] ; then cp openssl-$(OPENSSL_VERSION)/libcrypto-1_1.dll $(BUILD_PATH)/openssl-$(OPENSSL_VERSION).tmp/bin/ ; fi + if [ $(MSYSTEM) = "MINGW32" ] ; then cp openssl-$(OPENSSL_VERSION)/libcrypto-1_1.dll $(BUILD_PATH)/openssl-$(OPENSSL_VERSION).tmp/bin/ ; fi + if [ $(MSYSTEM) = "MINGW32" ] ; then cp openssl-$(OPENSSL_VERSION)/libssl-1_1.dll $(BUILD_PATH)/openssl-$(OPENSSL_VERSION).tmp/bin/ ; fi + if [ $(MSYSTEM) = "MINGW64" ] ; then cp openssl-$(OPENSSL_VERSION)/libcrypto-1_1-x64.dll $(BUILD_PATH)/openssl-$(OPENSSL_VERSION).tmp/bin/ ; fi + if [ $(MSYSTEM) = "MINGW64" ] ; then cp openssl-$(OPENSSL_VERSION)/libssl-1_1-x64.dll $(BUILD_PATH)/openssl-$(OPENSSL_VERSION).tmp/bin/ ; fi mkdir -p $(BUILD_PATH)/openssl-$(OPENSSL_VERSION).tmp/lib cp openssl-$(OPENSSL_VERSION)/libcrypto.dll.a $(BUILD_PATH)/openssl-$(OPENSSL_VERSION).tmp/lib/ cp openssl-$(OPENSSL_VERSION)/libssl.dll.a $(BUILD_PATH)/openssl-$(OPENSSL_VERSION).tmp/lib/ @@ -202,7 +205,7 @@ $(BUILD_PATH)/opencv-$(OPENCV_VERSION): $(DOWNLOAD_PATH)/opencv-$(OPENCV_VERSION mkdir -p $(BUILD_PATH)/opencv-$(OPENCV_VERSION).tmp/include cp -r opencv-$(OPENCV_VERSION)/build/install/include/* $(BUILD_PATH)/opencv-$(OPENCV_VERSION).tmp/include/ mkdir -p $(BUILD_PATH)/opencv-$(OPENCV_VERSION).tmp/lib/opencv - cp -r opencv-$(OPENCV_VERSION)/build/install/x86/mingw/staticlib/* $(BUILD_PATH)/opencv-$(OPENCV_VERSION).tmp/lib/opencv/ + cp -r opencv-$(OPENCV_VERSION)/build/install/x64/mingw/staticlib/* $(BUILD_PATH)/opencv-$(OPENCV_VERSION).tmp/lib/opencv/ # cleanup rm -r -f opencv-$(OPENCV_VERSION) mv $(BUILD_PATH)/opencv-$(OPENCV_VERSION).tmp $(BUILD_PATH)/opencv-$(OPENCV_VERSION) diff --git a/build_scripts/Windows/build-libs/build-libs.bat b/build_scripts/Windows/build-libs/build-libs.bat index e0c5af09b..b0dc51277 100644 --- a/build_scripts/Windows/build-libs/build-libs.bat +++ b/build_scripts/Windows/build-libs/build-libs.bat @@ -6,7 +6,7 @@ setlocal :: Parameter -set MakeParam="DOWNLOAD_PATH=../download" +set MakeParam="DOWNLOAD_PATH=../../download" set MakeTask= :param_loop @@ -19,25 +19,29 @@ if "%~1" NEQ "" ( :: Initialize environment call "%~dp0..\env.bat" if errorlevel 1 goto error_env -call "%EnvPath%\env-msys.bat" +call "%EnvPath%\env-msys2.bat" if errorlevel 1 goto error_env :: Check MSYS environment -if not exist "%EnvMSYSSH%" %cecho% error "Please install MSYS first." & exit /B 1 +if not exist "%EnvMSYS2SH%" %cecho% error "Please install MSYS2 first." & exit /B 1 :: Initialize environment call "%~dp0env.bat" if errorlevel 1 goto error_env -:: Add tools path to PATH environment -set PATH=%EnvToolsPath%;%PATH% - -call "%ToolsPath%\msys-path.bat" "%~dp0" MSYSCurPath -call "%ToolsPath%\msys-path.bat" "%BuildLibsPath%" MSYSBuildLibsPath +call "%ToolsPath%\msys2-path.bat" "%~dp0" MSYS2CurPath +call "%ToolsPath%\msys2-path.bat" "%BuildLibsPath%" MSYS2BuildLibsPath if not exist "%BuildLibsPath%" mkdir "%BuildLibsPath%" -%EnvMSYSCmd% "cd "%MSYSBuildLibsPath%" && make -f %MSYSCurPath%/makefile %MakeParam% %MakeTask%" +set MSYSTEM=MINGW%MSYS2Base% +set MSYS2_PATH_TYPE=inherit + +%EnvMSYS2Cmd% "pacman --needed --noconfirm -S diffutils perl tar make wget mingw-w64-%MSYS2Architecture%-make" +::mingw-w64-%MSYS2Architecture%-cmake +::%EnvMSYS2Cmd% "pacman --noconfirm -Rd --nodeps mingw-w64-%MSYS2Architecture%-zlib" + +%EnvMSYS2Cmd% "cd "%MSYS2BuildLibsPath%" && make -f %MSYS2CurPath%/makefile %MakeParam% %MakeTask%" exit /B %ERRORLEVEL% diff --git a/build_scripts/Windows/build-libs/env.bat b/build_scripts/Windows/build-libs/env.bat index e340a6f88..48687e7f0 100644 --- a/build_scripts/Windows/build-libs/env.bat +++ b/build_scripts/Windows/build-libs/env.bat @@ -4,9 +4,10 @@ call "%ToolsPath%\find-in-path.bat" MinGWPath gcc.exe if "%MinGWPath%"=="" %cecho% error "Please run command in the Qt Command Prompt or add the path to MinGW bin folder to PATH variable." & exit /B 1 :: Get gcc versions -call "%ToolsPath%\get-gcc-version.bat" GCCVersion +call "%ToolsPath%\get-gcc-version.bat" GCCVersion GCCArchitecture if "%GCCVersion%"=="" %cecho% error "Cannot get gcc version." & exit /B 1 +if "%GCCArchitecture%"=="" %cecho% error "Cannot get gcc architecture." & exit /B 1 -set BuildLibsPath=%EnvRootPath%\build-libs\gcc-%GCCVersion% +set BuildLibsPath=%EnvRootPath%\build-libs\gcc-%GCCVersion%\%GCCArchitecture% exit /B 0 diff --git a/build_scripts/Windows/build-tor.bat b/build_scripts/Windows/build-tor.bat deleted file mode 100644 index a82e63f16..000000000 --- a/build_scripts/Windows/build-tor.bat +++ /dev/null @@ -1,27 +0,0 @@ -@echo off - -setlocal - -:: Initialize environment -call "%~dp0env.bat" -if errorlevel 1 goto error_env -call "%EnvPath%\env.bat" -if errorlevel 1 goto error_env - -%cecho% info "Build libraries" -call "%~dp0build-libs\build-libs.bat" -if errorlevel 1 %cecho% error "Failed to build libraries." & exit /B %ERRORLEVEL% - -%cecho% info "Build %SourceName%" -call "%~dp0build\build.bat" release tor autologin plugins -if errorlevel 1 %cecho% error "Failed to build %SourceName%." & exit /B %ERRORLEVEL% - -%cecho% info "Pack %SourceName%" -call "%~dp0build\pack.bat" release tor -if errorlevel 1 %cecho% error "Failed to pack %SourceName%." & exit /B %ERRORLEVEL% - -exit /B 0 - -:error_env -echo Failed to initialize environment. -exit /B 1 diff --git a/build_scripts/Windows/build.bat b/build_scripts/Windows/build.bat index c91c64753..89179e22c 100644 --- a/build_scripts/Windows/build.bat +++ b/build_scripts/Windows/build.bat @@ -13,7 +13,8 @@ call "%~dp0build-libs\build-libs.bat" if errorlevel 1 %cecho% error "Failed to build libraries." & exit /B %ERRORLEVEL% %cecho% info "Build %SourceName%" -call "%~dp0build\build.bat" release autologin plugins +call "%~dp0build\build.bat" release autologin jsonapi +rem plugins if errorlevel 1 %cecho% error "Failed to build %SourceName%." & exit /B %ERRORLEVEL% %cecho% info "Pack %SourceName%" diff --git a/build_scripts/Windows/build/build-installer.bat b/build_scripts/Windows/build/build-installer.bat index afa01ffe0..b34e76157 100644 --- a/build_scripts/Windows/build/build-installer.bat +++ b/build_scripts/Windows/build/build-installer.bat @@ -21,6 +21,12 @@ if not exist "%BuildLibsPath%\libs\gcc-version" %cecho% error "Cannot get gcc ve set /P LibsGCCVersion=<"%BuildLibsPath%\libs\gcc-version" if "%LibsGCCVersion%" NEQ "%GCCVersion%" %cecho% error "Please use correct version of external libraries. (gcc %GCCVersion% ^<^> libs %LibsGCCVersion%)." & exit /B 1 +:: Get date +call "%ToolsPath%\get-rs-date.bat" "%SourcePath%" RsDate +if errorlevel 1 %cecho% error "Could not get date."& goto error + +if "%RsDate%"=="" %cecho% error "Could not get date."& goto error + :: Build defines for script set NSIS_PARAM= @@ -30,6 +36,10 @@ set NSIS_PARAM=%NSIS_PARAM% /DMINGWDIR="%MinGWPath%\.." set NSIS_PARAM=%NSIS_PARAM% /DOUTDIR="%RsPackPath%" set NSIS_PARAM=%NSIS_PARAM% /DINSTALLERADD="%RsArchiveAdd%" set NSIS_PARAM=%NSIS_PARAM% /DEXTERNAL_LIB_DIR="%BuildLibsPath%\libs" +set NSIS_PARAM=%NSIS_PARAM% /DARCHITECTURE="%GCCArchitecture%" +set NSIS_PARAM=%NSIS_PARAM% /DDATE="%RsDate%" + +if exist "%EnvTorPath%\Tor\tor.exe" set NSIS_PARAM=%NSIS_PARAM% /DTORDIR="%EnvTorPath%\Tor" :: Get compiled version call "%ToolsPath%\get-rs-version.bat" "%RsBuildPath%\retroshare-gui\src\%RsBuildConfig%\retroshare.exe" RsVersion diff --git a/build_scripts/Windows/build/build.bat b/build_scripts/Windows/build/build.bat index 62eb5eca6..a7f02027f 100644 --- a/build_scripts/Windows/build/build.bat +++ b/build_scripts/Windows/build/build.bat @@ -50,8 +50,8 @@ echo. title Build - %SourceName%-%RsBuildConfig% [qmake] set RS_QMAKE_CONFIG=%RsBuildConfig% -if "%ParamVersion%"=="1" set RS_QMAKE_CONFIG=%RS_QMAKE_CONFIG% version_detail_bash_script if "%ParamAutologin%"=="1" set RS_QMAKE_CONFIG=%RS_QMAKE_CONFIG% rs_autologin +if "%ParamJsonApi%"=="1" set RS_QMAKE_CONFIG=%RS_QMAKE_CONFIG% rs_jsonapi if "%ParamPlugins%"=="1" set RS_QMAKE_CONFIG=%RS_QMAKE_CONFIG% retroshare_plugins qmake "%SourcePath%\RetroShare.pro" -r -spec win32-g++ "CONFIG+=%RS_QMAKE_CONFIG%" "EXTERNAL_LIB_DIR=%BuildLibsPath%\libs" @@ -63,11 +63,15 @@ echo. title Build - %SourceName%-%RsBuildConfig% [make] -if exist "%EnvJomExe%" ( - "%EnvJomExe%" -) else ( - mingw32-make -) +mingw32-make -j %CoreCount% +if errorlevel 1 goto error + +echo. +echo === Changelog +echo. + +title Build - %SourceName%-%RsBuildConfig% [changelog] +call "%ToolsPath%\generate-changelog.bat" "%SourcePath%" "%RsBuildPath%\changelog.txt" :error popd diff --git a/build_scripts/Windows/build/env.bat b/build_scripts/Windows/build/env.bat index f10ab2075..100fa32cd 100644 --- a/build_scripts/Windows/build/env.bat +++ b/build_scripts/Windows/build/env.bat @@ -3,7 +3,10 @@ set ParamRelease=0 set ParamDebug=0 set ParamAutologin=0 set ParamPlugins=0 +set ParamJsonApi=0 set ParamTor=0 +set NonInteractive=0 +set CoreCount=%NUMBER_OF_PROCESSORS% :parameter_loop if "%~1" NEQ "" ( @@ -14,10 +17,16 @@ if "%~1" NEQ "" ( set ParamDebug=1 ) else if "%%~a"=="autologin" ( set ParamAutologin=1 + ) else if "%%~a"=="jsonapi" ( + set ParamJsonApi=1 ) else if "%%~a"=="plugins" ( set ParamPlugins=1 ) else if "%%~a"=="tor" ( set ParamTor=1 + ) else if "%%~a"=="non-interactive" ( + set NonInteractive=1 + ) else if "%%~a"=="singlethread" ( + set CoreCount=1 ) else ( echo. echo Unknown parameter %1 @@ -67,13 +76,14 @@ call "%ToolsPath%\get-qt-version.bat" QtVersion if "%QtVersion%"=="" %cecho% error "Cannot get Qt version." & exit /B 1 :: Get gcc versions -call "%ToolsPath%\get-gcc-version.bat" GCCVersion +call "%ToolsPath%\get-gcc-version.bat" GCCVersion GCCArchitecture if "%GCCVersion%"=="" %cecho% error "Cannot get gcc version." & exit /B 1 +if "%GCCArchitecture%"=="" %cecho% error "Cannot get gcc architecture." & exit /B 1 -set BuildLibsPath=%EnvRootPath%\build-libs\gcc-%GCCVersion% +set BuildLibsPath=%EnvRootPath%\build-libs\gcc-%GCCVersion%\%GCCArchitecture% -set RsBuildPath=%BuildPath%\Qt-%QtVersion%-%RsBuildConfig% -set RsDeployPath=%DeployPath%\Qt-%QtVersion%%RsType%-%RsBuildConfig% +set RsBuildPath=%BuildPath%\Qt-%QtVersion%-%GCCArchitecture%-%RsBuildConfig% +set RsDeployPath=%DeployPath%\Qt-%QtVersion%-%GCCArchitecture%%RsType%-%RsBuildConfig% set RsPackPath=%DeployPath% set RsArchiveAdd= @@ -93,9 +103,16 @@ echo release^|debug Build release or debug version echo. echo Optional parameter (need clean when changed) echo autologin Build with autologin +echo jsonapi Build with jsonapi echo plugins Build plugins echo. +echo Optional parameter +echo singlethread Use only 1 thread for building +echo. echo Parameter for pack echo tor Pack tor version echo. +echo Parameter for git-log +echo non-interactive Non-interactive mode +echo. exit /B 2 diff --git a/build_scripts/Windows/build/git-log.bat b/build_scripts/Windows/build/git-log.bat index d711ededb..4d479fc3f 100644 --- a/build_scripts/Windows/build/git-log.bat +++ b/build_scripts/Windows/build/git-log.bat @@ -2,9 +2,6 @@ setlocal -set NoAsk= -if "%~2"=="no-ask" set NoAsk=1 - :: Initialize environment call "%~dp0..\env.bat" if errorlevel 1 goto error_env @@ -20,48 +17,29 @@ set GitPath= call "%ToolsPath%\find-in-path.bat" GitPath git.exe if "%GitPath%"=="" echo Git executable not found in PATH.& exit /B 1 -:: Get compiled revision -set GetRsVersion=%SourcePath%\build_scripts\Windows\tools\get-rs-version.bat -if not exist "%GetRsVersion%" ( - echo File not found - echo %GetRsVersion% - exit /B 1 -) - -call "%GetRsVersion%" RS_REVISION_STRING RsRevision -if "%RsRevision%"=="" echo Revision not found.& exit /B 1 - :: Get compiled version -call "%GetRsVersion%" RS_REVISION_STRING RsRevision -if "%RsRevision%"=="" echo Revision not found.& exit /B 1 +call "%ToolsPath%\get-rs-version.bat" "%RsBuildPath%\retroshare-gui\src\%RsBuildConfig%\retroshare.exe" RsVersion +if errorlevel 1 %cecho% error "Version not found."& goto error -call "%GetRsVersion%" RS_MAJOR_VERSION RsMajorVersion -if "%RsMajorVersion%"=="" echo Major version not found.& exit /B 1 +if "%RsVersion.Major%"=="" %cecho% error "Major version not found."& goto error +if "%RsVersion.Minor%"=="" %cecho% error "Minor version not found."& goto error +if "%RsVersion.Mini%"=="" %cecho% error "Mini number not found".& goto error +if "%RsVersion.Extra%"=="" %cecho% error "Extra number not found".& goto error -call "%GetRsVersion%" RS_MINOR_VERSION RsMinorVersion -if "%RsMinorVersion%"=="" echo Minor version not found.& exit /B 1 +set RsVersion=%RsVersion.Major%.%RsVersion.Minor%.%RsVersion.Mini% -call "%GetRsVersion%" RS_BUILD_NUMBER RsBuildNumber -if "%RsBuildNumber%"=="" echo Build number not found.& exit /B 1 +:: Get date +call "%ToolsPath%\get-rs-date.bat" "%SourcePath%" RsDate +if errorlevel 1 %cecho% error "Could not get date."& goto error -call "%GetRsVersion%" RS_BUILD_NUMBER_ADD RsBuildNumberAdd - -set RsVersion=%RsMajorVersion%.%RsMinorVersion%.%RsBuildNumber%%RsBuildNumberAdd% - -:: Check WMIC is available -wmic.exe alias /? >nul 2>&1 || echo WMIC is not available.&& exit /B 1 - -:: Use WMIC to retrieve date in format YYYYMMDD -set RsDate= -for /f "tokens=2 delims==" %%I in ('wmic os get localdatetime /format:list') do set RsDate=%%I -set RsDate=%RsDate:~0,4%%RsDate:~4,2%%RsDate:~6,2% +if "%RsDate%"=="" %cecho% error "Could not get date."& goto error :: Get last revision -set RsLastRefFile=%BuildPath%\Qt-%QtVersion%%RsType%-%RsBuildConfig%-LastRef.txt +set RsLastRefFile=%BuildPath%\Qt-%QtVersion%-%GCCArchitecture%%RsType%-%RsBuildConfig%-LastRef.txt set RsLastRef= if exist "%RsLastRefFile%" set /P RsLastRef=<"%RsLastRefFile%" -if "%NoAsk%"=="1" goto no_ask_for_last_revision +if "%NonInteractive%"=="1" goto no_ask_for_last_revision if not "%RsLastRef%"=="" echo Last Revision was %RsLastRef% set /P RsLastRefInput=Last Revision: if "%RsLastRefInput%" NEQ "" set RsLastRef=%RsLastRefInput% @@ -79,15 +57,15 @@ echo. echo Creating log from %RsLastRef% echo to %RsRef% -if "%NoAsk%"=="1" goto no_confirm +if "%NonInteractive%"=="1" goto no_confirm choice /M "Do you want to proceed?" if %errorlevel%==2 exit /B 1 :no_confirm if "%RsBuildConfig%" NEQ "release" ( - set RsGitLog=%DeployPath%\RetroShare-%RsVersion%-Windows-Portable-%RsDate%-%RsRevision%-Qt-%QtVersion%%RsType%%RsArchiveAdd%-%RsBuildConfig%.txt + set RsGitLog=%DeployPath%\RetroShare-%RsVersion%-Windows-Portable-%RsDate%-%RsVersion.Extra%-Qt-%QtVersion%-%GCCArchitecture%%RsType%%RsArchiveAdd%-%RsBuildConfig%.txt ) else ( - set RsGitLog=%DeployPath%\RetroShare-%RsVersion%-Windows-Portable-%RsDate%-%RsRevision%-Qt-%QtVersion%%RsType%%RsArchiveAdd%.txt + set RsGitLog=%DeployPath%\RetroShare-%RsVersion%-Windows-Portable-%RsDate%-%RsVersion.Extra%-Qt-%QtVersion%-%GCCArchitecture%%RsType%%RsArchiveAdd%.txt ) title %SourceName%-%RsBuildConfig% [git log] diff --git a/build_scripts/Windows/build/pack.bat b/build_scripts/Windows/build/pack.bat index d97f3ea22..d48d93a65 100644 --- a/build_scripts/Windows/build/pack.bat +++ b/build_scripts/Windows/build/pack.bat @@ -29,16 +29,8 @@ if exist "%RsDeployPath%" rmdir /S /Q "%RsDeployPath%" :: Check compilation if not exist "%RsBuildPath%\Makefile" echo Project is not compiled.& goto error -:: Get compiled revision -set GetRsVersion=%SourcePath%\build_scripts\Windows\tools\get-rs-version.bat -if not exist "%GetRsVersion%" ( - %cecho% error "File not found" - echo %GetRsVersion% - goto error -) - :: Get compiled version -call "%GetRsVersion%" "%RsBuildPath%\retroshare-gui\src\%RsBuildConfig%\retroshare.exe" RsVersion +call "%ToolsPath%\get-rs-version.bat" "%RsBuildPath%\retroshare-gui\src\%RsBuildConfig%\retroshare.exe" RsVersion if errorlevel 1 %cecho% error "Version not found."& goto error if "%RsVersion.Major%"=="" %cecho% error "Major version not found."& goto error @@ -48,18 +40,17 @@ if "%RsVersion.Extra%"=="" %cecho% error "Extra number not found".& goto error set RsVersion=%RsVersion.Major%.%RsVersion.Minor%.%RsVersion.Mini% -:: Check WMIC is available -wmic.exe alias /? >nul 2>&1 || echo WMIC is not available.&& goto error +:: Get date +call "%ToolsPath%\get-rs-date.bat" "%SourcePath%" RsDate +if errorlevel 1 %cecho% error "Could not get date."& goto error -:: Use WMIC to retrieve date in format YYYYMMDD -set RsDate= -for /f "tokens=2 delims==" %%I in ('wmic os get localdatetime /format:list') do set RsDate=%%I -set RsDate=%RsDate:~0,4%%RsDate:~4,2%%RsDate:~6,2% +if "%RsDate%"=="" %cecho% error "Could not get date."& goto error +rem Tor if "%ParamTor%"=="1" ( :: Check for tor executable - if not exist "%EnvDownloadPath%\tor\Tor\tor.exe" ( - %cecho% error "Tor binary not found. Please download Tor Expert Bundle from\nhttps://www.torproject.org/download/download.html.en\nand unpack to\n%EnvDownloadPath:\=\\%\\tor" + if not exist "%EnvTorPath%\Tor\tor.exe" ( + %cecho% error "Tor binary not found. Please download Tor Expert Bundle from\nhttps://www.torproject.org/download/download.html.en\nand unpack to\n%EnvTorPath:\=\\%" goto error ) ) @@ -74,9 +65,9 @@ if "%QtMainVersion%"=="4" set QtMainVersion2=4 if "%QtMainVersion%"=="5" set QtMainVersion1=5 if "%RsBuildConfig%" NEQ "release" ( - set Archive=%RsPackPath%\RetroShare-%RsVersion%-Windows-Portable-%RsDate%-%RsVersion.Extra%-Qt-%QtVersion%%RsType%%RsArchiveAdd%-%RsBuildConfig%.7z + set Archive=%RsPackPath%\RetroShare-%RsVersion%-Windows-Portable-%RsDate%-%RsVersion.Extra%-Qt-%QtVersion%-%GCCArchitecture%%RsType%%RsArchiveAdd%-%RsBuildConfig%.7z ) else ( - set Archive=%RsPackPath%\RetroShare-%RsVersion%-Windows-Portable-%RsDate%-%RsVersion.Extra%-Qt-%QtVersion%%RsType%%RsArchiveAdd%.7z + set Archive=%RsPackPath%\RetroShare-%RsVersion%-Windows-Portable-%RsDate%-%RsVersion.Extra%-Qt-%QtVersion%-%GCCArchitecture%%RsType%%RsArchiveAdd%.7z ) if exist "%Archive%" del /Q "%Archive%" @@ -98,12 +89,13 @@ mkdir "%RsDeployPath%\qss" mkdir "%RsDeployPath%\stylesheets" mkdir "%RsDeployPath%\sounds" mkdir "%RsDeployPath%\translations" +mkdir "%RsDeployPath%\license" copy nul "%RsDeployPath%\portable" %Quite% echo copy binaries -copy "%RsBuildPath%\retroshare-gui\src\%RsBuildConfig%\RetroShare*.exe" "%RsDeployPath%" %Quite% -copy "%RsBuildPath%\retroshare-nogui\src\%RsBuildConfig%\retroshare*-nogui.exe" "%RsDeployPath%" %Quite% +copy "%RsBuildPath%\retroshare-gui\src\%RsBuildConfig%\retroshare*.exe" "%RsDeployPath%" %Quite% +copy "%RsBuildPath%\retroshare-service\src\%RsBuildConfig%\retroshare*-service.exe" "%RsDeployPath%" %Quite% echo copy extensions for /D %%D in ("%RsBuildPath%\plugins\*") do ( @@ -148,13 +140,15 @@ rmdir /S /Q "%RsDeployPath%\stylesheets\__MACOSX__Bubble" %Quite% echo copy sounds xcopy /S "%SourcePath%\retroshare-gui\src\sounds" "%RsDeployPath%\sounds" %Quite% +echo copy license +xcopy /S "%SourcePath%\retroshare-gui\src\license" "%RsDeployPath%\license" %Quite% + echo copy translation copy "%SourcePath%\retroshare-gui\src\translations\qt_tr.qm" "%RsDeployPath%\translations" %Quite% copy "%QtPath%\..\translations\qt_*.qm" "%RsDeployPath%\translations" %Quite% if "%QtMainVersion%"=="5" ( copy "%QtPath%\..\translations\qtbase_*.qm" "%RsDeployPath%\translations" %Quite% copy "%QtPath%\..\translations\qtscript_*.qm" "%RsDeployPath%\translations" %Quite% - copy "%QtPath%\..\translations\qtquick1_*.qm" "%RsDeployPath%\translations" %Quite% copy "%QtPath%\..\translations\qtmultimedia_*.qm" "%RsDeployPath%\translations" %Quite% copy "%QtPath%\..\translations\qtxmlpatterns_*.qm" "%RsDeployPath%\translations" %Quite% ) @@ -163,7 +157,7 @@ echo copy bdboot.txt copy "%SourcePath%\libbitdht\src\bitdht\bdboot.txt" "%RsDeployPath%" %Quite% echo copy changelog.txt -copy "%SourcePath%\retroshare-gui\src\changelog.txt" "%RsDeployPath%" %Quite% +copy "%RsBuildPath%\changelog.txt" "%RsDeployPath%" %Quite% if exist "%SourcePath%\libresapi\src\webui" ( echo copy webui @@ -173,7 +167,7 @@ if exist "%SourcePath%\libresapi\src\webui" ( if "%ParamTor%"=="1" ( echo copy tor - echo n | copy /-y "%EnvDownloadPath%\tor\Tor\*.*" "%RsDeployPath%" %Quite% + echo n | copy /-y "%EnvTorPath%\Tor\*.*" "%RsDeployPath%" %Quite% ) rem pack files diff --git a/build_scripts/Windows/env/env-msys.bat b/build_scripts/Windows/env/env-msys.bat deleted file mode 100644 index 99d988e1e..000000000 --- a/build_scripts/Windows/env/env-msys.bat +++ /dev/null @@ -1,22 +0,0 @@ -:: Usage: -:: call env-msys.bat [reinstall|clean] - -:: Initialize environment -call "%~dp0env.bat" -if errorlevel 1 goto error_env - -set EnvMSYSPath=%EnvRootPath%\msys - -call "%~dp0tools\prepare-msys.bat" %1 -if errorlevel 1 exit /B %ERRORLEVEL% - -set EnvMSYSSH=%EnvMSYSPath%\msys\1.0\bin\sh.exe -if not exist "%EnvMSYSSH%" if errorlevel 1 goto error_env - -set EnvMSYSCmd="%EnvMSYSSH%" --login -i -c - -exit /B 0 - -:error_env -echo Failed to initialize environment. -exit /B 1 diff --git a/build_scripts/Windows/env/env-msys2.bat b/build_scripts/Windows/env/env-msys2.bat new file mode 100644 index 000000000..b99ede679 --- /dev/null +++ b/build_scripts/Windows/env/env-msys2.bat @@ -0,0 +1,39 @@ +:: Usage: +:: call env-msys2.bat [reinstall|clean] + +:: Initialize environment +call "%~dp0env.bat" +if errorlevel 1 goto error_env + +rem openssl x86 doesn't compile with mingw64 x64 +:: Get gcc versions +call "%ToolsPath%\get-gcc-version.bat" GCCVersion GCCArchitecture +if "%GCCVersion%"=="" %cecho% error "Cannot get gcc version." & exit /B 1 +if "%GCCArchitecture%"=="" %cecho% error "Cannot get gcc architecture." & exit /B 1 + +rem IF DEFINED ProgramFiles(x86) ( +if "%GCCArchitecture%"=="x64" ( + :: x64 + set MSYS2Architecture=x86_64 + set MSYS2Base=64 +) else ( + :: x86 + set MSYS2Architecture=i686 + set MSYS2Base=32 +) + +set EnvMSYS2Path=%EnvRootPath%\msys2 + +call "%~dp0tools\prepare-msys2.bat" %1 +if errorlevel 1 exit /B %ERRORLEVEL% + +set EnvMSYS2SH=%EnvMSYS2Path%\msys%MSYS2Base%\usr\bin\sh.exe +if not exist "%EnvMSYS2SH%" if errorlevel 1 goto error_env + +set EnvMSYS2Cmd="%EnvMSYS2SH%" -lc + +exit /B 0 + +:error_env +echo Failed to initialize environment. +exit /B 1 diff --git a/build_scripts/Windows/env/env-qt.bat b/build_scripts/Windows/env/env-qt.bat deleted file mode 100644 index 776e2078c..000000000 --- a/build_scripts/Windows/env/env-qt.bat +++ /dev/null @@ -1,23 +0,0 @@ -:: Usage: -:: call env-qt4.bat version [reinstall|clean] - -:: Initialize environment -call "%~dp0env.bat" -if errorlevel 1 goto error_env - -set EnvQtBasePath=%EnvRootPath%\qt - -:: Create folders -if not exist "%EnvQtBasePath%" mkdir "%EnvQtBasePath%" - -call "%~dp0tools\prepare-qt.bat" %1 %2 -if errorlevel 1 exit /B %ERRORLEVEL% - -if "%MinGWDir%" NEQ "" set PATH=%MinGWDir%\bin;%PATH% -if "%QtDir%" NEQ "" set PATH=%QtDir%\bin;%PATH% - -exit /B 0 - -:error_env -echo Failed to initialize environment. -exit /B 1 diff --git a/build_scripts/Windows/env/env.bat b/build_scripts/Windows/env/env.bat index a640d1612..bc95045b0 100644 --- a/build_scripts/Windows/env/env.bat +++ b/build_scripts/Windows/env/env.bat @@ -6,11 +6,9 @@ set EnvRootPath=%RootPath%\%SourceName%-env set EnvToolsPath=%EnvRootPath%\tools set EnvTempPath=%EnvRootPath%\tmp set EnvDownloadPath=%EnvRootPath%\download +set EnvTorPath=%EnvDownloadPath%\tor -::set EnvCurlExe=%EnvToolsPath%\curl.exe -set EnvWgetExe=%EnvToolsPath%\wget.exe set EnvSevenZipExe=%EnvToolsPath%\7z.exe -set EnvJomExe=%EnvToolsPath%\jom.exe set EnvSedExe=%EnvToolsPath%\sed.exe set EnvCutExe=%EnvToolsPath%\cut.exe set EnvDependsExe=%EnvToolsPath%\depends.exe @@ -27,7 +25,7 @@ call "%~dp0tools\prepare-tools.bat" if errorlevel 1 exit /B %ERRORLEVEL% :: Add MinGit to PATH -set PATH=%EnvToolsPath%\MinGit\cmd;%PATH% +set PATH=%EnvToolsPath%\MinGit\cmd;%EnvToolsPath%\cmake\bin;%PATH% set HOME=%EnvToolsPath%\MinGit\home exit /B 0 diff --git a/build_scripts/Windows/env/tools/prepare-msys.bat b/build_scripts/Windows/env/tools/prepare-msys.bat deleted file mode 100644 index 23dd391f4..000000000 --- a/build_scripts/Windows/env/tools/prepare-msys.bat +++ /dev/null @@ -1,81 +0,0 @@ -:: Usage: -:: call prepare-msys.bat [reinstall|clean] - -setlocal enabledelayedexpansion - -if "%EnvMSYSPath%"=="" exit /B 1 -if not exist "%EnvRootPath%"=="" exit /B 1 - -copy "%~dp0root\update-msys.bat" "%EnvRootPath%" >nul - -if "%~1"=="clean" ( - %cecho% info "Clean MSYS" - call "%ToolsPath%\remove-dir.bat" "%EnvMSYSPath%" - goto exit -) - -if exist "%EnvMSYSPath%\bin\mingw-get.exe" ( - if "%~1"=="reinstall" ( - choice /M "Found existing MSYS version. Do you want to proceed?" - if !ERRORLEVEL!==2 goto exit - ) else ( - goto exit - ) -) - -set MSYSInstall=mingw-get-0.6.2-mingw32-beta-20131004-1-bin.zip -set MSYSUrl=http://sourceforge.net/projects/mingw/files/Installer/mingw-get/mingw-get-0.6.2-beta-20131004-1/%MSYSInstall%/download -set CMakeInstall=cmake-3.1.0-win32-x86.zip -set CMakeUrl=http://www.cmake.org/files/v3.1/%CMakeInstall% -set CMakeUnpackPath=%EnvMSYSPath%\msys\1.0 - -%cecho% info "Remove previous MSYS version" -call "%ToolsPath%\remove-dir.bat" "%EnvMSYSPath%" - -%cecho% info "Download installation files" -if not exist "%EnvDownloadPath%\%MSYSInstall%" call "%ToolsPath%\download-file.bat" "%MSYSUrl%" "%EnvDownloadPath%\%MSYSInstall%" -if not exist "%EnvDownloadPath%\%MSYSInstall%" %cecho% error "Cannot download MSYS" & goto error - -if not exist "%EnvDownloadPath%\%CMakeInstall%" call "%ToolsPath%\download-file.bat" "%CMakeUrl%" "%EnvDownloadPath%\%CMakeInstall%" -if not exist "%EnvDownloadPath%\%CMakeInstall%" %cecho% error "Cannot download CMake" & goto error - -%cecho% info "Unpack MSYS" -"%EnvSevenZipExe%" x -o"%EnvMSYSPath%" "%EnvDownloadPath%\%MSYSInstall%" - -%cecho% info "Install MSYS" -if not exist "%EnvMSYSPath%\var\lib\mingw-get\data\profile.xml" copy "%EnvMSYSPath%\var\lib\mingw-get\data\defaults.xml" "%EnvMSYSPath%\var\lib\mingw-get\data\profile.xml" -pushd "%EnvMSYSPath%\bin" -mingw-get.exe install mingw32-mingw-get -mingw-get.exe install msys-coreutils -mingw-get.exe install msys-base -mingw-get.exe install msys-autoconf -mingw-get.exe install msys-automake -mingw-get.exe install msys-autogen -mingw-get.exe install msys-mktemp -rem Use own wget binary, because MSYS version of wget is to old -rem mingw-get.exe install msys-wget -popd - -%cecho% info "Unpack CMake" -"%EnvSevenZipExe%" x -o"%CMakeUnpackPath%" "%EnvDownloadPath%\%CMakeInstall%" - -%cecho% info "Install CMake" -set CMakeVersion= -for /D %%F in (%CMakeUnpackPath%\cmake*) do set CMakeVersion=%%~nxF -if "%CMakeVersion%"=="" %cecho% error "CMake version not found." & goto :exit -%cecho% info "Found CMake version %CMakeVersion%" - -set FoundProfile= -for /f "tokens=3" %%F in ('find /c /i "%CMakeVersion%" "%EnvMSYSPath%\msys\1.0\etc\profile"') do set FoundProfile=%%F - -if "%FoundProfile%"=="0" ( - echo export PATH="${PATH}:/%CMakeVersion%/bin">>"%EnvMSYSPath%\msys\1.0\etc\profile" -) - -:exit -endlocal -exit /B 0 - -:error -endlocal -exit /B 1 diff --git a/build_scripts/Windows/env/tools/prepare-msys2.bat b/build_scripts/Windows/env/tools/prepare-msys2.bat new file mode 100644 index 000000000..80d17521f --- /dev/null +++ b/build_scripts/Windows/env/tools/prepare-msys2.bat @@ -0,0 +1,85 @@ +:: Usage: +:: call prepare-msys2.bat [reinstall|clean] + +setlocal enabledelayedexpansion + +if "%EnvMSYS2Path%"=="" exit /B 1 +if "%MSYS2Architecture%"=="" exit /B 1 +if "%MSYS2Base%"=="" exit /B 1 +if not exist "%EnvRootPath%"=="" exit /B 1 + +copy "%~dp0root\update-msys2.bat" "%EnvRootPath%" >nul + +if "%~1"=="clean" ( + %cecho% info "Clean MSYS2" + call "%ToolsPath%\remove-dir.bat" "%EnvMSYS2Path%" + goto exit +) + +if exist "%EnvMSYS2Path%\msys%MSYS2Base%\usr\bin\pacman.exe" ( + if "%~1"=="reinstall" ( + choice /M "Found existing MSYS2 version. Do you want to proceed?" + if !ERRORLEVEL!==2 goto exit + ) else ( + goto exit + ) +) + +set MSYS2Install=msys2-base-%MSYS2Architecture%-20190524.tar.xz +set MSYS2Url=http://sourceforge.net/projects/msys2/files/Base/%MSYS2Architecture%/%MSYS2Install%/download +set CMakeInstall=cmake-3.1.0-win32-x86.zip +set CMakeUrl=http://www.cmake.org/files/v3.1/%CMakeInstall% +set CMakeUnpackPath=%EnvMSYS2Path%\msys%MSYS2Base% + +if exist "%EnvMSYS2Path%\msys%MSYS2Base%" ( + %cecho% info "Remove previous MSYS2 version" + call "%ToolsPath%\remove-dir.bat" "%EnvMSYS2Path%\msys%MSYS2Base%" +) + +%cecho% info "Download installation files" +if not exist "%EnvDownloadPath%\%MSYS2Install%" call "%ToolsPath%\download-file.bat" "%MSYS2Url%" "%EnvDownloadPath%\%MSYS2Install%" +if not exist "%EnvDownloadPath%\%MSYS2Install%" %cecho% error "Cannot download MSYS" & goto error + +if not exist "%EnvDownloadPath%\%CMakeInstall%" call "%ToolsPath%\download-file.bat" "%CMakeUrl%" "%EnvDownloadPath%\%CMakeInstall%" +if not exist "%EnvDownloadPath%\%CMakeInstall%" %cecho% error "Cannot download CMake" & goto error + +%cecho% info "Unpack MSYS2" +"%EnvSevenZipExe%" x -so "%EnvDownloadPath%\%MSYS2Install%" | "%EnvSevenZipExe%" x -y -si -ttar -o"%EnvMSYS2Path%" + +%cecho% info "Unpack CMake" +"%EnvSevenZipExe%" x -o"%CMakeUnpackPath%" "%EnvDownloadPath%\%CMakeInstall%" + +%cecho% info "Install CMake" +set CMakeVersion= +for /D %%F in (%CMakeUnpackPath%\cmake*) do set CMakeVersion=%%~nxF +if "%CMakeVersion%"=="" %cecho% error "CMake version not found." & goto :exit +%cecho% info "Found CMake version %CMakeVersion%" + +set FoundProfile= +for /f "tokens=3" %%F in ('find /c /i "%CMakeVersion%" "%EnvMSYS2Path%\msys%MSYS2Base%\etc\profile"') do set FoundProfile=%%F + +if "%FoundProfile%"=="0" ( + echo export PATH="${PATH}:/%CMakeVersion%/bin">>"%EnvMSYS2Path%\msys%MSYS2Base%\etc\profile" +) + +set MSYS2SH=%EnvMSYS2Path%\msys%MSYS2Base%\usr\bin\sh + +%cecho% info "Update keyring" +"%MSYS2SH%" -lc "curl -O http://repo.msys2.org/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz" +"%MSYS2SH%" -lc "pacman --noconfirm -U msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz" + +%cecho% info "Initialize MSYS2" +"%MSYS2SH%" -lc "pacman -Sy" +"%MSYS2SH%" -lc "pacman --noconfirm --needed -S bash pacman pacman-mirrors msys2-runtime" + +call "%EnvMSYS2Path%\msys%MSYS2Base%\autorebase.bat" +call "%EnvRootPath%\update-msys2.bat" +call "%EnvRootPath%\update-msys2.bat" + +:exit +endlocal +exit /B 0 + +:error +endlocal +exit /B 1 diff --git a/build_scripts/Windows/env/tools/prepare-qt.bat b/build_scripts/Windows/env/tools/prepare-qt.bat deleted file mode 100644 index c2593f48d..000000000 --- a/build_scripts/Windows/env/tools/prepare-qt.bat +++ /dev/null @@ -1,204 +0,0 @@ -:: Usage: -:: call prepare-qt.bat version [reinstall|clean] - -setlocal enabledelayedexpansion - -if "%EnvQtBasePath%"=="" exit /B 1 -if not exist "%EnvRootPath%"=="" exit /B 1 - -set EnvQtVersion=%~1 -if "%EnvQtVersion%"=="" ( - %cecho% error "Please specify Qt version" - goto error -) - -for /f "tokens=1,2 delims=." %%A in ("%EnvQtVersion%") do set EnvQtMainVersion=%%A& set EnvQtBaseVersion=%%A.%%B -set EnvQtPath=%EnvQtBasePath%\%EnvQtVersion% - -if "%~2"=="clean" ( - %cecho% info "Clean Qt %EnvQtVersion%" - call "%ToolsPath%\remove-dir.bat" "%EnvQtPath%" - goto exit -) - -set CheckQmakeExe= -if "%EnvQtMainVersion%"=="4" ( - set CheckQmakeExe=%EnvQtPath%\Qt\bin\qmake.exe -) else ( - if "%EnvQtMainVersion%" GEQ "5" ( - call :get_mingw_version EnvQtMinGWVersion "%EnvQtPath%\%EnvQtBaseVersion%" - if "!EnvQtMinGWVersion!" NEQ "" ( - set CheckQmakeExe=%EnvQtPath%\%EnvQtBaseVersion%\!EnvQtMinGWVersion!\bin\qmake.exe - ) - ) -) - -if "%CheckQmakeExe%" NEQ "" ( - if exist "%CheckQmakeExe%" ( - if "%~2"=="reinstall" ( - choice /M "Found existing Qt %EnvQtVersion% version. Do you want to proceed?" - if !ERRORLEVEL!==2 goto exit - ) else ( - goto exit - ) - ) -) - -set QtInstall=qt-opensource-windows-x86-mingw-%EnvQtVersion%.exe -set QtInstallWildcard=qt-opensource-windows-x86-mingw*-%EnvQtVersion%.exe -set QtUrl=http://download.qt.io/official_releases/qt/%EnvQtBaseVersion%/%EnvQtVersion% - -%cecho% info "Remove previous Qt %EnvQtVersion% version" -call "%ToolsPath%\remove-dir.bat" "%EnvQtPath%" - -%cecho% info "Download Qt installation files" -if not exist "%EnvDownloadPath%\%QtInstall%" ( - call "%ToolsPath%\download-file-wildcard.bat" "%QtUrl%" "%QtInstallWildcard%" "%EnvDownloadPath%" QtInstallDownload - if "!QtInstallDownload!"=="" %cecho% error "Cannot download Qt %EnvQtVersion%" & goto error - ren "%EnvDownloadPath%\!QtInstallDownload!" "%QtInstall%" -) -if not exist "%EnvDownloadPath%\%QtInstall%" %cecho% error "Cannot download Qt %EnvQtVersion%" & goto error - -mkdir "%EnvQtPath%" - -if "%EnvQtMainVersion%"=="4" ( - rem Qt 4 - goto install_qt4 -) -if "%EnvQtMainVersion%" GEQ "5" ( - rem Qt >= 5 - goto install_qt5 -) - -%cecho% error "Unknown Qt version %EnvQtVersion%" - -:error -endlocal & set QtDir=& set MinGWDir= -exit /B 1 - -:exit -set QtDir= -set MinGWDir= - -if "%EnvQtMainVersion%"=="4" ( - rem Qt 4 - set QtDir=%EnvQtBasePath%\%EnvQtVersion%\Qt - set MinGWDir=%EnvQtBasePath%\%EnvQtVersion%\mingw32 -) else ( - if "%EnvQtMainVersion%" GEQ "5" ( - call :get_mingw_version EnvQtToolsMinGWVersion "%EnvQtPath%\Tools" - - set QtDir=%EnvQtPath%\%EnvQtBaseVersion%\!EnvQtMinGWVersion! - set MinGWDir=%EnvQtPath%\Tools\!EnvQtToolsMinGWVersion! - ) -) - -endlocal & set QtDir=%QtDir%& set MinGWDir=%MinGWDir% -exit /B 0 - -:get_mingw_version -setlocal enabledelayedexpansion -set Variable=%~1 -set Result= - -for /D %%A in (%~2\*) do set Name=%%~nA& if "!Name:~0,5!"=="mingw" set Result=!Name! -endlocal & set %Variable%=%Result% -goto :EOF - -:replace -set InFile=%~1 -set InFileName=%~nx1 -set OutFile=%~1.tmp -set SearchText=%~2 -set ReplaceText=%~3 - -if exist "%OutFile%" del /Q "%OutFile%" - -for /f "tokens=1* delims=]" %%A in ('find /n /v ""^<%InFile%') do ( - set string=%%B - - if "!string!"=="" ( - echo.>>%OutFile% - ) else ( - set modified=!string:%SearchText%=%ReplaceText%! - echo !modified!>> %OutFile% - ) -) -del "%InFile%" -rename "%OutFile%" "%InFileName%" -goto :EOF - -:install_qt4 -set MinGWInstall=i686-4.8.2-release-posix-dwarf-rt_v3-rev3.7z -set MinGWUrl=http://sourceforge.net/projects/mingw-w64/files/Toolchains targetting Win32/Personal Builds/mingw-builds/4.8.2/threads-posix/dwarf/%MinGWInstall%/download - -%cecho% info "Download MinGW installation files" -if not exist "%EnvDownloadPath%\%MinGWInstall%" call "%ToolsPath%\download-file.bat" "%MinGWUrl%" "%EnvDownloadPath%\%MinGWInstall%" -if not exist "%EnvDownloadPath%\%MinGWInstall%" %cecho% error "Cannot download MinGW" & goto error - -%cecho% info "Unpack Qt %EnvQtVersion%" -call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%" -mkdir "%EnvTempPath%" -"%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%QtInstall%" $_14_ -move "%EnvTempPath%\$_14_" "%EnvQtPath%\Qt" -call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%" - -%cecho% info "Unpack MinGW" -"%EnvSevenZipExe%" x -o"%EnvQtPath%" "%EnvDownloadPath%\%MinGWInstall%" - -echo Prepare Qt %EnvQtVersion% -echo [Paths]>"%EnvQtPath%\Qt\bin\qt.conf" -echo Prefix=..>>"%EnvQtPath%\Qt\bin\qt.conf" - -goto exit - -:install_qt5 -set EnvQtInstallerFrameworkVersion=2.0.3 - -set QtInstallerFrameworkInstall=QtInstallerFramework-%EnvQtInstallerFrameworkVersion%-win-x86.exe -set QtInstallerFrameworkUrl=http://download.qt.io/official_releases/qt-installer-framework/%EnvQtInstallerFrameworkVersion%/QtInstallerFramework-win-x86.exe - -%cecho% info "Download QtInstallerFramework installation files" -if not exist "%EnvDownloadPath%\%QtInstallerFrameworkInstall%" call "%ToolsPath%\download-file.bat" "%QtInstallerFrameworkUrl%" "%EnvDownloadPath%\%QtInstallerFrameworkInstall%" -if not exist "%EnvDownloadPath%\%QtInstallerFrameworkInstall%" %cecho% error "Cannot download Qt Installer Framework %EnvQtInstallerFrameworkVersion%" & goto error - -%cecho% info "Unpack Qt Installer Framework" -call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%" -mkdir "%EnvTempPath%" -"%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%QtInstallerFrameworkInstall%" bin\devtool.exe -move "%EnvTempPath%\bin\devtool.exe" "%EnvQtPath%" -call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%" - -%cecho% info "Unpack Qt %EnvQtVersion%" -call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%" -mkdir "%EnvTempPath%" -"%EnvQtPath%\devtool.exe" "%EnvDownloadPath%\%QtInstall%" --dump "%EnvTempPath%" - -pushd "%EnvTempPath%" -del /S *vcredist*.7z -del /S *qtcreator*.7z -del /S *1installer-changelog.7z -for /R %%F in (*.7z) do "%EnvSevenZipExe%" x -y -o"%EnvQtPath%" "%%F" -popd - -call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%" - -call :get_mingw_version EnvQtMinGWVersion "%EnvQtPath%\%EnvQtBaseVersion%" - -%cecho% info "Prepare Qt %EnvQtVersion%" -echo [Paths]>"%EnvQtPath%\%EnvQtBaseVersion%\%EnvQtMinGWVersion%\bin\qt.conf" -echo Documentation=../../Docs/Qt-%EnvQtBaseVersion%>>"%EnvQtPath%\%EnvQtBaseVersion%\%EnvQtMinGWVersion%\bin\qt.conf" -echo Examples=../../Examples/Qt-%EnvQtBaseVersion%>>"%EnvQtPath%\%EnvQtBaseVersion%\%EnvQtMinGWVersion%\bin\qt.conf" -echo Prefix=..>>"%EnvQtPath%\%EnvQtBaseVersion%\%EnvQtMinGWVersion%\bin\qt.conf" - -call :replace "%EnvQtPath%\%EnvQtBaseVersion%\%EnvQtMinGWVersion%\mkspecs\qconfig.pri" "Enterprise" "OpenSource" - -for /R "%EnvQtPath%\%EnvQtBaseVersion%\%EnvQtMinGWVersion%\lib" %%A in (*.pc) do ( - call :replace "%%A" "c:/Users/qt/work/install" "%EnvQtPath:\=\\%\%EnvQtBaseVersion%\\%EnvQtMinGWVersion%" - call :replace "%%A" "c:\Users\qt\work\install" "%EnvQtPath:\=/%\%EnvQtBaseVersion%/%EnvQtMinGWVersion%" -) -for /R "%EnvQtPath%\%EnvQtBaseVersion%\%EnvQtMinGWVersion%\lib" %%A in (*.prl) do ( - call :replace "%%A" "c:/Users/qt/work/install" "%EnvQtPath:\=\\%\%EnvQtBaseVersion%\\%EnvQtMinGWVersion%" - call :replace "%%A" "c:\Users\qt\work\install" "%EnvQtPath:\=/%\%EnvQtBaseVersion%/%EnvQtMinGWVersion%" -) -goto exit diff --git a/build_scripts/Windows/env/tools/prepare-tools.bat b/build_scripts/Windows/env/tools/prepare-tools.bat index e8bc7a951..8b48d7c32 100644 --- a/build_scripts/Windows/env/tools/prepare-tools.bat +++ b/build_scripts/Windows/env/tools/prepare-tools.bat @@ -4,36 +4,24 @@ if "%EnvRootPath%"=="" exit /B 1 set CEchoUrl=https://github.com/lordmulder/cecho/releases/download/2015-10-10/cecho.2015-10-10.zip set CEchoInstall=cecho.2015-10-10.zip -set SevenZipUrl=https://sourceforge.net/projects/sevenzip/files/7-Zip/18.05/7z1805.msi/download -set SevenZipInstall=7z1805.msi -::set CurlUrl=https://bintray.com/artifact/download/vszakats/generic/curl-7.50.1-win32-mingw.7z -::set CurlInstall=curl-7.50.1-win32-mingw.7z -set WgetUrl=https://eternallybored.org/misc/wget/1.19.4/32/wget.exe -set WgetInstall=wget.exe -set JomUrl=http://download.qt.io/official_releases/jom/jom.zip -set JomInstall=jom.zip +set SevenZipUrl=https://sourceforge.net/projects/sevenzip/files/7-Zip/19.00/7z1900.msi/download +set SevenZipInstall=7z1900.msi set DependsUrl=http://www.dependencywalker.com/depends22_x86.zip set DependsInstall=depends22_x86.zip set UnixToolsUrl=http://unxutils.sourceforge.net/UnxUpdates.zip set UnixToolsInstall=UnxUpdates.zip -set NSISUrl=http://prdownloads.sourceforge.net/nsis/nsis-3.0-setup.exe?download -set NSISInstall=nsis-3.0-setup.exe +set NSISInstall=nsis-3.05-setup.exe +set NSISUrl=http://prdownloads.sourceforge.net/nsis/%NSISInstall%?download set NSISInstallPath=%EnvToolsPath%\NSIS -set MinGitInstall=MinGit-2.19.1-32-bit.zip -set MinGitUrl=https://github.com/git-for-windows/git/releases/download/v2.19.1.windows.1/%MinGitInstall% +set MinGitInstall=MinGit-2.28.0-32-bit.zip +set MinGitUrl=https://github.com/git-for-windows/git/releases/download/v2.28.0.windows.1/%MinGitInstall% set MinGitInstallPath=%EnvToolsPath%\MinGit -set SigcheckInstall=Sigcheck.zip -set SigcheckUrl=https://download.sysinternals.com/files/%SigcheckInstall% - -if not exist "%EnvToolsPath%\wget.exe" ( - echo Download Wget installation - - if not exist "%EnvDownloadPath%\%WgetInstall%" call "%ToolsPath%\winhttpjs.bat" %WgetUrl% -saveTo "%EnvDownloadPath%\%WgetInstall%" - if not exist "%EnvDownloadPath%\%WgetInstall%" %cecho% error "Cannot download Wget installation" & goto error - - echo Copy Wget - copy "%EnvDownloadPath%\wget.exe" "%EnvToolsPath%" -) +set CMakeVersion=cmake-3.1.0-win32-x86 +set CMakeInstall=%CMakeVersion%.zip +set CMakeUrl=http://www.cmake.org/files/v3.1/%CMakeInstall% +set CMakeInstallPath=%EnvToolsPath%\cmake +set TorProjectUrl=https://www.torproject.org +set TorDownloadIndexUrl=%TorProjectUrl%/download/tor if not exist "%EnvToolsPath%\7z.exe" ( call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%" @@ -68,45 +56,13 @@ if not exist "%EnvToolsPath%\cecho.exe" ( call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%" ) -::if not exist "%EnvToolsPath%\curl.exe" ( -:: call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%" -:: mkdir "%EnvTempPath%" -:: -:: echo Download Curl installation -:: -:: if not exist "%EnvDownloadPath%\%CurlInstall%" call "%ToolsPath%\winhttpjs.bat" %CurlUrl% -saveTo "%EnvDownloadPath%\%CurlInstall%" -:: if not exist "%EnvDownloadPath%\%CurlInstall%" echo Cannot download Curl installation& goto error -:: -:: echo Unpack Curl -:: "%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%CurlInstall%" -:: copy "%EnvTempPath%\curl-7.50.1-win32-mingw\bin\curl.exe" "%EnvToolsPath%" -:: -:: call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%" -::) - -if not exist "%EnvToolsPath%\jom.exe" ( - call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%" - mkdir "%EnvTempPath%" - - %cecho% info "Download jom installation" - - if not exist "%EnvDownloadPath%\%JomInstall%" call "%ToolsPath%\winhttpjs.bat" %JomUrl% -saveTo "%EnvDownloadPath%\%JomInstall%" - if not exist "%EnvDownloadPath%\%JomInstall%" %cecho% error "Cannot download jom installation" & goto error - - %cecho% info "Unpack jom" - "%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%JomInstall%" - copy "%EnvTempPath%\jom.exe" "%EnvToolsPath%" - - call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%" -) - if not exist "%EnvToolsPath%\depends.exe" ( call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%" mkdir "%EnvTempPath%" %cecho% info "Download Dependency Walker installation" - if not exist "%EnvDownloadPath%\%DependsInstall%" call "%ToolsPath%\winhttpjs.bat" %DependsUrl% -saveTo "%EnvDownloadPath%\%DependsInstall%" + if not exist "%EnvDownloadPath%\%DependsInstall%" call "%ToolsPath%\download-file.bat" %DependsUrl% "%EnvDownloadPath%\%DependsInstall%" if not exist "%EnvDownloadPath%\%DependsInstall%" %cecho% error "Cannot download Dependendy Walker installation" & goto error %cecho% info "Unpack Dependency Walker" @@ -122,7 +78,7 @@ if not exist "%EnvToolsPath%\cut.exe" ( %cecho% info "Download Unix Tools installation" - if not exist "%EnvDownloadPath%\%UnixToolsInstall%" call "%ToolsPath%\winhttpjs.bat" %UnixToolsUrl% -saveTo "%EnvDownloadPath%\%UnixToolsInstall%" + if not exist "%EnvDownloadPath%\%UnixToolsInstall%" call "%ToolsPath%\download-file.bat" %UnixToolsUrl% "%EnvDownloadPath%\%UnixToolsInstall%" if not exist "%EnvDownloadPath%\%UnixToolsInstall%" %cecho% error "Cannot download Unix Tools installation" & goto error %cecho% info "Unpack Unix Tools" @@ -138,7 +94,7 @@ if not exist "%EnvToolsPath%\sed.exe" ( %cecho% info "Download Unix Tools installation" - if not exist "%EnvDownloadPath%\%UnixToolsInstall%" call "%ToolsPath%\winhttpjs.bat" %UnixToolsUrl% -saveTo "%EnvDownloadPath%\%UnixToolsInstall%" + if not exist "%EnvDownloadPath%\%UnixToolsInstall%" call "%ToolsPath%\download-file.bat" %UnixToolsUrl% "%EnvDownloadPath%\%UnixToolsInstall%" if not exist "%EnvDownloadPath%\%UnixToolsInstall%" %cecho% error "Cannot download Unix Tools installation" & goto error %cecho% info "Unpack Unix Tools" @@ -148,8 +104,12 @@ if not exist "%EnvToolsPath%\sed.exe" ( call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%" ) +if not exist "%EnvDownloadPath%\%NSISInstall%" call "%ToolsPath%\remove-dir.bat" "%NSISInstallPath%" if not exist "%NSISInstallPath%\nsis.exe" ( call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%" + + if exist "%NSISInstallPath%" call "%ToolsPath%\remove-dir.bat" "%NSISInstallPath%" + mkdir "%EnvTempPath%" %cecho% info "Download NSIS installation" @@ -175,14 +135,45 @@ if not exist "%MinGitInstallPath%\cmd\git.exe" ( "%EnvSevenZipExe%" x -o"%MinGitInstallPath%" "%EnvDownloadPath%\%MinGitInstall%" ) -if not exist "%EnvToolsPath%\sigcheck.exe" ( - %cecho% info "Download Sigcheck installation" +if not exist "%EnvDownloadPath%\%CMakeInstall%" call "%ToolsPath%\remove-dir.bat" "%CMakeInstallPath%" +if not exist "%CMakeInstallPath%\bin\cmake.exe" ( + %cecho% info "Download CMake installation" - if not exist "%EnvDownloadPath%\%SigcheckInstall%" call "%ToolsPath%\download-file.bat" "%SigcheckUrl%" "%EnvDownloadPath%\%SigcheckInstall%" - if not exist "%EnvDownloadPath%\%SigcheckInstall%" %cecho% error "Cannot download Sigcheck installation" & goto error + if exist "%CMakeInstallPath%" call "%ToolsPath%\remove-dir.bat" "%CMakeInstallPath%" - %cecho% info "Unpack Sigcheck" - "%EnvSevenZipExe%" x -o"%EnvToolsPath%" "%EnvDownloadPath%\%SigcheckInstall%" sigcheck.exe + mkdir "%EnvTempPath%" + + if not exist "%EnvDownloadPath%\%CMakeInstall%" call "%ToolsPath%\download-file.bat" "%CMakeUrl%" "%EnvDownloadPath%\%CMakeInstall%" + if not exist "%EnvDownloadPath%\%CMakeInstall%" %cecho% error "Cannot download CMake installation" & goto error + + %cecho% info "Unpack CMake" + "%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%CMakeInstall%" + + move "%EnvTempPath%\%CMakeVersion%" "%CMakeInstallPath%" + + call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%" +) + +rem Tor +rem Get download link and filename from download page +mkdir "%EnvTempPath%" +call "%ToolsPath%\download-file.bat" "%TorDownloadIndexUrl%" "%EnvTempPath%\index.html" +if not exist "%EnvTempPath%\index.html" %cecho% error "Cannot download Tor installation" & goto error + +for /F "tokens=1,2 delims= " %%A in ('%EnvSedExe% -r -n -e"s/.*href=\"^(.*^)^(tor-win32.*\.zip^)\".*/\2 \1\2/p" "%EnvTempPath%\index.html"') do set TorInstall=%%A& set TorDownloadUrl=%TorProjectUrl%%%B +call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%" +if "%TorInstall%"=="" %cecho% error "Cannot download Tor installation" & goto error +if "%TorDownloadUrl%"=="" %cecho% error "Cannot download Tor installation" & goto error + +if not exist "%EnvDownloadPath%\%TorInstall%" call "%ToolsPath%\remove-dir.bat" "%EnvTorPath%" +if not exist "%EnvTorPath%\Tor\tor.exe" ( + %cecho% info "Download Tor installation" + + if not exist "%EnvDownloadPath%\%TorInstall%" call "%ToolsPath%\download-file.bat" "%TorDownloadUrl%" "%EnvDownloadPath%\%TorInstall%" + if not exist "%EnvDownloadPath%\%TorInstall%" %cecho% error "Cannot download Tor installation" & goto error + + %cecho% info "Unpack Tor" + "%EnvSevenZipExe%" x -o"%EnvTorPath%" "%EnvDownloadPath%\%TorInstall%" ) :exit @@ -190,5 +181,6 @@ endlocal exit /B 0 :error +call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%" endlocal exit /B 1 diff --git a/build_scripts/Windows/env/tools/root/update-msys.bat b/build_scripts/Windows/env/tools/root/update-msys.bat deleted file mode 100644 index b0a109d01..000000000 --- a/build_scripts/Windows/env/tools/root/update-msys.bat +++ /dev/null @@ -1,15 +0,0 @@ -@echo off - -setlocal - -set MSYSPath=%~dp0msys - -if not exist "%MSYSPath%\bin\mingw-get.exe" echo MSYS is not installed& exit /B 0 - -echo Update MSYS -pushd "%MSYSPath%\bin" -mingw-get.exe update -mingw-get.exe upgrade -popd - -exit /B %ERRORLEVEL% diff --git a/build_scripts/Windows/env/tools/root/update-msys2.bat b/build_scripts/Windows/env/tools/root/update-msys2.bat new file mode 100644 index 000000000..3075e392b --- /dev/null +++ b/build_scripts/Windows/env/tools/root/update-msys2.bat @@ -0,0 +1,19 @@ +@echo off + +setlocal + +if exist "%~dp0msys2\msys32" call :update 32 +if exist "%~dp0msys2\msys64" call :update 64 + +goto :EOF + +:update +set MSYSSH=%~dp0msys2\msys%~1\usr\bin\sh + +echo Update MSYS2 %~1 +"%MSYSSH%" -lc "pacman -Sy" +"%MSYSSH%" -lc "pacman --noconfirm -Su" + +:exit +endlocal +goto :EOF diff --git a/build_scripts/Windows/installer/lang/ca_ES.nsh b/build_scripts/Windows/installer/lang/ca_ES.nsh index acb11b5a4..74f92ffe5 100644 --- a/build_scripts/Windows/installer/lang/ca_ES.nsh +++ b/build_scripts/Windows/installer/lang/ca_ES.nsh @@ -1,5 +1,7 @@ !insertmacro LANG_STRING Section_Main "${APPNAME}" !insertmacro LANG_STRING Section_Main_Desc "Instal·la ${APPNAME} i els components necessaris." +!insertmacro LANG_STRING Section_Tor "Tor" +!insertmacro LANG_STRING Section_Tor_Desc "Installs Tor." !insertmacro LANG_STRING Section_Data "Pells" !insertmacro LANG_STRING Section_Data_Desc "Instal·la pells." !insertmacro LANG_STRING Section_Shortcuts "Icones d'accés directe" diff --git a/build_scripts/Windows/installer/lang/de.nsh b/build_scripts/Windows/installer/lang/de.nsh index c5323f523..663e9e786 100644 --- a/build_scripts/Windows/installer/lang/de.nsh +++ b/build_scripts/Windows/installer/lang/de.nsh @@ -1,5 +1,7 @@ !insertmacro LANG_STRING Section_Main "${APPNAME}" !insertmacro LANG_STRING Section_Main_Desc "Installiert ${APPNAME} und die benötigten Komponenten." +!insertmacro LANG_STRING Section_Tor "Tor" +!insertmacro LANG_STRING Section_Tor_Desc "Installiert Tor." !insertmacro LANG_STRING Section_Data "Skins" !insertmacro LANG_STRING Section_Data_Desc "Skins installieren." !insertmacro LANG_STRING Section_Shortcuts "Verknüpfungssymbole" diff --git a/build_scripts/Windows/installer/lang/en.nsh b/build_scripts/Windows/installer/lang/en.nsh index b01f43baa..a709eec24 100644 --- a/build_scripts/Windows/installer/lang/en.nsh +++ b/build_scripts/Windows/installer/lang/en.nsh @@ -1,5 +1,7 @@ !insertmacro LANG_STRING Section_Main "${APPNAME}" !insertmacro LANG_STRING Section_Main_Desc "Installs ${APPNAME} and required components." +!insertmacro LANG_STRING Section_Tor "Tor" +!insertmacro LANG_STRING Section_Tor_Desc "Installs Tor." !insertmacro LANG_STRING Section_Data "Skins" !insertmacro LANG_STRING Section_Data_Desc "Installs skins." !insertmacro LANG_STRING Section_Shortcuts "Shortcut icons" diff --git a/build_scripts/Windows/installer/lang/es.nsh b/build_scripts/Windows/installer/lang/es.nsh index 68900ec62..66021c14e 100644 --- a/build_scripts/Windows/installer/lang/es.nsh +++ b/build_scripts/Windows/installer/lang/es.nsh @@ -1,5 +1,7 @@ !insertmacro LANG_STRING Section_Main "${APPNAME}" !insertmacro LANG_STRING Section_Main_Desc "Instala ${APPNAME} y los componentes requeridos." +!insertmacro LANG_STRING Section_Tor "Tor" +!insertmacro LANG_STRING Section_Tor_Desc "Installs Tor." !insertmacro LANG_STRING Section_Data "Coberturas (skins)" !insertmacro LANG_STRING Section_Data_Desc "Instalar coberturas" !insertmacro LANG_STRING Section_Shortcuts "Iconos de accesos directos" diff --git a/build_scripts/Windows/installer/lang/fr.nsh b/build_scripts/Windows/installer/lang/fr.nsh index d4c797833..680746da0 100644 --- a/build_scripts/Windows/installer/lang/fr.nsh +++ b/build_scripts/Windows/installer/lang/fr.nsh @@ -1,5 +1,7 @@ !insertmacro LANG_STRING Section_Main "${APPNAME}" !insertmacro LANG_STRING Section_Main_Desc "Installe ${APPNAME} et les composants requis." +!insertmacro LANG_STRING Section_Tor "Tor" +!insertmacro LANG_STRING Section_Tor_Desc "Installs Tor." !insertmacro LANG_STRING Section_Data "Habillages" !insertmacro LANG_STRING Section_Data_Desc "Installe des habillages." !insertmacro LANG_STRING Section_Shortcuts "Icônes de raccourci" diff --git a/build_scripts/Windows/installer/lang/pl.nsh b/build_scripts/Windows/installer/lang/pl.nsh index 9f6d4305e..53fc42934 100644 --- a/build_scripts/Windows/installer/lang/pl.nsh +++ b/build_scripts/Windows/installer/lang/pl.nsh @@ -1,5 +1,7 @@ !insertmacro LANG_STRING Section_Main "${APPNAME}" !insertmacro LANG_STRING Section_Main_Desc "Instaluje ${APPNAME} oraz wymagane komponenty." +!insertmacro LANG_STRING Section_Tor "Tor" +!insertmacro LANG_STRING Section_Tor_Desc "Installs Tor." !insertmacro LANG_STRING Section_Data "Skórki" !insertmacro LANG_STRING Section_Data_Desc "Instaluje skórki." !insertmacro LANG_STRING Section_Shortcuts "Ikony skrótów" diff --git a/build_scripts/Windows/installer/lang/ru.nsh b/build_scripts/Windows/installer/lang/ru.nsh index 09c578cb8..b8ba2df89 100644 --- a/build_scripts/Windows/installer/lang/ru.nsh +++ b/build_scripts/Windows/installer/lang/ru.nsh @@ -1,5 +1,7 @@ !insertmacro LANG_STRING Section_Main "${APPNAME}" !insertmacro LANG_STRING Section_Main_Desc "Установка ${APPNAME} и необходимых компонентов." +!insertmacro LANG_STRING Section_Tor "Tor" +!insertmacro LANG_STRING Section_Tor_Desc "Installs Tor." !insertmacro LANG_STRING Section_Data "Оболочки" !insertmacro LANG_STRING Section_Data_Desc "Установка оболочек." !insertmacro LANG_STRING Section_Shortcuts "Ярлыки" diff --git a/build_scripts/Windows/installer/lang/tr.nsh b/build_scripts/Windows/installer/lang/tr.nsh index f893ac076..85abeaac2 100644 --- a/build_scripts/Windows/installer/lang/tr.nsh +++ b/build_scripts/Windows/installer/lang/tr.nsh @@ -1,5 +1,7 @@ !insertmacro LANG_STRING Section_Main "${APPNAME}" !insertmacro LANG_STRING Section_Main_Desc "${APPNAME} ve gerekli bileşenleri kurar." +!insertmacro LANG_STRING Section_Tor "Tor" +!insertmacro LANG_STRING Section_Tor_Desc "Installs Tor." !insertmacro LANG_STRING Section_Data "Temalar" !insertmacro LANG_STRING Section_Data_Desc "Tema yükleyin." !insertmacro LANG_STRING Section_Shortcuts "Kısayol simgeleri" diff --git a/build_scripts/Windows/installer/lang/ts/en.ts b/build_scripts/Windows/installer/lang/ts/en.ts index 763636d3d..e95f6939f 100644 --- a/build_scripts/Windows/installer/lang/ts/en.ts +++ b/build_scripts/Windows/installer/lang/ts/en.ts @@ -8,6 +8,20 @@ + + Section_Tor + + Tor + + + + + Section_Tor_Desc + + Installs Tor. + + + Section_Data diff --git a/build_scripts/Windows/installer/lang/zh_CN.nsh b/build_scripts/Windows/installer/lang/zh_CN.nsh index 1075f7a1a..330de33ab 100644 --- a/build_scripts/Windows/installer/lang/zh_CN.nsh +++ b/build_scripts/Windows/installer/lang/zh_CN.nsh @@ -1,5 +1,7 @@ !insertmacro LANG_STRING Section_Main "${APPNAME}" !insertmacro LANG_STRING Section_Main_Desc "Installs ${APPNAME} and required components." +!insertmacro LANG_STRING Section_Tor "Tor" +!insertmacro LANG_STRING Section_Tor_Desc "Installs Tor." !insertmacro LANG_STRING Section_Data "皮肤" !insertmacro LANG_STRING Section_Data_Desc "安装皮肤" !insertmacro LANG_STRING Section_Shortcuts "快捷方式图标" diff --git a/build_scripts/Windows/installer/retroshare-Qt5.nsi b/build_scripts/Windows/installer/retroshare-Qt5.nsi index 99a3c7804..ba4f3d0d2 100644 --- a/build_scripts/Windows/installer/retroshare-Qt5.nsi +++ b/build_scripts/Windows/installer/retroshare-Qt5.nsi @@ -22,6 +22,9 @@ !ifndef MINGWDIR !error "MINGWDIR is not defined" !endif +!ifndef ARCHITECTURE +!error "Architecture is not defined" +!endif # Check optional defines !ifdef OUTDIR @@ -53,7 +56,17 @@ !endif # Date -!define /date Date "%Y%m%d" +!ifndef DATE +!define /date DATE "%Y%m%d" +!endif + +# Tor +!ifdef TORDIR +${!defineifexist} TOR_EXISTS "${TORDIR}\tor.exe" +!ifndef TOR_EXISTS +!error "tor.exe not found" +!endif +!endif # Application name and version !define APPNAME "RetroShare" @@ -61,7 +74,12 @@ !define PUBLISHER "RetroShare Team" # Install path -!define INSTDIR_NORMAL "$ProgramFiles\${APPNAME}" +!if ${ARCHITECTURE} == "x86" + !define INSTDIR_NORMAL "$ProgramFiles32\${APPNAME}" +!endif +!if ${ARCHITECTURE} == "x64" + !define INSTDIR_NORMAL "$ProgramFiles64\${APPNAME}" +!endif !define INSTDIR_PORTABLE "$Desktop\${APPNAME}" !define DATADIR_NORMAL "$APPDATA\${APPNAME}" @@ -70,7 +88,7 @@ # Main Install settings Name "${APPNAMEANDVERSION}" InstallDirRegKey HKLM "Software\${APPNAME}" "" -OutFile "${OUTDIR_}RetroShare-${VERSION}-${Date}-${REVISION}-Qt-${QTVERSION}${INSTALLERADD}-setup.exe" +OutFile "${OUTDIR_}RetroShare-${VERSION}-${DATE}-${REVISION}-Qt-${QTVERSION}-${ARCHITECTURE}${INSTALLERADD}-setup.exe" BrandingText "${APPNAMEANDVERSION}" RequestExecutionlevel highest # Use compression @@ -104,7 +122,7 @@ Var StyleSheetDir !define MUI_FINISHPAGE_RUN "$INSTDIR\retroshare.exe" !define MUI_FINISHPAGE_SHOWREADME $INSTDIR\changelog.txt !define MUI_FINISHPAGE_SHOWREADME_TEXT changelog.txt -!define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED +;!define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED !define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\orange-uninstall.ico" !define MUI_UNFINISHPAGE_NOAUTOCLOSE ;!define MUI_LANGDLL_REGISTRY_ROOT HKLM @@ -175,7 +193,7 @@ Section $(Section_Main) Section_Main ; Main binaries SetOutPath "$INSTDIR" File /oname=retroshare.exe "${RELEASEDIR}\retroshare-gui\src\release\retroshare.exe" - File /oname=retroshare-nogui.exe "${RELEASEDIR}\retroshare-nogui\src\release\retroshare-nogui.exe" + File /oname=retroshare-service.exe "${RELEASEDIR}\retroshare-service\src\release\retroshare-service.exe" ; Qt binaries File "${QTDIR}\bin\Qt5Core.dll" @@ -202,18 +220,33 @@ Section $(Section_Main) Section_Main ; MinGW binaries SetOutPath "$INSTDIR" File "${MINGWDIR}\bin\libstdc++-6.dll" - File "${MINGWDIR}\bin\libgcc_s_dw2-1.dll" + !if ${ARCHITECTURE} == "x86" + File "${MINGWDIR}\bin\libgcc_s_dw2-1.dll" + !endif + !if ${ARCHITECTURE} == "x64" + File "${MINGWDIR}\bin\libgcc_s_seh-1.dll" + !endif File "${MINGWDIR}\bin\libwinpthread-1.dll" ; External binaries File "${EXTERNAL_LIB_DIR}\bin\miniupnpc.dll" - File "${EXTERNAL_LIB_DIR}\bin\libeay32.dll" - File "${EXTERNAL_LIB_DIR}\bin\ssleay32.dll" + !if ${ARCHITECTURE} == "x86" + File "${EXTERNAL_LIB_DIR}\bin\libcrypto-1_1.dll" + File "${EXTERNAL_LIB_DIR}\bin\libssl-1_1.dll" + !endif + !if ${ARCHITECTURE} == "x64" + File "${EXTERNAL_LIB_DIR}\bin\libcrypto-1_1-x64.dll" + File "${EXTERNAL_LIB_DIR}\bin\libssl-1_1-x64.dll" + !endif ; Other files - File "${SOURCEDIR}\retroshare-gui\src\changelog.txt" + File "${RELEASEDIR}\changelog.txt" File "${SOURCEDIR}\libbitdht\src\bitdht\bdboot.txt" + ; License + SetOutPath "$INSTDIR\license" + File "${SOURCEDIR}\retroshare-gui\src\license\*.*" + ; Image formats SetOutPath "$INSTDIR\imageformats" File /r "${QTDIR}\plugins\imageformats\qgif.dll" @@ -236,19 +269,26 @@ Section $(Section_Main) Section_Main File /r "${QTDIR}\translations\qt_*.qm" File /r "${QTDIR}\translations\qtbase_*.qm" File /r "${QTDIR}\translations\qtscript_*.qm" - File /r "${QTDIR}\translations\qtquick1_*.qm" File /r "${QTDIR}\translations\qtmultimedia_*.qm" File /r "${QTDIR}\translations\qtxmlpatterns_*.qm" ; WebUI - SetOutPath "$INSTDIR\webui" - File /r "${SOURCEDIR}\libresapi\src\webui\*.*" +; SetOutPath "$INSTDIR\webui" +; File /r "${SOURCEDIR}\libresapi\src\webui\*.*" ; License SetOutPath "$INSTDIR\license" File /r "${SOURCEDIR}\retroshare-gui\src\license\*.*" SectionEnd +# Tor +!ifdef TOR_EXISTS + Section /o $(Section_Tor) Section_Tor + SetOutPath "$INSTDIR" + File /r "${TORDIR}\*" + SectionEnd +!endif + # Plugins ${!defineifexist} PLUGIN_FEEDREADER_EXISTS "${RELEASEDIR}\plugins\FeedReader\release\FeedReader.dll" ${!defineifexist} PLUGIN_VOIP_EXISTS "${RELEASEDIR}\plugins\VOIP\release\VOIP.dll" @@ -363,6 +403,7 @@ SectionEnd !insertmacro MUI_DESCRIPTION_TEXT ${Section_Plugin_VOIP} $(Section_Plugin_VOIP_Desc) ; !insertmacro MUI_DESCRIPTION_TEXT ${Section_Link} $(Section_Link_Desc) !insertmacro MUI_DESCRIPTION_TEXT ${Section_AutoStart} $(Section_AutoStart_Desc) + !insertmacro MUI_DESCRIPTION_TEXT ${Section_Tor} $(Section_Tor_Desc) !insertmacro MUI_FUNCTION_DESCRIPTION_END # Uninstall diff --git a/build_scripts/Windows/qt-cmd.bat b/build_scripts/Windows/qt-cmd.bat deleted file mode 100644 index c0ac6ecbd..000000000 --- a/build_scripts/Windows/qt-cmd.bat +++ /dev/null @@ -1,27 +0,0 @@ -:: Usage: -:: call qt-cmd.bat [command] - -@echo off - -setlocal - -set QtVersion=%~1 - -:: Initialize environment -call "%~dp0env.bat" -if errorlevel 1 goto error_env -call "%EnvPath%\env-qt.bat" %QtVersion% -if errorlevel 1 goto error_env - -if "%~2"=="" ( - "%ComSpec%" -) else ( - "%ComSpec%" /c %2 %3 %4 %5 %6 %7 %8 %9 -) - -exit /B %ERRORLEVEL% - -:error_env -echo Failed to initialize environment. -endlocal -exit /B 1 diff --git a/build_scripts/Windows/tools/download-file-wildcard.bat.bat b/build_scripts/Windows/tools/download-file-wildcard.bat.bat deleted file mode 100644 index 90015baf7..000000000 --- a/build_scripts/Windows/tools/download-file-wildcard.bat.bat +++ /dev/null @@ -1,46 +0,0 @@ -:: Usage: -:: call download-file-wildcard.bat url file-wildcard download-path variable - -if "%~4"=="" ( - echo. - echo Parameter error. - exit /B 1 -) - -if "%EnvTempPath%"=="" ( - echo. - echo Environment error. - exit /B 1 -) - -setlocal - -set Url=%~1 -set FileWildcard=%~2 -set DownloadPath=%~3 -set Var=%~4 -set File= - -call "%~dp0remove-dir.bat" "%EnvTempPath%" -mkdir "%EnvTempPath%" - -"%EnvWgetExe%" --recursive --continue --no-directories --no-parent -A "%FileWildcard%" --directory-prefix="%EnvTempPath%" "%Url%" - -if errorlevel 1 ( - call "%~dp0remove-dir.bat" "%EnvTempPath%" - endlocal & set %Var%= - exit /B %ERRORLEVEL% -) - -for %%A in (%EnvTempPath%\%FileWildcard%) do set File=%%~nxA -if "%File%"=="" ( - call "%~dp0remove-dir.bat" "%EnvTempPath%" - endlocal & set %Var%= - exit /B %ERRORLEVEL% -) - -move "%EnvTempPath%\%File%" "%DownloadPath%" -call "%~dp0remove-dir.bat" "%EnvTempPath%" - -endlocal & set %Var%=%File% -exit /B 0 diff --git a/build_scripts/Windows/tools/download-file.bat b/build_scripts/Windows/tools/download-file.bat index 62720c393..dbcecdcbb 100644 --- a/build_scripts/Windows/tools/download-file.bat +++ b/build_scripts/Windows/tools/download-file.bat @@ -7,7 +7,6 @@ if "%~2"=="" ( exit /B 1 ) -::"%EnvCurlExe%" -L -k "%~1" -o "%~2" -"%EnvWgetExe%" --no-check-certificate --continue "%~1" --output-document="%~2" +powershell -NoLogo -NoProfile -Command (New-Object System.Net.WebClient).DownloadFile(\""%~1\"", \""%~2\"") exit /B %ERRORLEVEL% diff --git a/build_scripts/Windows/tools/generate-changelog.bat b/build_scripts/Windows/tools/generate-changelog.bat new file mode 100644 index 000000000..c38225045 --- /dev/null +++ b/build_scripts/Windows/tools/generate-changelog.bat @@ -0,0 +1,50 @@ +@echo off +setlocal enabledelayedexpansion + +if "%~2"=="" ( + echo. + echo Parameter error. + echo Usage %~n0 sourcepath outputfile + exit /B 1 +) + +:: Check git executable +set GitPath= +call "%~dp0find-in-path.bat" GitPath git.exe +if "%GitPath%"=="" echo Git executable not found in PATH.& exit /B 1 + +set logfile=%~2 +copy nul %logfile% > nul + +pushd %~1 + +set last=HEAD +for /f %%t in ('git tag --sort=-taggerdate --merged ^| findstr v') do ( + echo generating changelog for !last!..%%t + echo ----------------------------------------------- >> %logfile% + if !last! neq HEAD ( + git tag -n !last! >> %logfile% + ) else ( + echo HEAD >> %logfile% + ) + rem echo !last! ---^> %%t >> %logfile% + echo ----------------------------------------------- >> %logfile% + echo. >> %logfile% + git log %%t..!last! --no-merges "--pretty=format:%%h %%ai %%<(10,trunc)%%an %%s" >> %logfile% + echo. >> %logfile% + echo. >> %logfile% + set last=%%t +) + +echo generating changelog for %last% +echo ----------------------------------------------- >> %logfile% +git tag -n %last% >> %logfile% +echo ----------------------------------------------- >> %logfile% +echo. >> %logfile% +git log %last% --no-merges "--pretty=format:%%h %%ai %%<(10,trunc)%%an %%s" >> %logfile% + +popd + +endlocal enabledelayedexpansion + +exit /B 0 diff --git a/build_scripts/Windows/tools/get-gcc-version.bat b/build_scripts/Windows/tools/get-gcc-version.bat index c9998376b..3977477b7 100644 --- a/build_scripts/Windows/tools/get-gcc-version.bat +++ b/build_scripts/Windows/tools/get-gcc-version.bat @@ -1,42 +1,37 @@ :: Usage: -:: call get-gcc-version.bat variable +:: call get-gcc-version.bat version architecture setlocal -set Var=%~1 -if "%Var%"=="" ( +set VarVersion=%~1 +if "%VarVersion%"=="" ( + echo. + echo Parameter error. + exit /B 1 +) + +set VarArchitecture=%~2 +if "%VarArchitecture%"=="" ( echo. echo Parameter error. exit /B 1 ) set GCCVersion= +set GCCArchitecture= +set _Architecture= call "%~dp0find-in-path.bat" GCCPath gcc.exe if "%GCCPath%"=="" ( echo. echo Cannot find gcc.exe in PATH. - goto exit + exit /B 1 ) -gcc --version >"%~dp0gccversion.tmp" -for /F "tokens=1*" %%A in (%~sdp0gccversion.tmp) do ( - if "%%A"=="gcc" ( - call :find_version %%B - goto exit - ) -) +for /F "tokens=1-8* delims= " %%A in ('gcc --version') do if "%%A"=="gcc" set _Architecture=%%B& set GCCVersion=%%G -:exit -if exist "%~dp0gccversion.tmp" del /Q "%~dp0gccversion.tmp" +if "%_Architecture:~1,4%"=="i686" set GCCArchitecture=x86 +if "%_Architecture:~1,6%"=="x86_64" set GCCArchitecture=x64 -endlocal & set %Var%=%GCCVersion% -goto :EOF - -:find_version -:loop -if "%2" NEQ "" ( - shift - goto loop -) -set GCCVersion=%1 +endlocal & set %VarVersion%=%GCCVersion%& set %VarArchitecture%=%GCCArchitecture% +exit /B 0 diff --git a/build_scripts/Windows/tools/get-git-ref.bat b/build_scripts/Windows/tools/get-git-ref.bat index f8f6efdeb..f0047e486 100644 --- a/build_scripts/Windows/tools/get-git-ref.bat +++ b/build_scripts/Windows/tools/get-git-ref.bat @@ -18,7 +18,7 @@ call "%~dp0find-in-path.bat" GitPath git.exe if "%GitPath%"=="" ( echo. echo Git executable not found in PATH. - goto exit + exit /B 1 ) set GitParameter= diff --git a/build_scripts/Windows/tools/get-qt-version.bat b/build_scripts/Windows/tools/get-qt-version.bat index c49939970..73afedcfc 100644 --- a/build_scripts/Windows/tools/get-qt-version.bat +++ b/build_scripts/Windows/tools/get-qt-version.bat @@ -16,19 +16,10 @@ call "%~dp0find-in-path.bat" QMakePath qmake.exe if "%QMakePath%"=="" ( echo. echo Cannot find qmake.exe in PATH. - goto exit + exit /B 1 ) -qmake.exe -version >"%~dp0qtversion.tmp" -for /F "tokens=1,2,3,4" %%A in (%~sdp0qtversion.tmp) do ( - if "%%A"=="Using" ( - set QtVersion=%%D - goto exit - ) -) - -:exit -if exist "%~dp0qtversion.tmp" del /Q "%~dp0qtversion.tmp" +for /F "tokens=1,2,3,4 delims= " %%A in ('qmake.exe -version') do if "%%A"=="Using" set QtVersion=%%D endlocal & set %Var%=%QtVersion% exit /B 0 \ No newline at end of file diff --git a/build_scripts/Windows/tools/get-rs-date.bat b/build_scripts/Windows/tools/get-rs-date.bat new file mode 100644 index 000000000..6fecc48f9 --- /dev/null +++ b/build_scripts/Windows/tools/get-rs-date.bat @@ -0,0 +1,32 @@ +REM Usage: +REM call get-rs-date.bat SourcePath Variable + +setlocal + +set SourcePath=%~1 +set Variable=%~2 +if "%Variable%"=="" ( + echo. + echo Parameter error + exit /B 1 +) + +:: Check git executable +set GitPath= +call "%~dp0find-in-path.bat" GitPath git.exe +if "%GitPath%"=="" ( + echo. + echo Git executable not found in PATH. + exit /B 1 +) + +set Date= + +pushd "%SourcePath%" +rem This doesn't work: git log -1 --date=format:"%Y%m%d" --format="%ad" +for /F "tokens=1,2,3* delims=-" %%A in ('git log -1 --date^=short --format^="%%ad"') do set Date=%%A%%B%%C +popd + +:exit +endlocal & set %Variable%=%Date% +exit /B 0 diff --git a/build_scripts/Windows/tools/get-rs-version.bat b/build_scripts/Windows/tools/get-rs-version.bat index 2398baf93..cc4e53ff8 100644 --- a/build_scripts/Windows/tools/get-rs-version.bat +++ b/build_scripts/Windows/tools/get-rs-version.bat @@ -27,7 +27,7 @@ set VersionMinor= set VersionMini= set VersionExtra= -for /F "tokens=1,2,3,* delims=.-" %%A in ('%EnvToolsPath%\sigcheck.exe -nobanner -n %Executable%') do ( +for /F "USEBACKQ tokens=1,2,3,* delims=.-" %%A in (`powershell -NoLogo -NoProfile -Command ^(Get-Item "%Executable%"^).VersionInfo.FileVersion`) do ( set VersionMajor=%%A set VersionMinor=%%B set VersionMini=%%C diff --git a/build_scripts/Windows/tools/msys-path.bat b/build_scripts/Windows/tools/msys-path.bat deleted file mode 100644 index 3b0c5fd69..000000000 --- a/build_scripts/Windows/tools/msys-path.bat +++ /dev/null @@ -1,20 +0,0 @@ -:: Usage: -:: call msys-path.bat path variable - -setlocal - -set WinPath=%~1 -set MSYSVar=%~2 - -if "%MSYSVar%"=="" ( - echo. - echo Parameter error. - exit /B 1 -) - -set MSYSPath=/%WinPath:~0,1%/%WinPath:~3% -set MSYSPath=%MSYSPath:\=/% - -endlocal & set %MSYSVar%=%MSYSPath% - -exit /B 0 diff --git a/build_scripts/Windows/tools/msys2-path.bat b/build_scripts/Windows/tools/msys2-path.bat new file mode 100644 index 000000000..9904417f6 --- /dev/null +++ b/build_scripts/Windows/tools/msys2-path.bat @@ -0,0 +1,20 @@ +:: Usage: +:: call msys2-path.bat path variable + +setlocal + +set WinPath=%~1 +set MSYS2Var=%~2 + +if "%MSYS2Var%"=="" ( + echo. + echo Parameter error. + exit /B 1 +) + +set MSYS2Path=/%WinPath:~0,1%/%WinPath:~3% +set MSYS2Path=%MSYS2Path:\=/% + +endlocal & set %MSYS2Var%=%MSYS2Path% + +exit /B 0 diff --git a/build_scripts/Windows/tools/winhttpjs.bat b/build_scripts/Windows/tools/winhttpjs.bat deleted file mode 100644 index cdea152eb..000000000 --- a/build_scripts/Windows/tools/winhttpjs.bat +++ /dev/null @@ -1,584 +0,0 @@ -@if (@X) == (@Y) @end /* JScript comment - @echo off - - rem :: the first argument is the script name as it will be used for proper help message - cscript //E:JScript //nologo "%~f0" "%~nx0" %* - - exit /b %errorlevel% - -@if (@X)==(@Y) @end JScript comment */ - -// used resources - -// update 12.10.15 -// osvikvi(https://github.com/osvikvi) has nodited that the -password option is not set , so this is fixed - -//https://msdn.microsoft.com/en-us/library/windows/desktop/aa384058(v=vs.85).aspx -//https://msdn.microsoft.com/en-us/library/windows/desktop/aa384055(v=vs.85).aspx -//https://msdn.microsoft.com/en-us/library/windows/desktop/aa384059(v=vs.85).aspx - -// global variables and constants - - -// ---------------------------------- -// -- asynch requests not included -- -// ---------------------------------- - - -//todo - save responceStream instead of responceBody !! -//todo - set all winthttp options ->//https://msdn.microsoft.com/en-us/library/windows/desktop/aa384108(v=vs.85).aspx -//todo - log all options -//todo - improve help message . eventual verbose option - - -var ARGS = WScript.Arguments; -var scriptName = ARGS.Item(0); - -var url = ""; -var saveTo = ""; - -var user = 0; -var pass = 0; - -var proxy = 0; -var bypass = 0; -var proxy_user = 0; -var proxy_pass = 0; - -var certificate = 0; - -var force = true; - -var body = ""; - -//ActiveX objects -var WinHTTPObj = new ActiveXObject("WinHttp.WinHttpRequest.5.1"); -var FileSystemObj = new ActiveXObject("Scripting.FileSystemObject"); -var AdoDBObj = new ActiveXObject("ADODB.Stream"); - -// HttpRequest SetCredentials flags. -var proxy_settings = 0; - -// -HTTPREQUEST_SETCREDENTIALS_FOR_SERVER = 0; -HTTPREQUEST_SETCREDENTIALS_FOR_PROXY = 1; - -//timeouts and their default values -var RESOLVE_TIMEOUT = 0; -var CONNECT_TIMEOUT = 90000; -var SEND_TIMEOUT = 90000; -var RECEIVE_TIMEOUT = 90000; - -//HttpRequestMethod -var http_method = 'GET'; - -//header -var header_file = ""; - -//report -var reportfile = ""; - -//test-this: -var use_stream = false; - -//autologon policy -var autologon_policy = 1; //0,1,2 - - -//headers will be stored as multi-dimensional array -var headers = []; - -//user-agent -var ua = ""; - -//escape URL -var escape = false; - -function printHelp() { - WScript.Echo(scriptName + " - sends HTTP request and saves the request body as a file and/or a report of the sent request"); - WScript.Echo(scriptName + " url [-force yes|no] [-user username -password password] [-proxy proxyserver:port] [-bypass bypass_list]"); - WScript.Echo(" [-proxyuser proxy_username -proxypassword proxy_password] [-certificate certificateString]"); - WScript.Echo(" [-method GET|POST|PATCH|DELETE|HEAD|OPTIONS|CONNECT]"); - WScript.Echo(" [-saveTo file] - to print response to console use con"); - - WScript.Echo(" [-sendTimeout int(milliseconds)]"); - WScript.Echo(" [-resolveTimeout int(milliseconds)]"); - WScript.Echo(" [-connectTimeout int(milliseconds)]"); - WScript.Echo(" [-receiveTimeout int(milliseconds)]"); - - WScript.Echo(" [-autologonPolicy 1|2|3]"); - WScript.Echo(" [-proxySettings 1|2|3] (https://msdn.microsoft.com/en-us/library/windows/desktop/aa384059(v=vs.85).aspx)"); - - //header - WScript.Echo(" [-headers-file header_file]"); - //reportfile - WScript.Echo(" [-reportfile reportfile]"); - WScript.Echo(" [-ua user-agent]"); - WScript.Echo(" [-ua-file user-agent-file]"); - - WScript.Echo(" [-escape yes|no]"); - - WScript.Echo(" [-body body-string]"); - WScript.Echo(" [-body-file body-file]"); - - WScript.Echo("-force - decide to not or to overwrite if the local files exists"); - - WScript.Echo("proxyserver:port - the proxy server"); - WScript.Echo("bypass- bypass list"); - WScript.Echo("proxy_user , proxy_password - credentials for proxy server"); - WScript.Echo("user , password - credentials for the server"); - WScript.Echo("certificate - location of SSL certificate"); - WScript.Echo("method - what HTTP method will be used.Default is GET"); - WScript.Echo("saveTo - save the responce as binary file"); - WScript.Echo(" "); - WScript.Echo("Header file should contain key:value pairs.Lines starting with \"#\" will be ignored."); - WScript.Echo("value should NOT be enclosed with quotes"); - WScript.Echo(" "); - WScript.Echo("Examples:"); - - WScript.Echo(scriptName + " http://somelink.com/somefile.zip -saveTo c:\\somefile.zip -certificate \"LOCAL_MACHINE\\Personal\\My Middle-Tier Certificate\""); - WScript.Echo(scriptName + " http://somelink.com/something.html -method POST -certificate \"LOCAL_MACHINE\\Personal\\My Middle-Tier Certificate\" -header c:\\header_file -reportfile c:\\reportfile.txt"); - WScript.Echo(scriptName + "\"http://somelink\" -method POST -header hdrs.txt -reportfile reportfile2.txt -saveTo responsefile2 -ua \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36\" -body-file some.json"); - -} - -function parseArgs() { - // - if (ARGS.Length < 2) { - WScript.Echo("insufficient arguments"); - printHelp(); - WScript.Quit(43); - } - // !!! - url = ARGS.Item(1); - // !!! - if (ARGS.Length % 2 != 0) { - WScript.Echo("illegal arguments"); - printHelp(); - WScript.Quit(44); - } - - for (var i = 2; i < ARGS.Length - 1; i = i + 2) { - var arg = ARGS.Item(i).toLowerCase(); - var next = ARGS.Item(i + 1); - - - try { - switch (arg) { // the try-catch is set mainly because of the parseInts - case "-force": - if (next == "no") { - force = false; - } - break; - case "-escape": - if (next == "yes") { - escape = true; - } - break; - case "-saveto": - saveTo = next; - break; - case "-user": - case "-u": - user = next; - break; - case "-pass": - case "-password": - case "-p": - pass = next; - break; - case "-proxy": - proxy = next; - break; - case "-bypass": - bypass = next; - break; - case "-proxyuser": - case "-pu": - proxy_user = next; - break; - case "-proxypassword": - case "-pp": - proxy_pass = next; - break; - case "-ua": - ua = next; - break; - case "-ua-file": - ua = readFile(next); - break; - case "-body": - body = next; - break; - case "-usestream": - //WScript.Echo("~~"); - if (next.toLowerCase() === "yes") { - use_stream = true - }; - break; - case "-body-file": - body = readFile(next); - break; - case "-certificate": - certificate = next; - break; - case "-method": - switch (next.toLowerCase()) { - case "post": - http_method = 'POST'; - break; - case "get": - http_method = 'GET'; - break; - case "head": - http_method = 'HEAD'; - break; - case "put": - http_method = 'PUT'; - break; - case "options": - http_method = 'OPTIONS'; - break; - case "connect": - http_method = 'CONNECT'; - break; - case "patch": - http_method = 'PATCH'; - break; - case "delete": - http_method = 'DELETE'; - break; - default: - WScript.Echo("Invalid http method passed " + next); - WScript.Echo("possible values are GET,POST,DELETE,PUT,CONNECT,PATCH,HEAD,OPTIONS"); - WScript.Quit(1326); - break; - } - break; - case "-headers-file": - case "-header": - headers = readPropFile(next); - break; - case "-reportfile": - reportfile = next; - break; - //timeouts - case "-sendtimeout": - SEND_TIMEOUT = parseInt(next); - break; - case "-connecttimeout": - CONNECT_TIMEOUT = parseint(next); - break; - case "-resolvetimeout": - RESOLVE_TIMEOUT = parseInt(next); - break; - case "-receivetimeout": - RECEIVE_TIMEOUT = parseInt(next); - break; - - case "-autologonpolicy": - autologon_policy = parseInt(next); - if (autologon_policy > 2 || autologon_policy < 0) { - WScript.Echo("out of autologon policy range"); - WScript.Quit(87); - }; - break; - case "-proxysettings": - proxy_settings = parseInt(next); - if (proxy_settings > 2 || proxy_settings < 0) { - WScript.Echo("out of proxy settings range"); - WScript.Quit(87); - }; - break; - default: - WScript.Echo("Invalid command line switch: " + arg); - WScript.Quit(1405); - break; - } - } catch (err) { - WScript.Echo(err.message); - WScript.Quit(1348); - } - } -} - -stripTrailingSlash = function(path) { - while (path.substr(path.length - 1, path.length) == '\\') { - path = path.substr(0, path.length - 1); - } - return path; -} - -function existsItem(path) { - return FileSystemObj.FolderExists(path) || FileSystemObj.FileExists(path); -} - -function deleteItem(path) { - if (FileSystemObj.FileExists(path)) { - FileSystemObj.DeleteFile(path); - return true; - } else if (FileSystemObj.FolderExists(path)) { - FileSystemObj.DeleteFolder(stripTrailingSlash(path)); - return true; - } else { - return false; - } -} - -//------------------------------- -//---------------------- -//---------- -//----- -//-- -function request(url) { - - try { - - WinHTTPObj.Open(http_method, url, false); - if (proxy != 0 && bypass != 0) { - WinHTTPObj.SetProxy(proxy_settings, proxy, bypass); - } - - if (proxy != 0) { - WinHTTPObj.SetProxy(proxy_settings, proxy); - } - - if (user != 0 && pass != 0) { - WinHTTPObj.SetCredentials(user, pass, HTTPREQUEST_SETCREDENTIALS_FOR_SERVER); - } - - if (proxy_user != 0 && proxy_pass != 0) { - WinHTTPObj.SetCredentials(proxy_user, proxy_pass, HTTPREQUEST_SETCREDENTIALS_FOR_PROXY); - } - - if (certificate != 0) { - WinHTTPObj.SetClientCertificate(certificate); - } - - //set autologin policy - WinHTTPObj.SetAutoLogonPolicy(autologon_policy); - //set timeouts - WinHTTPObj.SetTimeouts(RESOLVE_TIMEOUT, CONNECT_TIMEOUT, SEND_TIMEOUT, RECEIVE_TIMEOUT); - - if (headers.length !== 0) { - WScript.Echo("Sending with headers:"); - for (var i = 0; i < headers.length; i++) { - WinHTTPObj.SetRequestHeader(headers[i][0], headers[i][1]); - WScript.Echo(headers[i][0] + ":" + headers[i][1]); - } - WScript.Echo(""); - } - - if (ua !== "") { - //user-agent option from: - //WinHttpRequestOption enumeration - // other options can be added like bellow - //https://msdn.microsoft.com/en-us/library/windows/desktop/aa384108(v=vs.85).aspx - WinHTTPObj.Option(0) = ua; - } - if (escape) { - WinHTTPObj.Option(3) = true; - } - if (trim(body) === "") { - WinHTTPObj.Send(); - } else { - WinHTTPObj.Send(body); - } - - var status = WinHTTPObj.Status - } catch (err) { - WScript.Echo(err.message); - WScript.Quit(666); - } - - //////////////////////// - // report // - //////////////////////// - - if (reportfile != "") { - - //var report_string=""; - var n = "\r\n"; - var report_string = "Status:" + n; - report_string = report_string + " " + WinHTTPObj.Status; - report_string = report_string + " " + WinHTTPObj.StatusText + n; - report_string = report_string + " " + n; - report_string = report_string + "Response:" + n; - report_string = report_string + WinHTTPObj.ResponseText + n; - report_string = report_string + " " + n; - report_string = report_string + "Headers:" + n; - report_string = report_string + WinHTTPObj.GetAllResponseHeaders() + n; - - WinHttpRequestOption_UserAgentString = 0; // Name of the user agent - WinHttpRequestOption_URL = 1; // Current URL - WinHttpRequestOption_URLCodePage = 2; // Code page - WinHttpRequestOption_EscapePercentInURL = 3; // Convert percents - // in the URL - // rest of the options can be seen and eventually added using this as reference - // https://msdn.microsoft.com/en-us/library/windows/desktop/aa384108(v=vs.85).aspx - - report_string = report_string + "URL:" + n; - report_string = report_string + WinHTTPObj.Option(WinHttpRequestOption_URL) + n; - - report_string = report_string + "URL Code Page:" + n; - report_string = report_string + WinHTTPObj.Option(WinHttpRequestOption_URLCodePage) + n; - - report_string = report_string + "User Agent:" + n; - report_string = report_string + WinHTTPObj.Option(WinHttpRequestOption_UserAgentString) + n; - - report_string = report_string + "Escapped URL:" + n; - report_string = report_string + WinHTTPObj.Option(WinHttpRequestOption_EscapePercentInURL) + n; - - prepareateFile(force, reportfile); - - WScript.Echo("Writing report to " + reportfile); - - writeFile(reportfile, report_string); - - } - - switch (status) { - case 200: - WScript.Echo("Status: 200 OK"); - break; - default: - WScript.Echo("Status: " + status); - WScript.Echo("Status was not OK. More info -> https://en.wikipedia.org/wiki/List_of_HTTP_status_codes"); - } - - //if as binary - if (saveTo.toLowerCase() === "con") { - WScript.Echo(WinHTTPObj.ResponseText); - } - if (saveTo !== "" && saveTo.toLowerCase() !== "con") { - prepareateFile(force, saveTo); - try { - - if (use_stream) { - writeBinFile(saveTo, WinHTTPObj.ResponseStream); - } else { - writeBinFile(saveTo, WinHTTPObj.ResponseBody); - } - - } catch (err) { - WScript.Echo("Failed to save the file as binary.Attempt to save it as text"); - AdoDBObj.Close(); - prepareateFile(true, saveTo); - writeFile(saveTo, WinHTTPObj.ResponseText); - } - } - WScript.Quit(status); -} - -//-- -//----- -//---------- -//---------------------- -//------------------------------- - -function prepareateFile(force, file) { - if (force && existsItem(file)) { - if (!deleteItem(file)) { - WScript.Echo("Unable to delete " + file); - WScript.Quit(8); - } - } else if (existsItem(file)) { - WScript.Echo("Item " + file + " already exist"); - WScript.Quit(9); - } -} - -function writeBinFile(fileName, data) { - AdoDBObj.Type = 1; - AdoDBObj.Open(); - AdoDBObj.Position = 0; - AdoDBObj.Write(data); - AdoDBObj.SaveToFile(fileName, 2); - AdoDBObj.Close(); -} - -function writeFile(fileName, data) { - AdoDBObj.Type = 2; - AdoDBObj.CharSet = "iso-8859-1"; - AdoDBObj.Open(); - AdoDBObj.Position = 0; - AdoDBObj.WriteText(data); - AdoDBObj.SaveToFile(fileName, 2); - AdoDBObj.Close(); -} - - -function readFile(fileName) { - //check existence - try { - if (!FileSystemObj.FileExists(fileName)) { - WScript.Echo("file " + fileName + " does not exist!"); - WScript.Quit(13); - } - if (FileSystemObj.GetFile(fileName).Size === 0) { - return ""; - } - var fileR = FileSystemObj.OpenTextFile(fileName, 1); - var content = fileR.ReadAll(); - fileR.Close(); - return content; - } catch (err) { - WScript.Echo("Error while reading file: " + fileName); - WScript.Echo(err.message); - WScript.Echo("Will return empty string"); - return ""; - } -} - -function readPropFile(fileName) { - //check existence - resultArray = []; - if (!FileSystemObj.FileExists(fileName)) { - WScript.Echo("(headers)file " + fileName + " does not exist!"); - WScript.Quit(15); - } - if (FileSystemObj.GetFile(fileName).Size === 0) { - return resultArray; - } - var fileR = FileSystemObj.OpenTextFile(fileName, 1); - var line = ""; - var k = ""; - var v = ""; - var lineN = 0; - var index = 0; - try { - WScript.Echo("parsing headers form " + fileName + " property file "); - while (!fileR.AtEndOfStream) { - line = fileR.ReadLine(); - lineN++; - index = line.indexOf(":"); - if (line.indexOf("#") === 0 || trim(line) === "") { - continue; - } - if (index === -1 || index === line.length - 1 || index === 0) { - WScript.Echo("Invalid line " + lineN); - WScript.Quit(93); - } - k = trim(line.substring(0, index)); - v = trim(line.substring(index + 1, line.length)); - resultArray.push([k, v]); - } - fileR.Close(); - return resultArray; - } catch (err) { - WScript.Echo("Error while reading headers file: " + fileName); - WScript.Echo(err.message); - WScript.Echo("Will return empty array"); - return resultArray; - } -} - -function trim(str) { - return str.replace(/^\s+/, '').replace(/\s+$/, ''); -} - -function main() { - parseArgs(); - request(url); -} -main(); diff --git a/jsonapi-generator/src/async-method-wrapper-template.cpp.tmpl b/jsonapi-generator/src/async-method-wrapper-template.cpp.tmpl index 16bfe3304..29f53ad48 100644 --- a/jsonapi-generator/src/async-method-wrapper-template.cpp.tmpl +++ b/jsonapi-generator/src/async-method-wrapper-template.cpp.tmpl @@ -44,7 +44,7 @@ $%inputParamsDeserialization%$ const std::weak_ptr weakService(mService); const std::weak_ptr weakSession(session); - $%callbackName%$ = [this, weakService, weakSession]($%callbackParams%$) + $%callbackName%$ = [weakService, weakSession]($%callbackParams%$) { auto session = weakSession.lock(); if(!session || session->is_closed()) return; diff --git a/libbitdht/src/bitdht/bdboot.txt b/libbitdht/src/bitdht/bdboot.txt index 26fa614f6..6729c6218 100644 --- a/libbitdht/src/bitdht/bdboot.txt +++ b/libbitdht/src/bitdht/bdboot.txt @@ -1,500 +1,1004 @@ -61.81.86.213 50071 -2.89.235.12 18179 -139.192.128.54 54575 -61.224.74.1 45106 -76.189.15.171 28937 -80.98.74.161 54545 -110.137.230.187 1024 -118.10.139.25 15548 -174.2.19.15 11071 -124.106.250.65 2322 -94.208.10.154 52432 -176.9.35.88 2036 -47.9.158.37 2049 -88.174.58.215 51413 -78.60.4.214 6881 -78.119.213.102 44822 -189.150.157.153 16610 -88.217.31.208 7813 -125.1.181.171 22749 -210.136.215.55 6881 -151.236.7.73 57559 -105.227.47.34 35549 -211.47.87.58 62348 -175.152.60.168 16138 -85.195.226.115 5888 -46.191.196.157 49001 -118.200.194.97 6881 -151.70.147.15 58783 -95.150.185.164 6882 -163.44.160.81 6881 -88.149.179.95 6881 -121.176.181.60 58387 -52.40.120.56 6892 -51.175.237.18 15937 -178.204.71.144 48016 -95.31.49.9 9425 -108.196.87.74 24929 -77.37.160.170 1242 -135.0.8.24 57673 -150.242.197.4 39025 -202.231.169.253 21947 -1.36.227.158 9979 -60.130.54.195 26585 -92.39.216.40 23585 -104.208.36.117 50215 -79.253.173.153 58428 -178.45.90.225 26330 -210.139.186.89 22645 -151.49.55.78 51413 -2.32.208.156 51413 -51.255.58.0 51412 -23.88.236.63 51413 -13.69.158.91 45168 -92.134.139.162 10004 -24.75.160.81 53965 -98.202.170.205 41731 -62.76.100.235 22948 -79.100.105.242 14363 -175.156.161.33 22696 101.161.11.165 40232 -134.255.216.133 46771 -195.88.15.161 7743 -59.133.235.34 12525 -105.179.0.54 12806 -175.207.170.224 51967 -174.99.71.36 53440 -109.182.250.11 24255 -126.12.199.252 27499 -176.9.62.3 29342 -158.181.69.37 6881 -153.174.200.3 48948 -122.116.3.18 26552 -5.51.197.239 6881 -93.84.33.191 55762 -78.46.147.99 51413 -101.184.93.144 51413 -85.25.218.207 6888 -37.15.161.58 51413 -84.245.120.26 51413 -37.204.196.34 42929 -92.233.185.151 51413 -212.77.130.215 51413 -41.189.37.70 28295 -128.68.14.3 17098 -124.190.99.84 9648 -91.219.237.220 4087 -90.126.31.47 50000 -109.200.44.44 50321 -79.189.118.198 6881 -118.6.183.223 22360 -24.226.116.194 8547 -69.146.24.229 34202 -62.101.135.20 1029 -82.240.4.144 16729 -157.48.187.25 50321 -24.211.171.122 10349 -78.31.66.180 5307 -94.180.0.78 23879 -74.76.197.221 29154 -88.169.1.120 59258 -73.15.30.22 44905 -123.114.190.10 6881 -151.80.42.103 51413 -80.140.156.164 37500 -71.84.138.32 52296 -95.92.232.20 51413 -81.93.207.145 20963 -218.215.67.196 39700 -115.164.89.228 16588 -80.234.86.220 60386 -31.42.125.83 46470 -220.123.202.121 50427 -176.53.233.37 28697 -88.165.34.61 41708 -104.208.36.117 53133 -95.90.215.112 37742 -78.131.13.232 55633 -37.113.129.221 36551 -52.169.220.77 37717 -73.173.111.92 63666 -223.16.73.77 22303 -211.133.97.200 10467 -122.106.249.6 6881 -136.0.2.148 51413 -5.140.20.202 11609 -78.253.242.10 59410 -61.63.158.180 48321 -79.101.160.169 1453 -42.124.217.110 7789 -174.53.251.142 38400 -114.47.108.52 24637 -61.62.186.193 8442 -87.97.253.69 50649 -62.76.100.235 41047 -23.96.202.54 54203 -91.216.66.81 36678 -96.237.181.232 7915 -178.88.8.9 49001 -92.131.55.187 40332 -149.154.157.153 56789 -124.244.71.126 23173 -106.161.218.95 24577 -111.220.54.30 12861 -186.118.170.98 41824 -128.76.148.62 27027 -171.6.243.206 19401 -94.244.35.192 51413 -93.78.138.118 50567 -79.100.51.243 18404 -85.59.159.50 49001 -46.191.226.4 28344 -178.132.253.156 51413 101.175.156.170 61680 -76.125.33.68 54526 -77.135.115.253 9050 -50.191.160.70 20789 -107.184.38.87 43033 -2.138.139.164 62284 -95.25.229.186 6889 -213.127.73.24 4985 -162.224.188.31 7565 -76.22.35.229 16612 -96.49.182.210 21859 -83.240.93.202 51413 -65.95.73.182 42970 -42.2.41.156 24462 -135.23.129.163 61896 -188.194.127.171 39980 -84.75.22.54 51413 -212.50.134.32 49382 -147.32.203.245 55093 -91.89.196.37 61799 -2.26.160.4 26398 -95.222.150.218 38337 -60.248.95.76 6881 -216.126.213.67 25513 -62.235.217.73 61835 -176.50.116.149 17994 -66.8.165.71 16020 -74.140.153.59 59286 -81.153.207.27 51413 -203.219.80.229 54622 -58.168.124.241 3750 -86.237.133.79 51413 -178.253.199.55 1367 -46.19.85.245 50321 -194.199.26.8 43287 -118.8.19.13 6881 -37.222.221.222 8621 -37.131.222.30 21517 -87.78.153.16 5011 -46.127.246.32 40085 -24.6.186.56 34502 -161.184.231.117 6882 +101.184.93.144 51413 101.186.180.208 51413 -37.49.169.66 28133 -218.252.251.52 17648 -70.48.173.172 3504 -94.23.5.218 6881 -85.64.151.207 44822 -173.164.232.205 51413 -41.246.76.233 42181 -89.27.163.34 55315 -88.24.180.250 6249 -153.179.97.18 16218 -180.129.113.209 48695 -151.21.4.28 53210 -46.191.226.4 59982 -37.204.23.144 44822 -78.149.99.103 22683 -119.51.164.126 8568 -79.160.104.5 2513 -68.59.158.23 39460 -72.176.18.24 6881 -81.23.7.233 9224 -94.30.170.21 7105 -126.57.10.151 22962 -112.186.123.102 51413 -94.236.246.217 15554 -175.160.193.44 51413 -5.198.60.36 49007 -177.239.14.15 23036 -23.102.24.179 37253 -76.103.53.99 40573 -220.239.238.43 51413 -185.34.3.150 6881 -80.98.17.100 51413 +103.197.33.53 14354 +103.27.109.109 6884 +103.50.76.50 11623 +103.51.54.19 59914 +104.208.36.117 50215 +104.208.36.117 53133 +105.104.153.96 56532 +105.179.0.54 12806 +105.227.47.34 35549 +105.236.34.74 43961 +106.161.218.95 24577 +107.184.38.87 43033 +107.185.79.233 10278 +107.9.243.80 24112 +108.160.185.132 31530 +108.196.87.74 24929 +108.242.252.38 39304 +108.254.5.66 58192 +109.11.38.68 61722 +109.124.197.42 31559 +109.130.31.27 11460 109.168.199.53 6881 -213.46.132.81 50321 -31.185.5.4 10733 -188.37.221.51 50321 -5.18.182.29 51653 +109.174.3.81 51413 +109.182.250.11 24255 +109.184.163.146 11789 +109.187.114.190 49001 +109.194.234.36 14342 +109.197.191.45 19604 +109.200.44.44 50321 +109.209.40.17 55542 +109.211.143.205 13722 +109.214.57.82 1042 +109.28.82.4 33620 109.64.128.248 50321 -86.46.36.146 10124 -194.154.216.89 50321 -213.83.135.109 50321 -213.57.219.99 50321 -31.223.216.83 50321 -37.104.125.16 50321 -64.25.21.15 37321 -85.107.174.75 50321 -62.140.137.49 3706 -85.25.237.46 6887 -92.90.21.108 49760 -85.25.218.207 6884 -85.25.237.46 6882 +109.72.144.240 41290 +109.8.154.28 19120 +110.137.230.187 1024 +111.15.147.164 16001 +111.184.247.170 19087 +111.220.54.30 12861 +111.242.54.149 15979 +111.250.149.202 25863 +112.120.144.128 60152 +112.173.11.227 63314 +112.186.123.102 51413 +112.196.147.151 16425 +112.203.171.200 25932 +112.206.13.117 41825 +112.209.16.158 19102 +112.234.202.158 16001 +113.161.204.192 6881 +113.162.247.40 3800 +113.83.226.37 51413 +114.185.184.10 23151 +114.224.40.181 51413 +114.32.184.15 25962 +114.32.211.85 30116 +114.32.76.93 51413 +114.34.239.242 22647 +114.34.46.63 6881 +114.37.154.22 16120 +114.38.14.84 16958 +114.38.21.113 7694 +114.40.109.55 7841 +114.42.47.178 9089 +114.47.108.52 24637 +114.79.138.158 60635 +115.164.89.228 16588 +115.240.98.113 51413 +1.161.107.196 31811 +116.98.141.220 44824 +1.172.161.190 17526 +118.10.139.25 15548 +118.160.199.38 51532 +118.160.199.38 51651 +118.160.199.38 59277 +118.160.199.38 60134 +118.160.199.38 61182 +118.160.199.38 63151 +118.160.199.38 63400 +118.160.199.38 63619 +118.163.92.217 6881 +118.173.219.156 12148 +118.184.27.144 6881 +118.189.195.210 51413 +118.200.194.97 6881 +118.32.175.216 49802 +118.6.183.223 22360 +118.8.19.13 6881 +119.131.41.152 1231 +119.196.44.47 51763 +119.201.103.241 62715 +119.202.178.119 45249 +119.47.150.27 51294 +119.51.164.126 8568 +119.74.54.94 6881 +119.96.130.9 12038 +120.146.228.149 6881 +120.208.30.229 16001 +121.141.14.57 30323 +121.176.181.60 58387 +121.214.57.226 51413 +121.219.232.47 65073 +121.223.47.224 41563 +121.75.175.91 10146 +122.106.249.6 6881 +122.116.3.18 26552 +122.134.166.111 7105 +122.135.89.118 25051 +122.150.64.247 17132 +122.174.156.164 15004 +122.252.197.103 32378 +123.110.104.200 29499 +123.114.190.10 6881 +123.139.45.71 16001 +123.195.214.39 20846 +123.50.62.239 9433 +124.106.250.65 2322 +124.149.151.181 33603 +124.181.64.21 24406 +124.190.99.84 9648 +124.244.71.126 23173 +124.45.37.197 21084 +125.1.181.171 22749 +125.193.100.191 21802 +126.12.199.252 27499 +126.57.10.151 22962 +126.57.182.134 16514 +128.68.106.214 36595 +128.68.14.3 17098 +128.76.148.62 27027 +129.21.122.81 32785 130.193.244.41 15090 -85.25.218.207 6886 -202.62.94.74 39272 +130.43.29.146 52526 +130.89.165.24 10110 +132.147.102.21 1024 +133.130.90.125 6883 +134.236.242.96 1024 +134.255.216.133 46771 +135.0.8.24 57673 +135.23.129.163 57581 +135.23.129.163 61896 +135.23.129.163 61982 +136.0.2.148 51413 +1.36.227.158 9979 +13.69.158.91 45168 137.97.231.32 50321 +138.199.66.212 26174 +13.89.52.33 51409 +139.192.128.54 54575 +141.105.71.92 6881 +14.133.76.62 8771 +14.162.199.2 3800 +142.161.41.171 41788 +142.163.53.182 58144 +142.59.218.136 11300 +147.32.203.245 55093 +149.154.157.153 56789 +150.242.197.4 39025 +151.21.4.28 53210 +151.229.110.52 11163 +151.236.7.73 57559 +151.237.43.164 59133 +151.25.110.226 49289 +151.49.55.78 51413 +151.62.32.183 51413 +151.70.147.15 58783 +151.80.42.103 51413 +153.174.200.3 48948 +153.179.56.36 51413 +153.179.97.18 16218 +157.48.187.25 50321 157.49.171.10 50321 -45.76.69.49 6881 -178.32.216.187 6881 -77.180.216.131 24988 +157.7.109.77 6881 +158.181.69.37 6881 +158.193.227.67 8621 +161.184.231.117 6882 +162.224.188.31 7565 +163.44.160.81 6881 +164.215.87.224 57073 +165.132.236.14 25390 +169.149.196.18 50321 +171.6.243.206 19401 +171.97.173.243 9327 172.10.50.156 51413 172.56.7.143 18972 -89.248.171.137 6881 -185.34.3.103 6881 -185.34.3.20 6881 -185.34.3.172 6881 -68.227.56.45 57278 -122.252.197.103 32378 -112.196.147.151 16425 -90.27.140.90 1347 -176.241.133.130 49001 -23.252.55.56 29633 -36.225.186.145 25184 -14.162.199.2 3800 -62.76.100.235 34251 -42.126.157.252 22033 -95.111.46.227 18466 -68.0.53.95 11464 -213.111.37.228 9858 -37.208.66.86 30260 -91.105.156.125 1025 -116.98.141.220 44824 -183.82.1.44 59818 -41.210.224.40 11953 -114.40.109.55 7841 -51.141.3.234 31256 -81.45.175.39 33652 -62.76.100.235 40077 -80.249.65.131 9800 -94.233.110.175 49001 -61.57.113.156 1028 -92.46.228.108 26675 -46.101.56.83 30264 -87.110.236.117 64810 -62.76.100.235 33748 -91.121.136.132 59001 -61.89.6.203 19474 -23.102.24.179 33961 -78.73.38.9 11423 -40.117.89.72 24427 -52.228.47.105 39376 -67.188.183.70 41082 -62.76.100.235 39415 -92.234.58.147 13285 -62.76.100.235 43085 -213.152.161.101 34261 -124.181.64.21 24406 -103.51.54.19 59914 -62.76.100.235 28212 -69.175.34.187 28566 -27.194.238.27 6881 -194.176.114.54 28436 -77.28.66.239 15514 -113.162.247.40 3800 -92.58.106.183 8621 -61.57.118.157 25147 -90.31.140.85 16185 -126.57.182.134 16514 -219.251.128.233 50506 -52.43.139.55 6992 -89.78.249.54 49001 -90.116.231.10 33026 -5.187.143.52 6889 -124.45.37.197 21084 -39.84.172.237 6881 -190.29.115.127 10006 -86.252.48.255 59566 -27.117.108.178 56753 -60.128.90.71 15510 -128.68.106.214 36595 -92.45.192.16 22222 -135.23.129.163 61982 +172.87.31.56 6884 +173.164.232.205 51413 +173.31.76.104 21757 173.68.196.23 60073 -93.84.33.191 22071 -192.253.252.34 6881 -178.150.91.123 37787 -67.197.217.74 52624 -82.211.130.137 29777 -181.168.56.109 19567 -61.178.110.205 16001 -178.48.170.64 62331 -89.110.53.186 41210 -151.237.43.164 59133 -109.187.114.190 49001 -213.136.83.168 20024 -60.138.177.136 47243 -52.40.120.56 6992 -83.9.248.251 51413 -52.43.139.55 6892 -92.156.86.247 56432 -45.32.254.211 6881 -52.49.71.160 6892 -76.66.171.131 15021 -218.173.174.50 27069 -134.236.242.96 1024 -52.57.20.58 6992 -27.130.112.202 25477 +173.73.46.92 52828 +174.100.51.253 44460 +174.101.153.252 38195 +174.117.49.59 13052 +174.119.150.115 17065 +174.2.19.15 11071 +174.48.201.143 41918 +174.53.251.142 38400 +174.71.20.57 52193 +174.99.71.36 53440 175.138.57.51 18065 -95.31.49.9 9424 -52.40.7.160 6892 -52.57.140.142 6892 +175.151.131.80 16001 +175.152.60.168 16138 +175.156.161.33 22696 +175.156.223.157 22300 +175.160.193.44 51413 +175.207.170.224 51967 +176.104.214.178 59611 +176.108.146.88 6881 +176.212.60.108 60378 +176.214.214.73 64892 +176.214.76.87 7047 +176.226.153.243 61967 +176.241.133.130 49001 +176.31.109.159 24586 +176.31.248.87 6338 +176.49.172.107 49001 +176.50.116.149 17994 +176.53.233.37 28697 +176.9.113.77 61059 +176.9.35.88 2036 +176.9.62.3 29342 +176.97.212.163 22523 +177.239.14.15 23036 +177.32.251.226 27655 +177.5.36.152 12588 +178.120.85.170 24550 +178.122.199.251 16480 +178.132.253.156 51413 +178.137.112.35 14571 +178.140.169.137 22922 +178.148.68.18 53318 +178.150.91.123 37787 +178.150.91.123 8314 +178.168.58.44 23476 +178.185.98.43 12420 +178.204.71.144 26612 +178.204.71.144 48016 +178.219.38.34 1024 +178.221.81.112 59014 +178.253.199.55 1367 +178.32.185.12 6544 +178.32.216.187 6881 +178.35.204.111 6881 +178.36.198.215 22849 +178.44.187.32 52209 +178.45.159.135 31825 +178.45.90.225 26330 +178.48.170.64 62331 +178.72.159.43 47954 +178.88.3.227 33031 +178.88.8.9 49001 +178.89.100.227 6881 +180.129.113.209 48695 +180.183.213.112 11823 +180.18.98.235 29543 +180.64.39.48 14708 +181.168.56.109 19567 +182.177.228.43 31532 +182.210.49.84 11993 +182.53.45.3 14895 +183.109.112.49 58359 +183.82.1.44 59818 +183.89.3.101 10618 +184.145.109.37 31067 +184.146.76.179 13586 +184.147.116.143 49025 +184.166.72.229 63472 +184.18.8.33 8784 +184.41.75.34 57537 +184.89.243.73 43344 +185.157.221.247 25401 +185.21.217.59 37045 +185.34.3.103 6881 +185.34.3.150 6881 +185.34.3.172 6881 +185.34.3.20 6881 +186.118.170.98 41824 +186.254.61.185 40200 +186.4.42.27 20837 +186.54.1.246 38354 +188.121.244.133 10980 +188.142.164.252 43063 +188.164.212.104 28566 +188.168.14.169 1376 +188.176.218.22 35407 +188.186.13.95 21313 +188.187.45.126 43757 +188.194.127.171 39980 +188.224.10.109 46059 +188.231.143.20 6881 +188.232.61.83 6881 +188.235.149.202 63083 +188.25.246.130 16313 +188.37.221.51 50321 +188.49.25.79 46883 +188.72.98.36 15503 +188.77.246.133 35496 +189.150.157.153 16610 +189.5.22.202 52412 +189.54.195.49 50555 +190.29.115.127 10006 +190.60.48.165 41301 +190.88.179.253 20701 +192.253.252.34 6881 +193.160.224.34 29787 +193.253.220.184 6881 +193.77.159.24 43611 +194.154.216.89 50321 +194.176.114.54 28436 +194.199.26.8 43287 +195.154.136.170 55040 +195.222.95.57 51413 +195.234.21.119 6881 +195.240.45.224 6881 +195.245.221.35 36132 +195.88.15.161 7743 +197.87.68.102 25317 +199.127.250.15 29015 +201.252.144.11 15506 +201.53.229.60 1041 +201.83.71.123 50579 +202.231.169.253 21947 +202.62.94.74 39272 +203.198.141.201 7453 +203.213.94.200 14161 +203.219.80.229 54622 +203.87.14.9 51413 +205.209.160.171 6881 +207.224.196.66 10731 +207.255.112.154 6881 +208.101.109.128 53329 +209.195.77.83 34603 +210.136.215.55 6881 +210.139.186.89 22645 +210.195.47.196 19861 +211.133.168.214 44631 +211.133.97.200 10467 +211.14.227.69 23292 +211.217.2.251 41870 +211.47.87.58 62348 +2.1.187.15 50269 +212.129.33.50 6881 +212.129.33.59 6881 +212.21.13.209 56909 +212.45.81.173 16427 +212.49.47.32 19949 +212.50.134.32 49382 +212.74.222.13 38345 +212.77.130.215 51413 +213.111.37.228 9858 +213.112.177.129 43304 +213.114.48.42 38511 +213.127.73.24 4985 +213.136.83.168 20024 +213.152.161.101 34261 +213.167.206.125 38106 +213.174.60.46 17919 +213.231.145.208 12306 +213.245.41.143 40018 +213.251.184.146 64347 +213.26.96.117 27807 +213.27.20.223 53410 +213.46.132.81 50321 +213.57.219.99 50321 +2.138.139.164 62284 +213.83.135.109 50321 +216.126.213.67 25513 +216.151.180.79 3575 +217.12.220.238 51413 +217.165.53.221 24579 +217.209.125.15 26155 +217.29.187.23 62523 +217.76.184.18 51413 +218.161.29.209 20472 +218.173.174.50 27069 +218.215.67.196 39700 +218.252.251.52 17648 +219.251.128.233 50506 +219.84.184.204 61337 +220.100.126.4 24435 +220.120.236.151 11393 +220.123.202.121 50427 +220.136.37.144 27075 +220.143.1.39 19780 +220.161.21.128 6881 +220.18.72.3 64774 +220.239.238.43 51413 +221.118.191.176 17563 +221.127.216.99 21461 +221.217.171.96 28947 +221.76.232.11 51413 +223.16.68.18 6881 +223.16.73.77 22303 +223.18.249.29 7221 +223.219.73.227 17107 +223.247.194.12 16001 +2.26.160.4 26398 +23.102.24.179 33961 +23.102.24.179 37253 +23.16.200.217 51413 +2.32.208.156 51413 +23.252.167.81 6881 +23.252.55.56 29633 +23.27.125.26 3124 +23.88.236.63 51413 +23.96.202.54 54203 +24.128.39.166 5223 +24.144.184.102 14888 +24.211.171.122 10349 +24.226.116.194 8547 +24.6.186.56 34502 +24.75.160.81 53965 +24.78.8.98 6884 +2.51.116.185 63433 +27.117.108.178 56753 +27.130.112.202 25477 +27.194.238.27 6881 +2.89.235.12 18179 +2.94.72.90 27587 +31.147.61.11 23853 +31.185.5.4 10733 +31.220.168.41 35190 +31.223.216.83 50321 +31.35.57.129 13924 +31.42.125.83 46470 +31.8.136.66 11996 +31.8.71.71 10170 +36.225.186.145 25184 +37.104.125.16 50321 +37.107.121.203 18429 +37.110.127.110 6881 +37.113.129.221 36551 +37.131.222.30 21517 +37.15.161.58 51413 +37.201.195.67 26515 +37.204.196.34 42929 +37.204.23.144 44822 +37.208.66.86 30260 +37.218.190.131 35691 +37.222.221.222 8621 +37.229.60.103 6881 +37.235.150.240 47003 +37.235.207.247 30166 +37.49.169.66 28133 +37.59.55.58 51413 +38.132.51.41 53369 +39.84.172.237 6881 +40.117.89.72 24427 +41.189.37.70 28295 +41.210.224.40 11953 +41.212.221.164 33050 +41.246.76.233 42181 +41.45.41.123 18636 +42.124.217.110 7789 +42.126.157.252 22033 +42.2.41.156 24462 +43.255.188.4 6881 +45.242.102.235 10588 +45.32.254.211 6881 +45.63.55.106 6884 +45.63.57.15 6881 +45.76.69.49 6881 +46.101.56.83 30264 +46.103.53.38 61299 +46.108.17.98 10419 +46.116.177.103 22754 +46.118.116.209 20711 +46.118.83.183 12324 +46.119.196.109 19075 +46.127.246.32 40085 +46.138.37.148 59039 +46.180.245.79 53580 +46.187.88.219 48529 +46.188.32.80 65535 +46.191.196.157 49001 +46.191.225.36 14692 +46.191.226.4 28344 +46.191.226.4 59982 +46.19.85.245 50321 +46.211.113.5 49001 +46.22.234.12 14916 +46.29.147.201 20232 +46.37.83.122 37389 +46.41.109.26 39584 +46.42.23.240 53559 +46.48.90.11 55025 +46.61.12.234 49001 +46.8.136.196 57085 +47.145.143.92 24585 +47.9.158.37 2049 +49.145.59.194 38935 +49.213.154.99 10087 +50.0.146.178 51413 +50.113.92.209 6881 +50.191.160.70 20789 +50.201.138.122 10228 +50.43.117.21 42660 +50.53.143.88 57568 +50.71.135.98 20332 +50.72.81.218 6881 +50.83.34.19 23846 +51.141.3.234 31256 +51.175.237.18 15937 +5.12.126.206 34538 +51.235.58.9 16552 +51.255.58.0 51412 +5.13.18.103 24121 +5.140.20.202 11609 +5.149.211.210 22277 +5.165.66.132 63326 +5.18.182.29 51653 +5.187.143.52 6889 +5.198.60.36 49007 +52.10.254.137 6992 +52.169.220.77 27456 +52.169.220.77 37717 +52.187.56.29 27076 +52.228.47.105 39376 +5.227.122.6 3751 +52.29.243.196 6892 52.29.58.236 6892 52.29.58.236 6992 -132.147.102.21 1024 -91.151.203.4 21833 -77.203.253.126 33334 -52.49.71.160 6992 -62.117.149.142 20425 -80.8.119.70 48082 -122.135.89.118 25051 -86.241.109.212 1024 -52.30.149.135 6992 -64.34.187.221 4926 -78.57.193.191 46040 -118.163.92.217 6881 -220.143.1.39 19780 -217.12.220.238 51413 -89.110.53.186 49787 -103.27.109.109 6884 -99.255.185.162 912 -109.11.38.68 61722 -211.14.227.69 23292 -78.224.174.190 3908 -46.103.53.38 61299 -178.204.71.144 26612 -79.251.43.82 56107 -90.79.215.246 63040 -151.229.110.52 11163 -122.134.166.111 7105 -87.191.160.44 61212 -73.19.47.61 15685 -93.188.8.35 43842 52.30.149.135 6892 -221.217.171.96 28947 -51.235.58.9 16552 -169.149.196.18 50321 -158.193.227.67 8621 -223.247.194.12 16001 -5.227.122.6 3751 -89.3.231.151 45960 -212.129.33.50 6881 -91.241.236.6 49001 -90.207.148.109 51413 -119.47.150.27 51294 -52.187.56.29 27076 -46.108.17.98 10419 -185.21.217.59 37045 -178.32.185.12 6544 -176.214.76.87 7047 -91.222.89.195 49001 -45.242.102.235 10588 -81.75.221.92 49855 -173.31.76.104 21757 -176.104.214.178 59611 -118.32.175.216 49802 -218.161.29.209 20472 -103.197.33.53 14354 -113.83.226.37 51413 -95.208.144.94 6881 -52.57.140.142 6992 -83.252.149.71 42945 -125.193.100.191 21802 -85.17.127.168 38557 -52.169.220.77 27456 -77.239.87.77 10344 -85.74.189.101 53149 -95.76.164.90 64529 -175.156.223.157 22300 -59.25.226.139 52413 -213.26.96.117 27807 -107.185.79.233 10278 -203.87.14.9 51413 -61.219.68.63 15465 -37.201.195.67 26515 -92.238.93.208 53280 -111.250.149.202 25863 -178.150.91.123 8314 -122.150.64.247 17132 -220.100.126.4 24435 -47.145.143.92 24585 -153.179.56.36 51413 -91.121.16.72 51413 -98.234.210.209 51413 -178.221.81.112 59014 -121.75.175.91 10146 -195.154.136.170 55040 -95.47.197.189 49001 -85.26.92.236 29761 -107.9.243.80 24112 -62.210.90.181 6881 -82.224.22.64 15190 -92.37.142.2 62290 -195.245.221.35 36132 -72.188.223.147 41910 -13.89.52.33 51409 -95.84.255.47 51413 -90.231.4.40 6881 -151.62.32.183 51413 -114.224.40.181 51413 -65.95.195.245 49706 -52.10.254.137 6992 -93.21.41.89 58623 -123.50.62.239 9433 -103.50.76.50 11623 -118.189.195.210 51413 -79.52.149.182 6881 -50.72.81.218 6881 -52.57.20.58 6892 -223.16.68.18 6881 -111.184.247.170 19087 -193.160.224.34 29787 -188.168.14.169 1376 -14.133.76.62 8771 -114.32.76.93 51413 -141.105.71.92 6881 -90.65.17.120 24080 -216.151.180.79 3575 -135.23.129.163 57581 -77.180.216.131 5249 -52.29.243.196 6892 -37.235.150.240 47003 -46.191.225.36 14692 +52.30.149.135 6992 +52.40.120.56 6892 +52.40.120.56 6992 +52.40.7.160 6892 +52.43.139.55 6892 +52.43.139.55 6992 52.48.137.241 6992 -81.21.216.18 22828 -178.219.38.34 1024 -43.255.188.4 6881 -221.76.232.11 51413 -49.213.154.99 10087 -105.104.153.96 56532 -93.90.227.17 49001 -71.233.72.165 39789 -118.184.27.144 6881 -58.94.255.72 25896 -118.173.219.156 12148 -38.132.51.41 53369 -205.209.160.171 6881 -45.63.55.106 6884 -157.7.109.77 6881 -172.87.31.56 6884 -23.252.167.81 6881 -45.63.57.15 6881 -220.161.21.128 6881 -133.130.90.125 6883 +52.49.71.160 6892 +52.49.71.160 6992 +52.57.140.142 6892 +52.57.140.142 6992 +52.57.20.58 6892 +52.57.20.58 6992 +5.2.58.49 49001 52.78.176.27 6881 -23.27.125.26 3124 -46.188.32.80 65535 +5.51.197.239 6881 +58.168.124.241 3750 +58.7.210.130 51413 +58.94.255.72 25896 +59.133.235.34 12525 +59.25.226.139 52413 +60.128.90.71 15510 +60.130.54.195 26585 +60.138.177.136 47243 +60.241.209.107 57588 +60.241.87.17 24478 +60.248.95.76 6881 +61.178.110.205 16001 +61.219.68.63 15465 +61.224.74.1 45106 +61.57.113.156 1028 +61.57.118.157 25147 +61.62.186.193 8442 +61.63.158.180 48321 +61.81.86.213 50071 +61.89.6.203 19474 +62.101.135.20 1029 +62.117.149.142 20425 +62.140.137.49 3706 +62.169.127.9 26665 +62.210.90.181 6881 +62.235.217.73 61835 +62.65.216.158 13414 +62.76.100.235 22948 +62.76.100.235 28212 +62.76.100.235 33748 +62.76.100.235 34251 +62.76.100.235 39415 +62.76.100.235 40077 +62.76.100.235 41047 +62.76.100.235 43085 +62.83.111.85 36398 +62.93.99.6 28010 +64.113.125.178 61127 +64.121.118.201 61270 +64.25.21.15 37321 +64.34.187.221 4926 +65.95.191.51 51413 +65.95.195.245 49706 +65.95.73.182 42970 +66.197.135.74 9870 +66.8.165.71 16020 +67.128.168.196 11524 +67.164.23.233 7119 +67.167.180.162 11451 +67.173.33.90 33134 +67.185.95.9 32173 +67.188.183.70 41082 +67.197.217.74 52624 +67.215.246.10 6881 +67.242.169.143 6886 +67.71.140.245 15582 +67.8.141.69 63990 +67.86.174.199 33605 +68.0.53.95 11464 +68.106.225.140 47417 +68.192.162.202 31159 +68.209.239.148 50375 +68.227.56.45 57278 +68.59.158.23 39460 +68.82.32.238 18307 +68.98.108.234 24290 +69.146.24.229 34202 +69.175.34.187 28566 +69.176.171.40 11254 +69.181.169.181 7313 +70.113.2.167 45645 +70.114.155.3 34892 +70.48.173.172 3504 +70.83.35.78 31982 +71.187.209.2 22274 +71.232.195.149 56530 +71.233.72.165 39789 +71.237.6.246 41304 +71.63.225.31 21053 +71.84.138.32 52296 +72.176.18.24 6881 +72.188.223.147 41910 +72.192.215.39 30028 +72.223.102.189 11614 +72.230.75.87 21095 +73.15.30.22 44905 +73.173.111.92 63666 +73.19.47.61 15685 +74.140.153.59 59286 +74.219.135.69 30239 +74.76.197.221 29154 +74.96.156.188 31749 +75.142.21.2 56117 +75.185.11.189 23993 +75.187.202.104 44822 +75.40.21.238 51413 +75.69.72.31 51413 +75.70.84.67 52856 +76.103.53.99 40573 +76.103.79.28 36151 +76.121.38.23 58982 +76.125.33.68 54526 +76.174.205.156 8605 +76.181.135.146 17016 +76.189.15.171 28937 +76.22.35.229 16612 +76.30.131.83 49366 +76.66.171.131 15021 +76.7.214.210 35875 +76.88.254.191 27137 +77.101.130.178 61762 +77.135.115.253 9050 +77.180.216.131 24988 +77.180.216.131 5249 +77.196.18.56 22750 +77.203.253.126 33334 +77.232.162.51 14257 +77.239.87.77 10344 +77.249.124.77 49187 +77.28.66.239 15514 +77.34.44.71 35691 +77.37.160.170 1242 +77.46.26.128 38127 +77.71.220.234 62357 +77.71.69.65 12087 +77.73.46.124 28966 +77.84.97.16 31837 +78.105.105.87 42127 +78.106.178.64 46183 +78.119.193.151 55227 +78.119.213.102 44822 +78.128.52.177 24574 +78.131.13.232 55633 +78.141.127.164 55618 +78.149.99.103 22683 +78.163.181.120 16519 +78.163.220.242 31758 +78.173.175.56 53453 +78.213.113.102 9182 +78.220.115.99 49482 +78.222.160.139 37995 +78.224.174.190 3908 +78.227.88.161 23191 +78.228.230.130 12577 +78.236.77.65 32026 +78.237.111.89 45254 +78.241.70.65 22014 +78.242.168.94 34153 +78.24.231.166 6881 +78.247.176.4 18105 +78.253.242.10 59410 +78.29.75.248 6881 +78.31.66.180 5307 +78.43.137.174 10409 +78.46.147.99 51413 +78.55.114.191 42839 +78.57.193.191 46040 +78.60.4.214 6881 +78.73.38.9 11423 +79.100.105.242 14363 +79.100.51.243 18404 +79.101.160.169 1453 +79.114.14.206 4602 +79.114.193.207 17966 +79.136.243.46 46486 +79.138.67.151 47942 +79.153.101.234 11154 +79.160.104.5 2513 +79.168.31.62 2622 +79.169.77.54 63189 +79.175.106.234 12684 +79.176.173.50 33043 +79.18.246.192 20356 +79.189.118.198 6881 +79.226.190.78 18189 +79.251.43.82 56107 +79.253.173.153 58428 +79.52.149.182 6881 +79.82.130.49 20757 +79.9.144.222 29037 +79.93.218.69 28681 +79.95.171.102 11092 +80.103.109.43 55260 +80.140.156.164 37500 +80.14.161.164 51789 +80.234.86.220 60386 +80.249.65.131 9800 +80.42.246.251 20502 +80.57.45.239 28239 +80.8.119.70 48082 +80.83.245.36 6993 +80.98.106.227 9773 +80.98.158.177 29436 +80.98.17.100 51413 +80.98.74.161 54545 +81.13.253.217 46370 +81.153.207.27 51413 +81.21.216.18 22828 +81.221.84.77 20214 +81.23.7.233 9224 +81.249.29.121 7812 +81.45.175.39 33652 +81.51.97.133 40039 +81.75.221.92 49855 +81.93.207.145 20963 +82.136.113.22 6881 +82.137.118.90 26474 +82.178.114.74 50836 +82.192.32.151 58864 +82.211.130.137 29777 +82.224.22.64 15190 +82.232.212.66 55826 +82.235.101.26 7816 +82.237.40.39 39052 +82.240.4.144 16729 +82.47.63.231 26035 +82.7.124.219 12334 +83.110.225.79 17257 +83.134.60.207 10267 +83.155.218.188 51413 +83.157.77.228 7812 +83.202.163.16 6881 +83.213.185.157 43235 +83.240.93.202 51413 +83.252.149.71 42945 +83.252.23.84 10699 +83.49.197.212 61627 +83.6.22.221 52512 +83.87.5.5 43317 +83.9.248.251 51413 +84.123.4.247 29043 +84.123.59.159 13963 +84.151.248.98 61500 +84.154.235.198 59678 +84.217.43.156 45620 +84.245.120.26 51413 +84.40.80.3 6890 +84.52.169.26 10838 +84.75.22.54 51413 +85.107.174.75 50321 +85.107.182.248 19486 +85.114.60.149 17582 +85.138.230.74 56197 +85.167.249.218 55649 +85.17.127.168 38557 +85.195.226.115 5888 +85.219.118.111 27483 +85.220.66.170 35131 +85.246.109.226 6890 +85.250.43.217 41490 +85.25.218.207 6884 +85.25.218.207 6886 +85.25.218.207 6888 +85.25.237.46 6882 +85.25.237.46 6887 +85.26.92.236 29761 +85.54.211.184 17283 +85.59.159.50 49001 +85.64.151.207 44822 +85.74.189.101 53149 +86.100.227.142 50497 +86.145.211.200 49330 +86.168.167.166 55451 +86.169.117.185 51938 +86.194.215.166 14316 +86.237.133.79 51413 +86.241.109.212 1024 +86.252.48.255 59566 +86.29.220.199 28527 +86.46.36.146 10124 +87.106.187.29 20277 +87.110.236.117 64810 +87.16.197.67 20787 +87.191.160.44 61212 +87.244.169.128 55807 +87.253.26.85 45934 +87.2.74.180 6783 +87.78.153.16 5011 +87.97.253.69 50649 +87.98.162.88 6881 +88.148.233.194 38081 +88.149.179.95 6881 +88.155.36.246 15856 +88.165.34.61 41708 +88.169.1.120 59258 +88.169.60.195 17108 +88.170.61.126 32761 +88.174.167.76 30032 +88.174.176.173 56969 +88.174.58.215 51413 +88.186.230.88 12048 +88.217.31.208 7813 +88.217.36.182 2924 +88.230.184.110 25432 +88.233.167.39 20620 +88.24.180.250 6249 +88.245.188.55 28097 +88.3.236.194 10926 +88.84.191.25 30536 +89.110.53.186 41210 +89.110.53.186 49787 +89.132.181.63 61498 +89.133.89.51 55846 +89.143.138.33 64606 +89.204.110.192 6881 +89.209.82.248 12425 +89.224.149.142 25365 +89.248.171.137 6881 +89.27.163.34 55315 +89.3.231.151 45960 +89.33.72.233 1398 +89.78.249.54 49001 +89.92.251.113 16946 +90.116.231.10 33026 +90.126.31.47 50000 +90.149.227.168 49241 +90.16.53.195 31338 +90.184.93.164 22828 +90.204.42.12 48402 +90.207.148.109 51413 +90.225.103.241 40066 +90.231.4.40 6881 +90.27.140.90 1347 +90.29.226.250 14329 +90.31.140.85 16185 +90.38.76.72 4699 +90.41.206.239 30502 +90.65.17.120 24080 +90.79.215.246 63040 +91.105.156.125 1025 +91.121.114.171 58846 +91.121.136.132 59001 +91.121.16.72 51413 +91.137.168.238 27616 +91.148.14.62 11206 +91.151.203.4 21833 +91.157.230.16 1885 +91.216.66.81 36678 +91.219.237.220 4087 +91.222.89.195 49001 +91.241.236.6 49001 +91.247.142.205 58199 +91.67.129.216 1210 +91.74.119.134 49246 +91.89.196.37 61799 +92.125.17.239 25548 +92.131.55.187 40332 +92.134.139.162 10004 +92.137.208.191 29253 +92.139.252.16 36535 +92.156.86.247 56432 +92.2.212.97 15078 +92.233.185.151 51413 +92.234.58.147 13285 +92.238.93.208 53280 +92.246.22.40 33576 +92.247.248.50 23666 +92.255.208.221 34798 +92.37.142.2 62290 +92.37.27.83 37375 +92.39.216.40 23585 +92.43.189.45 3140 +92.45.192.16 22222 +92.46.228.108 26675 +92.49.5.186 38678 +92.58.106.183 8621 +92.80.201.189 20026 +92.83.188.95 10477 +92.87.167.73 55624 +92.90.21.108 49760 +92.96.51.71 56237 +92.98.171.108 51413 +92.99.221.105 51405 +93.105.81.215 21000 +93.109.84.235 21443 +93.11.175.201 41027 +93.124.97.171 49001 +93.181.201.89 54631 +93.186.192.165 39020 +93.188.8.35 43842 +93.21.41.89 58623 +93.58.14.83 51413 +93.77.32.187 57408 +93.78.138.118 50567 +93.80.96.134 23683 +93.81.126.53 6881 +93.84.33.191 22071 +93.84.33.191 55762 +93.90.227.17 49001 +93.95.160.156 13118 +93.96.13.136 12181 +94.103.196.139 54378 +94.155.59.112 57833 +94.180.0.78 23879 +94.190.105.157 6881 +94.208.10.154 52432 +94.21.30.143 63448 +94.21.70.206 50541 +94.23.0.84 51582 +94.233.110.175 49001 +94.23.49.143 8000 +94.23.5.218 6881 +94.236.246.217 15554 +94.244.35.192 51413 +94.245.153.136 23040 +94.30.170.21 7105 +94.41.249.131 10613 +94.44.164.74 60966 +94.62.25.152 39020 +94.68.144.118 10009 +94.96.112.204 35187 +94.96.167.130 64130 +95.104.80.193 6881 +95.111.0.20 34295 +95.111.46.227 18466 +95.135.101.164 55829 +95.150.185.164 6882 +95.153.169.40 19697 +95.188.23.129 42927 +95.196.203.111 59402 +95.208.144.94 6881 +95.222.150.218 38337 +95.239.56.106 62200 +95.24.101.211 6881 +95.25.229.186 6889 +95.26.91.94 6881 +95.31.49.9 9424 +95.31.49.9 9425 +95.32.187.214 14778 +95.37.162.145 14476 +95.37.18.51 59118 +95.47.197.189 49001 +95.54.76.150 56930 +95.76.164.90 64529 +95.84.208.14 9372 +95.84.255.47 51413 +95.90.215.112 37742 +95.90.236.216 41463 +95.92.232.20 51413 +96.237.181.232 7915 +96.49.182.210 21859 +96.63.15.208 34651 +97.106.156.54 63552 +97.85.86.199 6890 +98.119.4.86 1024 +98.127.163.114 53062 +98.194.6.21 51413 +98.197.42.116 19866 +98.199.98.228 32970 +98.200.252.137 18502 +98.202.170.205 41731 +98.214.166.21 38072 +98.225.11.98 51769 +98.232.199.100 29721 +98.234.210.209 51413 +99.192.77.139 33333 +99.242.88.203 21704 +99.255.185.162 912 +99.59.129.46 26141 +99.60.78.40 18894 diff --git a/libbitdht/src/bitdht/bdboot_generate.sh b/libbitdht/src/bitdht/bdboot_generate.sh new file mode 100755 index 000000000..97618f0df --- /dev/null +++ b/libbitdht/src/bitdht/bdboot_generate.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +< + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + +LICENSE + +< bdboot.txt +-------------------------------- +README + +function check_dht_host() +{ + mHost="$1" + mPort="$2" + + sudo nmap -oG - -sU -p $mPort $mHost | grep open | \ + awk '{print $2" "$5}' | awk -F/ '{print $1}' +} + +cat | while read line; do + hostIP="$(echo $line | awk '{print $1}')" + hostPort="$(echo $line | awk '{print $2}')" + check_dht_host $hostIP $hostPort +done + +check_dht_host router.utorrent.com 6881 +check_dht_host router.bittorrent.com 6881 +check_dht_host dht.libtorrent.org 25401 +check_dht_host dht.transmissionbt.com 6881 + diff --git a/libbitdht/src/util/bdnet.cc b/libbitdht/src/util/bdnet.cc index 4c5f681a6..ba8c33553 100644 --- a/libbitdht/src/util/bdnet.cc +++ b/libbitdht/src/util/bdnet.cc @@ -285,12 +285,6 @@ int sleep(unsigned int sec) } #endif -int usleep(unsigned int usec) -{ - Sleep(usec / 1000); - return 0; -} - /********************************** WINDOWS/UNIX SPECIFIC PART ******************/ #else // UNIX diff --git a/libbitdht/src/util/bdnet.h b/libbitdht/src/util/bdnet.h index 639a2ed1a..89ecfa710 100644 --- a/libbitdht/src/util/bdnet.h +++ b/libbitdht/src/util/bdnet.h @@ -159,7 +159,6 @@ int bdnet_w2u_errno(int error); #ifndef __MINGW64_VERSION_MAJOR int sleep(unsigned int sec); #endif -int usleep(unsigned int usec); #endif // END of WINDOWS defines. /********************************** WINDOWS/UNIX SPECIFIC PART ******************/ diff --git a/libretroshare/src/chat/distributedchat.cc b/libretroshare/src/chat/distributedchat.cc index 45fc4e29e..ca24cac1e 100644 --- a/libretroshare/src/chat/distributedchat.cc +++ b/libretroshare/src/chat/distributedchat.cc @@ -219,9 +219,16 @@ bool DistributedChatService::checkSignature(RsChatLobbyBouncingObject *obj,const // network pre-request key to allow message authentication. - mGixs->requestKey(obj->signature.keyId,peer_list,RsIdentityUsage(RS_SERVICE_TYPE_CHAT,RsIdentityUsage::CHAT_LOBBY_MSG_VALIDATION,RsGxsGroupId(),RsGxsMessageId(),obj->lobby_id)); + mGixs->requestKey(obj->signature.keyId,peer_list,RsIdentityUsage(RsServiceType::CHAT, + RsIdentityUsage::CHAT_LOBBY_MSG_VALIDATION, + RsGxsGroupId(), + RsGxsMessageId(), + RsGxsMessageId(), + RsGxsMessageId(), + obj->lobby_id)); - uint32_t size = RsChatSerialiser(RsServiceSerializer::SERIALIZATION_FLAG_SIGNATURE).size(dynamic_cast(obj)) ; + uint32_t size = RsChatSerialiser(RsSerializationFlags::SIGNATURE) + .size(dynamic_cast(obj)); RsTemporaryMemory memory(size) ; #ifdef DEBUG_CHAT_LOBBIES @@ -229,14 +236,21 @@ bool DistributedChatService::checkSignature(RsChatLobbyBouncingObject *obj,const std::cerr << " signature id: " << obj->signature.keyId << std::endl; #endif - if(!RsChatSerialiser(RsServiceSerializer::SERIALIZATION_FLAG_SIGNATURE).serialise(dynamic_cast(obj),memory,&size)) + if( !RsChatSerialiser(RsSerializationFlags::SIGNATURE) + .serialise(dynamic_cast(obj),memory,&size) ) { std::cerr << " (EE) Cannot serialise message item. " << std::endl; return false ; } uint32_t error_status ; - RsIdentityUsage use_info(RS_SERVICE_TYPE_CHAT,RsIdentityUsage::CHAT_LOBBY_MSG_VALIDATION,RsGxsGroupId(),RsGxsMessageId(),obj->lobby_id) ; + RsIdentityUsage use_info(RsServiceType::CHAT, + RsIdentityUsage::CHAT_LOBBY_MSG_VALIDATION, + RsGxsGroupId(), + RsGxsMessageId(), + RsGxsMessageId(), + RsGxsMessageId(), + obj->lobby_id) ; if(!mGixs->validateData(memory,size,obj->signature,false,use_info,error_status)) { @@ -1003,10 +1017,12 @@ bool DistributedChatService::locked_initLobbyBouncableObject(const ChatLobbyId& // now sign the object, if the lobby expects it - uint32_t size = RsChatSerialiser(RsServiceSerializer::SERIALIZATION_FLAG_SIGNATURE).size(dynamic_cast(&item)) ; + uint32_t size = RsChatSerialiser(RsSerializationFlags::SIGNATURE) + .size(dynamic_cast(&item)); RsTemporaryMemory memory(size) ; - if(!RsChatSerialiser(RsServiceSerializer::SERIALIZATION_FLAG_SIGNATURE).serialise(dynamic_cast(&item),memory,&size)) + if( !RsChatSerialiser(RsSerializationFlags::SIGNATURE) + .serialise(dynamic_cast(&item),memory,&size) ) { std::cerr << "(EE) Cannot sign message item. " << std::endl; return false ; @@ -1600,6 +1616,12 @@ ChatLobbyId DistributedChatService::createChatLobby(const std::string& lobby_nam #endif ChatLobbyId lobby_id ; { + if (!rsIdentity->isOwnId(lobby_identity)) + { + RsErr() << __PRETTY_FUNCTION__ << " lobby_identity RsGxsId id must be own" << std::endl; + return 0; + } + RsStackMutex stack(mDistributedChatMtx); /********** STACK LOCKED MTX ******/ // create a unique id. diff --git a/libretroshare/src/chat/p3chatservice.cc b/libretroshare/src/chat/p3chatservice.cc index 28ade2674..3fbbdaf75 100644 --- a/libretroshare/src/chat/p3chatservice.cc +++ b/libretroshare/src/chat/p3chatservice.cc @@ -722,6 +722,23 @@ bool p3ChatService::initiateDistantChatConnexion( const RsGxsId& to_gxs_id, uint32_t& error_code, bool notify ) { + + if(to_gxs_id.isNull()) + { + RsErr() << __PRETTY_FUNCTION__ << " Destination RsGxsId is invalid" << std::endl; + return false; + } + if (from_gxs_id.isNull()) + { + RsErr() << __PRETTY_FUNCTION__ << " Origin RsGxsId is invalid" << std::endl; + return false; + } + if (!rsIdentity->isOwnId(from_gxs_id)) + { + RsErr() << __PRETTY_FUNCTION__ << " Origin RsGxsId id must be own" << std::endl; + return false; + } + if(DistantChatService::initiateDistantChatConnexion( to_gxs_id, from_gxs_id, pid, error_code, notify )) @@ -881,6 +898,13 @@ bool p3ChatService::handleRecvChatMsgItem(RsChatMsgItem *& ci) RsServer::notify()->notifyChatMessage(cm); mHistoryMgr->addMessage(cm); + + if(rsEvents) + { + auto ev = std::make_shared(); + ev->mChatMessage = cm; + rsEvents->postEvent(ev); + } return true ; } diff --git a/libretroshare/src/chat/p3chatservice.h b/libretroshare/src/chat/p3chatservice.h index 3a5b08ab9..bc4dd56b9 100644 --- a/libretroshare/src/chat/p3chatservice.h +++ b/libretroshare/src/chat/p3chatservice.h @@ -42,6 +42,22 @@ class p3HistoryMgr; typedef RsPeerId ChatLobbyVirtualPeerId ; +struct RsChatMessageEvent : RsEvent +{ + RsChatMessageEvent() : RsEvent(RsEventType::CHAT_MESSAGE) {} + ~RsChatMessageEvent() override = default; + + ///* @see RsEvent @see RsSerializable + void serial_process( RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx ) override + { + RsEvent::serial_process(j, ctx); + + RS_SERIAL_PROCESS(mChatMessage); + } + + ChatMessage mChatMessage; +}; + //!The basic Chat service. /** * diff --git a/libretroshare/src/chat/rschatitems.cc b/libretroshare/src/chat/rschatitems.cc index d3a837b07..13d272c4e 100644 --- a/libretroshare/src/chat/rschatitems.cc +++ b/libretroshare/src/chat/rschatitems.cc @@ -70,23 +70,14 @@ void RsChatMsgItem::serial_process(RsGenericSerializer::SerializeJob j,RsGeneric /*************************************************************************/ -RsChatAvatarItem::~RsChatAvatarItem() -{ - if(image_data != NULL) - { - free(image_data) ; - image_data = NULL ; - } -} - void RsChatLobbyBouncingObject::serial_process(RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx) { RsTypeSerializer::serial_process(j,ctx,lobby_id,"lobby_id") ; RsTypeSerializer::serial_process(j,ctx,msg_id ,"msg_id") ; RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_NAME,nick,"nick") ; - if(!(ctx.mFlags & RsServiceSerializer::SERIALIZATION_FLAG_SIGNATURE)) - RsTypeSerializer::serial_process(j,ctx,signature,"signature") ; + if(!(ctx.mFlags & RsSerializationFlags::SIGNATURE)) + RS_SERIAL_PROCESS(signature); } void RsChatLobbyMsgItem::serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) diff --git a/libretroshare/src/chat/rschatitems.h b/libretroshare/src/chat/rschatitems.h index 244be60b2..aca3a411a 100644 --- a/libretroshare/src/chat/rschatitems.h +++ b/libretroshare/src/chat/rschatitems.h @@ -99,8 +99,6 @@ class RsChatItem: public RsItem } virtual ~RsChatItem() {} - virtual std::ostream& print(std::ostream &out, uint16_t /*indent*/ = 0) { return out; } // derived from RsItem, but should be removed - virtual void clear() {} }; @@ -348,14 +346,20 @@ class RsChatStatusItem: public RsChatItem // class RsChatAvatarItem: public RsChatItem { - public: - RsChatAvatarItem() :RsChatItem(RS_PKT_SUBTYPE_CHAT_AVATAR) {setPriorityLevel(QOS_PRIORITY_RS_CHAT_AVATAR_ITEM) ;} +public: + RsChatAvatarItem(): + RsChatItem(RS_PKT_SUBTYPE_CHAT_AVATAR), + image_size(0), image_data(nullptr) + { setPriorityLevel(QOS_PRIORITY_RS_CHAT_AVATAR_ITEM); } - virtual ~RsChatAvatarItem() ; - void serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx); + ~RsChatAvatarItem() override { free(image_data); } - uint32_t image_size ; // size of data in bytes - unsigned char *image_data ; // image + void serial_process( + RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx) override; + + uint32_t image_size; /// size of data in bytes + unsigned char* image_data ; /// image data }; @@ -371,9 +375,8 @@ struct PrivateOugoingMapItem : RsChatItem struct RsChatSerialiser : RsServiceSerializer { - RsChatSerialiser(SerializationFlags flags = SERIALIZATION_FLAG_NONE) : - RsServiceSerializer( RS_SERVICE_TYPE_CHAT, - RsGenericSerializer::FORMAT_BINARY, flags ) {} + RsChatSerialiser(RsSerializationFlags flags = RsSerializationFlags::NONE): + RsServiceSerializer(RS_SERVICE_TYPE_CHAT, flags) {} virtual RsItem *create_item(uint16_t service_id,uint8_t item_sub_id) const; }; diff --git a/libretroshare/src/file_sharing/file_tree.cc b/libretroshare/src/file_sharing/file_tree.cc index 4953b192b..ad34501e3 100644 --- a/libretroshare/src/file_sharing/file_tree.cc +++ b/libretroshare/src/file_sharing/file_tree.cc @@ -3,7 +3,9 @@ * * * libretroshare: retroshare core library * * * - * Copyright 2018 by Retroshare Team * + * Copyright (C) 2018 Retroshare Team * + * Copyright (C) 2020 Gioacchino Mazzurco * + * Copyright (C) 2020 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -20,40 +22,100 @@ * * ******************************************************************************/ #include -#include -#include +#include "util/radix64.h" +#include "util/rsbase64.h" +#include "util/rsdir.h" +#include "retroshare/rsfiles.h" #include "file_sharing_defaults.h" #include "filelist_io.h" -#include "file_tree.h" +#include "serialiser/rstypeserializer.h" -std::string FileTreeImpl::toRadix64() const +void RsFileTree::DirData::serial_process( + RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx ) { - unsigned char *buff = NULL ; - uint32_t size = 0 ; - - serialise(buff,size) ; - - std::string res ; - - Radix64::encode(buff,size,res) ; - - free(buff) ; - return res ; + RS_SERIAL_PROCESS(name); + RS_SERIAL_PROCESS(subdirs); + RS_SERIAL_PROCESS(subfiles); } -FileTree *FileTree::create(const std::string& radix64_string) +void RsFileTree::FileData::serial_process( + RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx ) { - FileTreeImpl *ft = new FileTreeImpl ; + RS_SERIAL_PROCESS(name); + RS_SERIAL_PROCESS(size); + RS_SERIAL_PROCESS(hash); +} +/*static*/ std::tuple, std::error_condition> +RsFileTree::fromBase64(const std::string& base64) +{ + const auto failure = [](std::error_condition ec) + { return std::make_tuple(nullptr, ec); }; + + if(base64.empty()) return failure(std::errc::invalid_argument); + + std::error_condition ec; + std::vector mem; + if( (ec = RsBase64::decode(base64, mem)) ) return failure(ec); + + RsGenericSerializer::SerializeContext ctx( + mem.data(), static_cast(mem.size()), + RsSerializationFlags::INTEGER_VLQ ); + std::unique_ptr ft(new RsFileTree); + ft->serial_process( + RsGenericSerializer::SerializeJob::DESERIALIZE, ctx); + if(ctx.mOk) return std::make_tuple(std::move(ft), std::error_condition()); + + return failure(std::errc::invalid_argument); +} + +std::string RsFileTree::toBase64() const +{ + RsGenericSerializer::SerializeContext ctx; + ctx.mFlags = RsSerializationFlags::INTEGER_VLQ; + RsFileTree* ncThis = const_cast(this); + ncThis->serial_process( + RsGenericSerializer::SerializeJob::SIZE_ESTIMATE, ctx ); + + std::vector buf(ctx.mOffset); + ctx.mSize = ctx.mOffset; ctx.mOffset = 0; ctx.mData = buf.data(); + + ncThis->serial_process( + RsGenericSerializer::SerializeJob::SERIALIZE, ctx ); + std::string result; + RsBase64::encode(ctx.mData, ctx.mSize, result, false, true); + return result; +} + +std::string RsFileTree::toRadix64() const +{ + unsigned char* buff = nullptr; + uint32_t size = 0; + serialise(buff, size); + std::string res; + Radix64::encode(buff,size,res); + free(buff); + return res; +} + +std::unique_ptr RsFileTree::fromRadix64( + const std::string& radix64_string ) +{ + std::unique_ptr ft(new RsFileTree); std::vector mem = Radix64::decode(radix64_string); - ft->deserialise(mem.data(),mem.size()) ; - - return ft ; + if(ft->deserialise(mem.data(), static_cast(mem.size()))) + return ft; + return nullptr; } -void FileTreeImpl::recurs_buildFileTree(FileTreeImpl& ft,uint32_t index,const DirDetails& dd,bool remote,bool remove_top_dirs) +void RsFileTree::recurs_buildFileTree( + RsFileTree& ft, uint32_t index, const DirDetails& dd, bool remote, + bool remove_top_dirs ) { + RsDbg() << __PRETTY_FUNCTION__ << " index: " << index << std::endl; if(ft.mDirs.size() <= index) ft.mDirs.resize(index+1) ; @@ -67,14 +129,14 @@ void FileTreeImpl::recurs_buildFileTree(FileTreeImpl& ft,uint32_t index,const Di DirDetails dd2 ; - FileSearchFlags flags = remote?RS_FILE_HINTS_REMOTE:RS_FILE_HINTS_LOCAL ; + FileSearchFlags flags = remote ? RS_FILE_HINTS_REMOTE : RS_FILE_HINTS_LOCAL; for(uint32_t i=0;iRequestDirDetails(dd.children[i].ref,dd2,flags)) { if(dd.children[i].type == DIR_TYPE_FILE) { - FileTree::FileData f ; + FileData f ; f.name = dd2.name ; f.size = dd2.count ; f.hash = dd2.hash ; @@ -97,33 +159,50 @@ void FileTreeImpl::recurs_buildFileTree(FileTreeImpl& ft,uint32_t index,const Di std::cerr << "(EE) Cannot request dir details for pointer " << dd.children[i].ref << std::endl; } -bool FileTreeImpl::getDirectoryContent(uint32_t index,std::string& name,std::vector& subdirs,std::vector& subfiles) const +bool RsFileTree::getDirectoryContent( + std::string& name, std::vector& subdirs, + std::vector& subfiles, uint64_t index_p ) const { - if(index >= mDirs.size()) - return false ; + // Avoid warnings on Android armv7 + using sz_t = std::vector::size_type; + sz_t index = static_cast(index_p); + + if(index >= mDirs.size()) return false; name = mDirs[index].name; subdirs = mDirs[index].subdirs ; subfiles.clear() ; - for(uint32_t i=0;i(mDirs[index].subfiles[i])]); - return true ; + return true; } -FileTree *FileTree::create(const DirDetails& dd, bool remote,bool remove_top_dirs) +std::unique_ptr RsFileTree::fromDirDetails( + const DirDetails& dd, bool remote ,bool remove_top_dirs ) { - FileTreeImpl *ft = new FileTreeImpl ; + std::unique_ptrft(new RsFileTree); + if(dd.type == DIR_TYPE_FILE) + { + FileData fd; + fd.name = dd.name; fd.hash = dd.hash; fd.size = dd.count; + ft->mFiles.push_back(fd); + ft->mTotalFiles = 1; + ft->mTotalSize = fd.size; - FileTreeImpl::recurs_buildFileTree(*ft,0,dd,remote,remove_top_dirs) ; - - return ft ; + DirData dd; + dd.name = "/"; + dd.subfiles.push_back(0); + ft->mDirs.push_back(dd); + } + else recurs_buildFileTree(*ft, 0, dd, remote, remove_top_dirs ); + return ft; } typedef FileListIO::read_error read_error ; -bool FileTreeImpl::deserialise(unsigned char *buffer,uint32_t buffer_size) +bool RsFileTree::deserialise(unsigned char *buffer,uint32_t buffer_size) { uint32_t buffer_offset = 0 ; @@ -218,7 +297,7 @@ bool FileTreeImpl::deserialise(unsigned char *buffer,uint32_t buffer_size) return true ; } -bool FileTreeImpl::serialise(unsigned char *& buffer,uint32_t& buffer_size) const +bool RsFileTree::serialise(unsigned char *& buffer,uint32_t& buffer_size) const { buffer = 0 ; uint32_t buffer_offset = 0 ; @@ -234,7 +313,11 @@ bool FileTreeImpl::serialise(unsigned char *& buffer,uint32_t& buffer_size) cons { // Write some header - if(!FileListIO::writeField(buffer,buffer_size,buffer_offset,FILE_LIST_IO_TAG_LOCAL_DIRECTORY_VERSION,(uint32_t) FILE_LIST_IO_LOCAL_DIRECTORY_TREE_VERSION_0001)) throw std::runtime_error("Write error") ; + if(!FileListIO::writeField( + buffer, buffer_size, buffer_offset, + FILE_LIST_IO_TAG_LOCAL_DIRECTORY_VERSION, + (uint32_t) FILE_LIST_IO_LOCAL_DIRECTORY_TREE_VERSION_0001 ) ) + throw std::runtime_error("Write error") ; if(!FileListIO::writeField(buffer,buffer_size,buffer_offset,FILE_LIST_IO_TAG_RAW_NUMBER,(uint32_t) mFiles.size())) throw std::runtime_error("Write error") ; if(!FileListIO::writeField(buffer,buffer_size,buffer_offset,FILE_LIST_IO_TAG_RAW_NUMBER,(uint32_t) mDirs.size())) throw std::runtime_error("Write error") ; @@ -292,28 +375,4 @@ bool FileTreeImpl::serialise(unsigned char *& buffer,uint32_t& buffer_size) cons } } -void FileTreeImpl::print() const -{ - std::cerr << "File hierarchy: name=" << mDirs[0].name << " size=" << mTotalSize << std::endl; - recurs_print(0," ") ; -} - -void FileTreeImpl::recurs_print(uint32_t index,const std::string& indent) const -{ - if(index >= mDirs.size()) - { - std::cerr << "(EE) inconsistent FileTree structure" << std::endl; - return; - } - std::cerr << indent << mDirs[index].name << std::endl; - - for(uint32_t i=0;isearch(hash, hintflags, info) ) + return true; if(hintflags & RS_FILE_HINTS_LOCAL) { diff --git a/libretroshare/src/ft/ftcontroller.cc b/libretroshare/src/ft/ftcontroller.cc index 0af02144f..29494f189 100644 --- a/libretroshare/src/ft/ftcontroller.cc +++ b/libretroshare/src/ft/ftcontroller.cc @@ -38,7 +38,7 @@ #include "util/rsdiscspace.h" #include "util/rsmemory.h" #include "util/rstime.h" - +#include "util/cxx17retrocompat.h" #include "ft/ftcontroller.h" #include "ft/ftfilecreator.h" @@ -923,10 +923,15 @@ bool ftController::alreadyHaveFile(const RsFileHash& hash, FileInfo &info) return false ; } -bool ftController::FileRequest(const std::string& fname, const RsFileHash& hash, - uint64_t size, const std::string& dest, TransferRequestFlags flags, - const std::list &_srcIds, uint16_t state) +bool ftController::FileRequest( + const std::string& fname, const RsFileHash& hash, uint64_t size, + const std::string& dest, TransferRequestFlags flags, + const std::list &_srcIds, uint16_t state ) { + /* TODO: To support collections faithfully we need to be able to save + * the same file to multiple locations, both if already downloaded or + * incomplete */ + std::list srcIds(_srcIds) ; /* check if we have the file */ @@ -965,8 +970,9 @@ bool ftController::FileRequest(const std::string& fname, const RsFileHash& hash // create void file with the target name. FILE *f = RsDirUtil::rs_fopen(destination.c_str(),"w") ; - if(f == NULL) - std::cerr << "Could not open file " << destination << " for writting." << std::endl ; + if(!f) + RsErr() << __PRETTY_FUNCTION__ << " Could not write file " + << destination << std::endl; else fclose(f) ; @@ -979,12 +985,13 @@ bool ftController::FileRequest(const std::string& fname, const RsFileHash& hash // if(size >= 1024ull*1024ull*((1ull << 32) - 1)) { - std::cerr << "FileRequest Error: unexpected size. This is probably a bug." << std::endl; - std::cerr << " name = " << fname << std::endl ; - std::cerr << " flags = " << flags << std::endl ; - std::cerr << " dest = " << dest << std::endl ; - std::cerr << " size = " << size << std::endl ; - return false ; + RsErr() << __PRETTY_FUNCTION__ + << " unexpected size. This is probably a bug." + << " name = " << fname + << " flags = " << flags + << " dest = " << dest + << " size = " << size << std::endl; + return false; } /* If file transfer is not enabled .... @@ -1004,8 +1011,8 @@ bool ftController::FileRequest(const std::string& fname, const RsFileHash& hash } } - // remove the sources from the list, if they don't have clearance for direct transfer. This happens only for non cache files. - // + /* remove the sources from the list, if they don't have clearance for direct + * transfer. This happens only for non cache files. */ for(std::list::iterator it = srcIds.begin(); it != srcIds.end(); ) { bool bAllowDirectDL = false; @@ -1052,11 +1059,10 @@ bool ftController::FileRequest(const std::string& fname, const RsFileHash& hash * This is important as some guis request duplicate files regularly. */ - { - RsStackMutex stack(ctrlMutex); /******* LOCKED ********/ - - std::map::const_iterator dit = mDownloads.find(hash); + { + RS_STACK_MUTEX(ctrlMutex); + auto dit = std::as_const(mDownloads).find(hash); if (dit != mDownloads.end()) { /* we already have it! */ @@ -1110,7 +1116,7 @@ bool ftController::FileRequest(const std::string& fname, const RsFileHash& hash return true; } - } /******* UNLOCKED ********/ + } // RS_STACK_MUTEX(ctrlMutex); unlocked if(mSearch && !(flags & RS_FILE_REQ_NO_SEARCH)) diff --git a/libretroshare/src/ft/ftextralist.cc b/libretroshare/src/ft/ftextralist.cc index ccefe810d..a4a1245e0 100644 --- a/libretroshare/src/ft/ftextralist.cc +++ b/libretroshare/src/ft/ftextralist.cc @@ -4,7 +4,7 @@ * libretroshare: retroshare core library * * * * Copyright (C) 2008 Robert Fernie * - * Copyright (C) 2018-2019 Gioacchino Mazzurco * + * Copyright (C) 2018-2020 Gioacchino Mazzurco * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -21,6 +21,9 @@ * * *******************************************************************************/ +#include +#include + #ifdef WINDOWS_SYS #include "util/rswin.h" #endif @@ -245,12 +248,8 @@ bool ftExtraList::cleanupOldFiles() /* remove items */ for(std::list::iterator rit = toRemove.begin(); rit != toRemove.end(); ++rit) { - if (mFiles.end() != (it = mFiles.find(*rit))) - { - cleanupEntry(it->second.info.path, it->second.info.transfer_info_flags); - mFiles.erase(it); - } - mHashOfHash.erase(makeEncryptedHash(*rit)) ; + if (mFiles.end() != (it = mFiles.find(*rit))) mFiles.erase(it); + mHashOfHash.erase(makeEncryptedHash(*rit)); } IndicateConfigChanged(); @@ -258,46 +257,39 @@ bool ftExtraList::cleanupOldFiles() return true; } - -bool ftExtraList::cleanupEntry(std::string /*path*/, TransferRequestFlags /*flags*/) -{ -// if (flags & RS_FILE_CONFIG_CLEANUP_DELETE) -// { -// /* Delete the file? - not yet! */ -// } - return true; -} - - /*** - * Hash file, and add to the files, - * file is removed after period. - **/ - bool ftExtraList::hashExtraFile( std::string path, uint32_t period, TransferRequestFlags flags ) { -#ifdef DEBUG_ELIST - std::cerr << "ftExtraList::hashExtraFile() path: " << path; - std::cerr << " period: " << period; - std::cerr << " flags: " << flags; + constexpr rstime_t max_int = std::numeric_limits::max(); + const rstime_t now = time(nullptr); + const rstime_t timeOut = now + period; - std::cerr << std::endl; -#endif - - auto failure = [](std::string errMsg) + if(timeOut > max_int) { - RsErr() << __PRETTY_FUNCTION__ << " " << errMsg << std::endl; + /* Under the hood period is stored as int FileInfo::age so we do this + * check here to detect 2038 year problem + * https://en.wikipedia.org/wiki/Year_2038_problem */ + RsErr() << __PRETTY_FUNCTION__ << " period: " << period << " > " + << max_int - now << std::errc::value_too_large << std::endl; return false; - }; + } if(!RsDirUtil::fileExists(path)) - return failure("file: " + path + "not found"); + { + RsErr() << __PRETTY_FUNCTION__ << " path: " << path + << std::errc::no_such_file_or_directory << std::endl; + return false; + } if(RsDirUtil::checkDirectory(path)) - return failure("Cannot add a directory: " + path + "as extra file"); + { + RsErr() << __PRETTY_FUNCTION__ << " path: " << path + << std::errc::is_a_directory << std::endl; + return false; + } FileDetails details(path, period, flags); - details.info.age = static_cast(time(nullptr) + period); + details.info.age = static_cast(timeOut); { RS_STACK_MUTEX(extMutex); @@ -492,8 +484,7 @@ bool ftExtraList::loadList(std::list& load) if (ts > (rstime_t)fi->file.age) { - /* to old */ - cleanupEntry(fi->file.path, TransferRequestFlags(fi->flags)); + /* too old */ delete (*it); continue ; } diff --git a/libretroshare/src/ft/ftextralist.h b/libretroshare/src/ft/ftextralist.h index 52bc87c98..229c0fe3c 100644 --- a/libretroshare/src/ft/ftextralist.h +++ b/libretroshare/src/ft/ftextralist.h @@ -60,7 +60,7 @@ #include "pqi/p3cfgmgr.h" #include "util/rstime.h" -class FileDetails +class RS_DEPRECATED_FOR(FileInfo) FileDetails { public: FileDetails() @@ -130,7 +130,11 @@ public: * file is removed after period. **/ - bool hashExtraFile(std::string path, uint32_t period, TransferRequestFlags flags); + /** + * Hash file, and add to the files, file is removed after period. + */ + bool hashExtraFile( + std::string path, uint32_t period, TransferRequestFlags flags ); bool hashExtraFileDone(std::string path, FileInfo &info); /*** @@ -165,7 +169,6 @@ private: /* Worker Functions */ void hashAFile(); bool cleanupOldFiles(); - bool cleanupEntry(std::string path, TransferRequestFlags flags); mutable RsMutex extMutex; diff --git a/libretroshare/src/ft/ftserver.cc b/libretroshare/src/ft/ftserver.cc index 256543da6..0f42b8437 100644 --- a/libretroshare/src/ft/ftserver.cc +++ b/libretroshare/src/ft/ftserver.cc @@ -20,6 +20,11 @@ * * *******************************************************************************/ +#include +#include +#include +#include + #include "crypto/chacha20.h" //const int ftserverzone = 29539; @@ -40,18 +45,16 @@ #include "retroshare/rstypes.h" #include "retroshare/rspeers.h" #include "retroshare/rsinit.h" - +#include "util/cxx17retrocompat.h" #include "rsitems/rsfiletransferitems.h" #include "rsitems/rsserviceids.h" - +#include "util/rsmemory.h" #include "rsserver/p3face.h" #include "turtle/p3turtle.h" - +#include "util/rsurl.h" #include "util/rsdebug.h" #include "util/rsdir.h" #include "util/rsprint.h" - -#include #include "util/rstime.h" #ifdef RS_DEEP_FILES_INDEX @@ -66,6 +69,18 @@ #define FTSERVER_DEBUG() std::cerr << time(NULL) << " : FILE_SERVER : " << __FUNCTION__ << " : " #define FTSERVER_ERROR() std::cerr << "(EE) FILE_SERVER ERROR : " +/*static*/ const RsFilesErrorCategory RsFilesErrorCategory::instance; + +/*static*/ const std::string RsFiles::DEFAULT_FILES_BASE_URL = + "retroshare:///files"; +/*static*/ const std::string RsFiles::FILES_URL_COUNT_FIELD = "filesCount"; +/*static*/ const std::string RsFiles::FILES_URL_DATA_FIELD = "filesData"; +// Use a 5 character word so there is no mistake it isn't base64 as 5%4=1 +/*static*/ const std::string RsFiles::FILES_URL_FAGMENT_FORWARD = "fragm"; +/*static*/ const std::string RsFiles::FILES_URL_NAME_FIELD = "filesName"; +/*static*/ const std::string RsFiles::FILES_URL_SIZE_FIELD = "filesSize"; + + static const rstime_t FILE_TRANSFER_LOW_PRIORITY_TASKS_PERIOD = 5 ; // low priority tasks handling every 5 seconds static const rstime_t FILE_TRANSFER_MAX_DELAY_BEFORE_DROP_USAGE_RECORD = 10 ; // keep usage records for 10 secs at most. @@ -280,10 +295,14 @@ bool ftServer::getFileData(const RsFileHash& hash, uint64_t offset, uint32_t& re bool ftServer::alreadyHaveFile(const RsFileHash& hash, FileInfo &info) { - return mFileDatabase->search(hash, RS_FILE_HINTS_LOCAL, info); + return mFileDatabase->search( + hash, RS_FILE_HINTS_EXTRA | RS_FILE_HINTS_LOCAL, info ); } -bool ftServer::FileRequest(const std::string& fname, const RsFileHash& hash, uint64_t size, const std::string& dest, TransferRequestFlags flags, const std::list& srcIds) +bool ftServer::FileRequest( + const std::string& fname, const RsFileHash& hash, uint64_t size, + const std::string& dest, TransferRequestFlags flags, + const std::list& srcIds ) { #ifdef SERVER_DEBUG FTSERVER_DEBUG() << "Requesting " << fname << std::endl ; @@ -295,6 +314,93 @@ bool ftServer::FileRequest(const std::string& fname, const RsFileHash& hash, uin return true ; } +std::error_condition ftServer::requestFiles( + const RsFileTree& collection, const std::string& destPath, + const std::vector& srcIds, FileRequestFlags flags ) +{ + constexpr auto fname = __PRETTY_FUNCTION__; + const auto dirsCount = collection.mDirs.size(); + const auto filesCount = collection.mFiles.size(); + + Dbg2() << fname << " dirsCount: " << dirsCount + << " filesCount: " << filesCount << std::endl; + + if(!dirsCount) + { + RsErr() << fname << " Directories list empty in collection " + << std::endl; + return std::errc::not_a_directory; + } + + if(!filesCount) + { + RsErr() << fname << " Files list empty in collection " << std::endl; + return std::errc::invalid_argument; + } + + if(filesCount != collection.mTotalFiles) + { + RsErr() << fname << " Files count mismatch" << std::endl; + return std::errc::invalid_argument; + } + + std::string basePath = destPath.empty() ? getDownloadDirectory() : destPath; + // Track how many time a directory have been explored + std::vector dirsSeenCnt(dirsCount, 0); + // + using StackEntry = std::tuple; + std::deque dStack = { std::make_tuple(0, basePath) }; + + const auto exploreDir = [&](const StackEntry& se)-> std::error_condition + { + uint64_t dirHandle; std::string parentPath; + std::tie(dirHandle, parentPath) = se; + + const auto& dirData = collection.mDirs[dirHandle]; + auto& seenTimes = dirsSeenCnt[dirHandle]; + std::string dirPath = RsDirUtil::makePath(parentPath, dirData.name); + + /* This check is not perfect but is cheap and interrupt loop exploration + * before it becomes pathological */ + if(seenTimes++ > dirsCount) + { + RsErr() << fname << " loop detected! dir: " + << dirHandle << " \"" << dirPath + << "\" explored too many times" << std::endl; + return std::errc::too_many_symbolic_link_levels; + } + + for(auto fHandle: dirData.subfiles) + { + if(fHandle >= filesCount) return std::errc::argument_out_of_domain; + + const RsFileTree::FileData& fData = collection.mFiles[fHandle]; + + bool fr = + FileRequest( fData.name, fData.hash, fData.size, + dirPath, + TransferRequestFlags::fromEFT(flags), + std::list(srcIds.begin(), srcIds.end()) ); + + Dbg2() << fname << " requested: " << fr << " " + << fData.hash << " -> " << dirPath << std::endl; + } + + for(auto dHandle: dirData.subdirs) + dStack.push_back(std::make_tuple(dHandle, dirPath)); + + return std::error_condition(); + }; + + while(!dStack.empty()) + { + if(std::error_condition ec = exploreDir(dStack.front())) return ec; + dStack.pop_front(); + } + + return std::error_condition(); +} + bool ftServer::activateTunnels(const RsFileHash& hash,uint32_t encryption_policy,TransferRequestFlags flags,bool onoff) { RsFileHash hash_of_hash ; @@ -716,6 +822,14 @@ bool ftServer::ExtraFileRemove(const RsFileHash& hash) bool ftServer::ExtraFileHash( std::string localpath, rstime_t period, TransferRequestFlags flags ) { + constexpr rstime_t uintmax = std::numeric_limits::max(); + if(period > uintmax) + { + RsErr() << __PRETTY_FUNCTION__ << " period: " << period << " > " + << uintmax << std::errc::value_too_large << std::endl; + return false; + } + return mFtExtra->hashExtraFile( localpath, static_cast(period), flags ); } @@ -740,7 +854,7 @@ bool ftServer::findChildPointer(void *ref, int row, void *& result, FileSearchFl } bool ftServer::requestDirDetails( - DirDetails &details, std::uintptr_t handle, FileSearchFlags flags ) + DirDetails &details, uint64_t handle, FileSearchFlags flags ) { return RequestDirDetails(reinterpret_cast(handle), details, flags); } int ftServer::RequestDirDetails(void *ref, DirDetails &details, FileSearchFlags flags) @@ -1872,15 +1986,16 @@ void ftServer::ftReceiveSearchResult(RsTurtleFTSearchResultItem *item) hasCallback = true; std::vector cRes; - for( const auto& tfiold : item->result) - cRes.push_back(tfiold); + for(auto& res: std::as_const(item->result)) + cRes.push_back(TurtleFileInfoV2(res)); cbpt->second.first(cRes); } } // end RS_STACK_MUTEX(mSearchCallbacksMapMutex); if(!hasCallback) - RsServer::notify()->notifyTurtleSearchResult(item->PeerId(),item->request_id, item->result ); + RsServer::notify()->notifyTurtleSearchResult( + item->PeerId(), item->request_id, item->result ); } bool ftServer::receiveSearchRequest( @@ -2101,3 +2216,144 @@ RsFileItem::RsFileItem(RsFileItemType subtype) : void RsFileSearchRequestItem::clear() { queryString.clear(); } void RsFileSearchResultItem::clear() { mResults.clear(); } + + +std::error_condition RsFilesErrorCategory::default_error_condition(int ev) +const noexcept +{ + switch(static_cast(ev)) + { + case RsFilesErrorNum::FILES_HANDLE_NOT_FOUND: + return std::errc::invalid_argument; + default: + return std::error_condition(ev, *this); + } +} + +std::error_condition ftServer::dirDetailsToLink( + std::string& link, + const DirDetails& dirDetails, bool fragSneak, const std::string& baseUrl ) +{ + std::unique_ptr tFileTree = + RsFileTree::fromDirDetails(dirDetails, false); + if(!tFileTree) return std::errc::invalid_argument; + + link = tFileTree->toBase64(); + + if(!baseUrl.empty()) + { + RsUrl tUrl(baseUrl); + tUrl.setQueryKV(FILES_URL_COUNT_FIELD, + std::to_string(tFileTree->mTotalFiles) ) + .setQueryKV(FILES_URL_NAME_FIELD, dirDetails.name) + .setQueryKV( FILES_URL_SIZE_FIELD, + std::to_string(tFileTree->mTotalSize) ); + if(fragSneak) + tUrl.setQueryKV(FILES_URL_DATA_FIELD, FILES_URL_FAGMENT_FORWARD) + .setFragment(link); + else tUrl.setQueryKV(FILES_URL_DATA_FIELD, link); + link = tUrl.toString(); + } + + return std::error_condition(); +} + +std::error_condition ftServer::exportCollectionLink( + std::string& link, uint64_t handle, bool fragSneak, + const std::string& baseUrl ) +{ + DirDetails tDirDet; + if(!requestDirDetails(tDirDet, handle)) + return RsFilesErrorNum::FILES_HANDLE_NOT_FOUND; + + return dirDetailsToLink(link, tDirDet, fragSneak, baseUrl); +} + +/// @see RsFiles +std::error_condition ftServer::exportFileLink( + std::string& link, const RsFileHash& fileHash, uint64_t fileSize, + const std::string& fileName, bool fragSneak, const std::string& baseUrl ) +{ + if(fileHash.isNull() || !fileSize || fileName.empty()) + return std::errc::invalid_argument; + + DirDetails tDirDet; + tDirDet.type = DIR_TYPE_FILE; + tDirDet.name = fileName; + tDirDet.hash = fileHash; + tDirDet.count = fileSize; + + return dirDetailsToLink(link, tDirDet, fragSneak, baseUrl); +} + +std::error_condition ftServer::parseFilesLink( + const std::string& link, RsFileTree& collection ) +{ + RsUrl tUrl(link); + + { + /* Handle retrocompatibility with old stile single file links + * retroshare://file?name=$FILE_NAME&size=$FILE_SIZE&hash=$FILE_HASH + */ + if( tUrl.scheme() == "retroshare" && tUrl.host() == "file" + && tUrl.hasQueryK("name") && !tUrl.getQueryV("name")->empty() + && tUrl.hasQueryK("size") && !tUrl.getQueryV("size")->empty() + && tUrl.hasQueryK("hash") && !tUrl.getQueryV("hash")->empty() ) + { + DirDetails dt; + dt.type = DIR_TYPE_FILE; + dt.hash = RsFileHash(*tUrl.getQueryV("hash")); + dt.name = *tUrl.getQueryV("name"); + try + { + dt.count = std::stoull(*tUrl.getQueryV("size")); + std::unique_ptr ft; + if( !dt.hash.isNull() && + (ft = RsFileTree::fromDirDetails(dt, true)) ) + { + collection = *ft; + return std::error_condition(); + } + } + catch (...) {} + } + } + + { + /* Handle retrocompatibility with old stile collection links + * retroshare://collection?name=$NAME&size=$SIZE&radix=$RADIX&files=$COUNT + */ + if( tUrl.scheme() == "retroshare" && tUrl.host() == "collection" + && tUrl.hasQueryK("name") && !tUrl.getQueryV("name")->empty() + && tUrl.hasQueryK("size") && !tUrl.getQueryV("size")->empty() + && tUrl.hasQueryK("radix") && !tUrl.getQueryV("radix")->empty() + && tUrl.hasQueryK("files") && !tUrl.getQueryV("files")->empty() ) + { + try + { + // Don't need the values just check they are valid numbers + std::stoull(*tUrl.getQueryV("size")); + std::stoull(*tUrl.getQueryV("files")); + + auto ft = RsFileTree::fromRadix64(*tUrl.getQueryV("radix")); + if(ft) + { + collection = *ft; + return std::error_condition(); + } + } + catch (...) {} + } + } + + // Finaly handle the new files link format + rs_view_ptr radixPtr = + tUrl.getQueryV(FILES_URL_DATA_FIELD); + if(!radixPtr) radixPtr = &link; + else if(*radixPtr == FILES_URL_FAGMENT_FORWARD) radixPtr = &tUrl.fragment(); + + std::unique_ptr tft; std::error_condition ec; + std::tie(tft, ec) = RsFileTree::fromBase64(*radixPtr); + if(tft) collection = *tft; + return ec; +} diff --git a/libretroshare/src/ft/ftserver.h b/libretroshare/src/ft/ftserver.h index 6f719c04e..f6cc4b44c 100644 --- a/libretroshare/src/ft/ftserver.h +++ b/libretroshare/src/ft/ftserver.h @@ -207,14 +207,39 @@ public: virtual uint32_t filePermDirectDL() ; /// @see RsFiles - virtual bool turtleSearchRequest( + std::error_condition requestFiles( + const RsFileTree& collection, + const std::string& destPath = "", + const std::vector& srcIds = std::vector(), + FileRequestFlags flags = FileRequestFlags::ANONYMOUS_ROUTING + ) override; + + /// @see RsFiles + bool turtleSearchRequest( const std::string& matchString, const std::function& results)>& multiCallback, - rstime_t maxWait = 300 ); + rstime_t maxWait = 300 ) override; virtual TurtleSearchRequestId turtleSearch(const std::string& string_to_match) ; virtual TurtleSearchRequestId turtleSearch(const RsRegularExpression::LinearizedExpression& expr) ; + /// @see RsFiles + std::error_condition exportCollectionLink( + std::string& link, uint64_t handle, bool fragSneak = false, + const std::string& baseUrl = RsFiles::DEFAULT_FILES_BASE_URL + ) override; + + /// @see RsFiles + std::error_condition exportFileLink( + std::string& link, const RsFileHash& fileHash, uint64_t fileSize, + const std::string& fileName, bool fragSneak = false, + const std::string& baseUrl = RsFiles::DEFAULT_FILES_BASE_URL + ) override; + + /// @see RsFiles + std::error_condition parseFilesLink( + const std::string& link, RsFileTree& collection ) override; + /*** * Control of Downloads Priority. ***/ @@ -254,7 +279,7 @@ public: /// @see RsFiles::RequestDirDetails virtual bool requestDirDetails( - DirDetails &details, std::uintptr_t handle = 0, + DirDetails &details, uint64_t handle = 0, FileSearchFlags flags = RS_FILE_HINTS_LOCAL ); virtual bool findChildPointer(void *ref, int row, void *& result, FileSearchFlags flags) ; @@ -365,11 +390,11 @@ protected: bool findEncryptedHash(const RsPeerId& virtual_peer_id, RsFileHash& encrypted_hash); bool checkUploadLimit(const RsPeerId& pid,const RsFileHash& hash); -private: - /**** INTERNAL FUNCTIONS ***/ - //virtual int reScanDirs(); - //virtual int check_dBUpdate(); + std::error_condition dirDetailsToLink( + std::string& link, + const DirDetails& dirDetails, bool fragSneak, + const std::string& baseUrl ); private: diff --git a/libretroshare/src/gossipdiscovery/gossipdiscoveryitems.cc b/libretroshare/src/gossipdiscovery/gossipdiscoveryitems.cc index bee4c9b59..6f66684c9 100644 --- a/libretroshare/src/gossipdiscovery/gossipdiscoveryitems.cc +++ b/libretroshare/src/gossipdiscovery/gossipdiscoveryitems.cc @@ -37,6 +37,7 @@ RsItem *RsDiscSerialiser::create_item( { case RsGossipDiscoveryItemType::PGP_LIST: return new RsDiscPgpListItem(); case RsGossipDiscoveryItemType::PGP_CERT_BINARY: return new RsDiscPgpKeyItem(); + case RsGossipDiscoveryItemType::PGP_CERT: return new RsDiscPgpCertItem(); // deprecated, hanlde to suppress "unkown item" warning case RsGossipDiscoveryItemType::CONTACT: return new RsDiscContactItem(); case RsGossipDiscoveryItemType::IDENTITY_LIST: return new RsDiscIdentityListItem(); default: @@ -74,8 +75,8 @@ void RsDiscPgpKeyItem::clear() { pgpKeyId.clear(); free(bin_data); - bin_data = nullptr; - bin_len=0; + bin_data = nullptr; + bin_len = 0; } void RsDiscContactItem::clear() diff --git a/libretroshare/src/gossipdiscovery/gossipdiscoveryitems.h b/libretroshare/src/gossipdiscovery/gossipdiscoveryitems.h index f5e219b95..b77074e37 100644 --- a/libretroshare/src/gossipdiscovery/gossipdiscoveryitems.h +++ b/libretroshare/src/gossipdiscovery/gossipdiscoveryitems.h @@ -84,17 +84,44 @@ class RsDiscPgpKeyItem: public RsDiscItem { public: - RsDiscPgpKeyItem() : RsDiscItem(RsGossipDiscoveryItemType::PGP_CERT_BINARY) + RsDiscPgpKeyItem() : + RsDiscItem(RsGossipDiscoveryItemType::PGP_CERT_BINARY), + bin_data(nullptr), bin_len(0) { setPriorityLevel(QOS_PRIORITY_RS_DISC_PGP_CERT); } - virtual ~RsDiscPgpKeyItem() { delete[](bin_data);bin_data=nullptr;bin_len=0;} - + ~RsDiscPgpKeyItem() override { free(bin_data); } void clear() override; - void serial_process( RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx) override; - RsPgpId pgpKeyId; // duplicate information for practical reasons - unsigned char *bin_data; // binry key data allocated with new unsigned char[] - uint32_t bin_len; + void serial_process( + RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx ) override; + + /// duplicate information for practical reasons + RsPgpId pgpKeyId; + + unsigned char* bin_data; + uint32_t bin_len; +}; + +class RS_DEPRECATED_FOR(RsDiscPgpKeyItem) RsDiscPgpCertItem: public RsDiscItem +{ +public: + RsDiscPgpCertItem() : RsDiscItem(RsGossipDiscoveryItemType::PGP_CERT) + { setPriorityLevel(QOS_PRIORITY_RS_DISC_PGP_CERT); } + + void clear() override + { + pgpId.clear(); + pgpCert.clear(); + } + void serial_process(RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx) override + { + RsTypeSerializer::serial_process(j,ctx,pgpId,"pgpId") ; + RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_PGPCERT,pgpCert,"pgpCert") ; + } + + RsPgpId pgpId; + std::string pgpCert; }; class RsDiscContactItem: public RsDiscItem diff --git a/libretroshare/src/gossipdiscovery/p3gossipdiscovery.cc b/libretroshare/src/gossipdiscovery/p3gossipdiscovery.cc index 81dd097e0..caaea0e86 100644 --- a/libretroshare/src/gossipdiscovery/p3gossipdiscovery.cc +++ b/libretroshare/src/gossipdiscovery/p3gossipdiscovery.cc @@ -108,14 +108,6 @@ p3discovery2::p3discovery2( // Add self into PGP FriendList. mFriendList[AuthGPG::getAuthGPG()->getGPGOwnId()] = DiscPgpInfo(); - - if(rsEvents) - rsEvents->registerEventsHandler( - RsEventType::GOSSIP_DISCOVERY, - [this](std::shared_ptr event) - { - rsEventsHandler(*event); - }, mRsEventsHandle ); // mRsEventsHandle is zeroed in initializer list } @@ -260,6 +252,7 @@ int p3discovery2::handleIncoming() { RsDiscPgpListItem* pgplist = nullptr; RsDiscPgpKeyItem* pgpkey = nullptr; + RsDiscPgpCertItem* pgpcert = nullptr; // deprecated, hanlde for retro compability RsDiscContactItem* contact = nullptr; RsDiscIdentityListItem* gxsidlst = nullptr; @@ -282,6 +275,9 @@ int p3discovery2::handleIncoming() } else if((pgpkey = dynamic_cast(item)) != nullptr) recvPGPCertificate(item->PeerId(), pgpkey); + else if((pgpcert = dynamic_cast(item)) != nullptr) + // sink + delete pgpcert; else if((pgplist = dynamic_cast(item)) != nullptr) { if (pgplist->mode == RsGossipDiscoveryPgpListMode::FRIENDS) @@ -392,7 +388,12 @@ void p3discovery2::recvOwnContactInfo(const RsPeerId &fromId, const RsDiscContac setPeerVersion(fromId, item->version); - updatePeerAddresses(item); + // Hidden nodes do not need IP information. So that information is dropped. + // However, that doesn't mean hidden nodes do not know that information. Normally + // normal nodes should not send it, but old nodes still do. + + if(!mPeerMgr->isHiddenNode(rsPeers->getOwnId())) + updatePeerAddresses(item); // if the peer is not validated, we stop the exchange here @@ -454,7 +455,7 @@ void p3discovery2::recvIdentityList(const RsPeerId& pid,const std::list std::cerr << "p3discovery2::recvIdentityList(): from peer " << pid << ": " << ids.size() << " identities" << std::endl; #endif - RsIdentityUsage use_info(RS_SERVICE_TYPE_DISC,RsIdentityUsage::IDENTITY_DATA_UPDATE); + RsIdentityUsage use_info(RsServiceType::GOSSIP_DISCOVERY,RsIdentityUsage::IDENTITY_NEW_FROM_DISCOVERY); for(auto it(ids.begin());it!=ids.end();++it) { @@ -1280,11 +1281,6 @@ bool p3discovery2::setPeerVersion(const RsPeerId &peerId, const std::string &ver return true; } -void p3discovery2::rsEventsHandler(const RsEvent& event) -{ - Dbg3() << __PRETTY_FUNCTION__ << " " << static_cast(event.mType) << std::endl; -} - /*************************************************************************************/ /* AuthGPGService */ diff --git a/libretroshare/src/grouter/grouteritems.cc b/libretroshare/src/grouter/grouteritems.cc index 302465414..10c91b222 100644 --- a/libretroshare/src/grouter/grouteritems.cc +++ b/libretroshare/src/grouter/grouteritems.cc @@ -99,12 +99,10 @@ void RsGRouterGenericDataItem::serial_process(RsGenericSerializer::SerializeJob RsTypeSerializer::serial_process (j,ctx,destination_key,"destination_key") ; RsTypeSerializer::serial_process(j,ctx,service_id,"service_id") ; - RsTypeSerializer::TlvMemBlock_proxy prox(data_bytes,data_size) ; + RsTypeSerializer::RawMemoryWrapper prox(data_bytes, data_size); + RsTypeSerializer::serial_process(j, ctx, prox, "data"); - RsTypeSerializer::serial_process(j,ctx,prox,"data") ; - - if(ctx.mFlags & RsGenericSerializer::SERIALIZATION_FLAG_SIGNATURE) - return ; + if(!!(ctx.mFlags & RsSerializationFlags::SIGNATURE)) return; RsTypeSerializer::serial_process(j,ctx,signature,"signature") ; RsTypeSerializer::serial_process(j,ctx,duplication_factor,"duplication_factor") ; @@ -133,8 +131,7 @@ void RsGRouterSignedReceiptItem::serial_process(RsGenericSerializer::SerializeJo RsTypeSerializer::serial_process (j,ctx,service_id,"service_id") ; RsTypeSerializer::serial_process (j,ctx,data_hash,"data_hash") ; - if(ctx.mFlags & RsGenericSerializer::SERIALIZATION_FLAG_SIGNATURE) - return ; + if(!!(ctx.mFlags & RsSerializationFlags::SIGNATURE)) return; RsTypeSerializer::serial_process(j,ctx,signature,"signature") ; } diff --git a/libretroshare/src/grouter/grouteritems.h b/libretroshare/src/grouter/grouteritems.h index 14957ab87..0a12cb3f0 100644 --- a/libretroshare/src/grouter/grouteritems.h +++ b/libretroshare/src/grouter/grouteritems.h @@ -286,7 +286,9 @@ class RsGRouterRoutingInfoItem: public RsGRouterItem, public GRouterRoutingInfo, class RsGRouterSerialiser: public RsServiceSerializer { public: - explicit RsGRouterSerialiser(SerializationFlags flags = SERIALIZATION_FLAG_NONE) : RsServiceSerializer(RS_SERVICE_TYPE_GROUTER,RsGenericSerializer::FORMAT_BINARY,flags) {} + explicit RsGRouterSerialiser( + RsSerializationFlags flags = RsSerializationFlags::NONE ): + RsServiceSerializer(RS_SERVICE_TYPE_GROUTER, flags) {} virtual RsItem *create_item(uint16_t service,uint8_t subtype) const ; }; diff --git a/libretroshare/src/grouter/p3grouter.cc b/libretroshare/src/grouter/p3grouter.cc index 7fe2cc19e..16148d508 100644 --- a/libretroshare/src/grouter/p3grouter.cc +++ b/libretroshare/src/grouter/p3grouter.cc @@ -1658,7 +1658,9 @@ void p3GRouter::handleIncomingReceiptItem(const RsGRouterSignedReceiptItem *rece Sha1CheckSum p3GRouter::computeDataItemHash(const RsGRouterGenericDataItem *data_item) { - RsGRouterSerialiser signature_serializer(RsGenericSerializer::SERIALIZATION_FLAG_SIGNATURE | RsGenericSerializer::SERIALIZATION_FLAG_SKIP_HEADER); + RsGRouterSerialiser signature_serializer( + RsSerializationFlags::SIGNATURE | + RsSerializationFlags::SKIP_HEADER ); uint32_t signed_data_size = signature_serializer.size(const_cast(data_item)); uint32_t total_size = signed_data_size + data_item->signature.TlvSize() ; @@ -2034,7 +2036,9 @@ bool p3GRouter::signDataItem(RsGRouterAbstractMsgItem *item,const RsGxsId& signi std::cerr << " Key ID = " << signing_id << std::endl; std::cerr << " Getting key material..." << std::endl; //#endif - RsGRouterSerialiser signature_serializer(RsGenericSerializer::SERIALIZATION_FLAG_SIGNATURE | RsGenericSerializer::SERIALIZATION_FLAG_SKIP_HEADER) ; + RsGRouterSerialiser signature_serializer( + RsSerializationFlags::SIGNATURE | + RsSerializationFlags::SKIP_HEADER ); uint32_t data_size = signature_serializer.size(item) ; RsTemporaryMemory data(data_size) ; @@ -2092,8 +2096,8 @@ bool p3GRouter::verifySignedDataItem(const RsGRouterAbstractMsgItem *item,const } RsGRouterSerialiser signature_serializer( - RsGenericSerializer::SERIALIZATION_FLAG_SIGNATURE | - RsGenericSerializer::SERIALIZATION_FLAG_SKIP_HEADER ); + RsSerializationFlags::SIGNATURE | + RsSerializationFlags::SKIP_HEADER ); uint32_t data_size = signature_serializer.size(const_cast(item)); // the const cast shouldn't be necessary if size() took a const. RsTemporaryMemory data(data_size); @@ -2104,7 +2108,7 @@ bool p3GRouter::verifySignedDataItem(const RsGRouterAbstractMsgItem *item,const if(!signature_serializer.serialise(const_cast(item),data,&data_size)) throw std::runtime_error("Cannot serialise signed data."); - RsIdentityUsage use(RS_SERVICE_TYPE_GROUTER,info); + RsIdentityUsage use(RsServiceType::GROUTER,info); if(!mGixs->validateData( data, data_size, item->signature, true, use, error_status )) { diff --git a/libretroshare/src/gxs/gxssecurity.cc b/libretroshare/src/gxs/gxssecurity.cc index 608e938fc..d5b377328 100644 --- a/libretroshare/src/gxs/gxssecurity.cc +++ b/libretroshare/src/gxs/gxssecurity.cc @@ -289,7 +289,7 @@ bool GxsSecurity::generateKeyPair(RsTlvPublicRSAKey& public_key,RsTlvPrivateRSAK if(!(private_key.checkKey() && public_key.checkKey())) { std::cerr << "(EE) ERROR while generating keys. Something inconsistent in flags. This is probably a bad sign!" << std::endl; - return false ; + return false ; } return true ; @@ -418,13 +418,11 @@ bool GxsSecurity::validateNxsMsg(const RsNxsMsg& msg, const RsTlvKeySignature& s /* check signature timeperiod */ if ((msgMeta.mPublishTs < key.startTS) || (key.endTS != 0 && msgMeta.mPublishTs > key.endTS)) - { - #ifdef GXS_SECURITY_DEBUG - std::cerr << " GxsSecurity::validateNxsMsg() TS out of range"; - std::cerr << std::endl; - #endif - return false; - } + { + RsWarn() << __PRETTY_FUNCTION__ << " GxsSecurity::validateNxsMsg() TS out of range for key " << msgMeta.mAuthorId + << " usage is limited to TS=[" << key.startTS << "," << key.endTS << "] and msg publish time is " << msgMeta.mPublishTs << std::endl; + return false; + } /* decode key */ const unsigned char *keyptr = (const unsigned char *) key.keyData.bin_data; @@ -653,6 +651,9 @@ bool GxsSecurity::encrypt(uint8_t *& out, uint32_t &outlen, const uint8_t *in, u try { + if(keys.empty()) + throw std::runtime_error("EVP_SealInit will not be called with 0 keys. GxsSecurity::encrypt() was called with an empty set of destination keys!") ; + for(uint32_t i=0;i &list, const std::string &attribute) { list.push_back(attribute); @@ -123,7 +121,6 @@ RsDataService::RsDataService(const std::string &serviceDir, const std::string &d : RsGeneralDataService(), mDbMutex("RsDataService"), mServiceDir(serviceDir), mDbName(dbName), mDbPath(mServiceDir + "/" + dbName), mServType(serviceType), mDb(NULL) { bool isNewDatabase = !RsDirUtil::fileExists(mDbPath); - mGrpMetaDataCache_ContainsAllDatabase = false ; mDb = new RetroDb(mDbPath, RetroDb::OPEN_READWRITE_CREATE, key); @@ -488,8 +485,7 @@ bool RsDataService::finishReleaseUpdate(int release, bool result) RsGxsGrpMetaData* RsDataService::locked_getGrpMeta(RetroCursor &c, int colOffset,bool use_cache) { #ifdef RS_DATA_SERVICE_DEBUG - std::cerr << "RsDataService::locked_getGrpMeta()"; - std::cerr << std::endl; + std::cerr << "RsDataService::locked_getGrpMeta()" << std::endl; #endif bool ok = true; @@ -506,21 +502,17 @@ RsGxsGrpMetaData* RsDataService::locked_getGrpMeta(RetroCursor &c, int colOffset RsGxsGrpMetaData* grpMeta ; RsGxsGroupId grpId(tempId) ; - if(use_cache) - { - auto it = mGrpMetaDataCache.find(grpId) ; + if(grpId.isNull()) // not in the DB! + return nullptr; - if(it != mGrpMetaDataCache.end()) - grpMeta = it->second ; - else - { - grpMeta = new RsGxsGrpMetaData(); - mGrpMetaDataCache[grpId] = grpMeta ; - } - } + if(use_cache) + grpMeta = mGrpMetaDataCache.getOrCreateMeta(grpId); else grpMeta = new RsGxsGrpMetaData(); + if(!grpMeta->mGroupId.isNull()) // the grpMeta is already initialized because it comes from the cache + return grpMeta; + grpMeta->mGroupId = RsGxsGroupId(tempId); c.getString(mColGrpMeta_NxsIdentity + colOffset, tempId); grpMeta->mAuthorId = RsGxsId(tempId); @@ -653,24 +645,40 @@ RsNxsGrp* RsDataService::locked_getGroup(RetroCursor &c) return NULL; } -RsGxsMsgMetaData* RsDataService::locked_getMsgMeta(RetroCursor &c, int colOffset) +RsGxsMsgMetaData* RsDataService::locked_getMsgMeta(RetroCursor &c, int colOffset,bool use_cache) { - RsGxsMsgMetaData* msgMeta = new RsGxsMsgMetaData(); - bool ok = true; uint32_t data_len = 0, offset = 0; char* data = NULL; + RsGxsGroupId group_id; + RsGxsMessageId msg_id; + std::string gId; c.getString(mColMsgMeta_GrpId + colOffset, gId); - msgMeta->mGroupId = RsGxsGroupId(gId); + group_id = RsGxsGroupId(gId); std::string temp; c.getString(mColMsgMeta_MsgId + colOffset, temp); - msgMeta->mMsgId = RsGxsMessageId(temp); + msg_id = RsGxsMessageId(temp); + // without these, a msg is meaningless - ok &= (!msgMeta->mGroupId.isNull()) && (!msgMeta->mMsgId.isNull()); + if(group_id.isNull() || msg_id.isNull()) + return nullptr; + + RsGxsMsgMetaData* msgMeta = nullptr; + + if(use_cache) + msgMeta = mMsgMetaDataCache[group_id].getOrCreateMeta(msg_id); + else + msgMeta = new RsGxsMsgMetaData(); + + if(!msgMeta->mGroupId.isNull()) // we cannot do that because the cursor needs to advance. Is there a method to skip some data in the db? + return msgMeta; + + msgMeta->mGroupId = group_id; + msgMeta->mMsgId = msg_id; c.getString(mColMsgMeta_OrigMsgId + colOffset, temp); msgMeta->mOrigMsgId = RsGxsMessageId(temp); @@ -704,7 +712,7 @@ RsGxsMsgMetaData* RsDataService::locked_getMsgMeta(RetroCursor &c, int colOffset if(ok) return msgMeta; - else + else if(!use_cache) delete msgMeta; return NULL; @@ -834,7 +842,8 @@ int RsDataService::storeMessage(const std::list& msg) // This is needed so that mLastPost is correctly updated in the group meta when it is re-loaded. - locked_clearGrpMetaCache(msgMetaPtr->mGroupId); + mGrpMetaDataCache.clear(msgMetaPtr->mGroupId); + mMsgMetaDataCache[msgMetaPtr->mGroupId].updateMeta(msgMetaPtr->mMsgId,*msgMetaPtr); } // finish transaction @@ -926,7 +935,7 @@ int RsDataService::storeGroup(const std::list& grp) cv.put(KEY_GRP_STATUS, (int32_t)grpMetaPtr->mGroupStatus); cv.put(KEY_GRP_LAST_POST, (int32_t)grpMetaPtr->mLastPost); - locked_updateGrpMetaCache(*grpMetaPtr); + mGrpMetaDataCache.updateMeta(grpMetaPtr->mGroupId,*grpMetaPtr); if (!mDb->sqlInsert(GRP_TABLE_NAME, "", cv)) { @@ -942,54 +951,6 @@ int RsDataService::storeGroup(const std::list& grp) return ret; } -void RsDataService::locked_updateGrpMetaCache(const RsGxsGrpMetaData& meta) -{ - auto it = mGrpMetaDataCache.find(meta.mGroupId) ; - - if(it != mGrpMetaDataCache.end()) - *(it->second) = meta ; - else - mGrpMetaDataCache[meta.mGroupId] = new RsGxsGrpMetaData(meta) ; -} - -void RsDataService::locked_clearGrpMetaCache(const RsGxsGroupId& gid) -{ - rstime_t now = time(NULL) ; - auto it = mGrpMetaDataCache.find(gid) ; - - // We dont actually delete the item, because it might be used by a calling client. - // In this case, the memory will not be used for long, so we keep it into a list for a safe amount - // of time and delete it later. Using smart pointers here would be more elegant, but that would need - // to be implemented thread safe, which is difficult in this case. - - if(it != mGrpMetaDataCache.end()) - { -#ifdef RS_DATA_SERVICE_DEBUG - std::cerr << "(II) moving database cache entry " << (void*)(*it).second << " to dead list." << std::endl; -#endif - - mOldCachedItems.push_back(std::make_pair(now,it->second)) ; - - mGrpMetaDataCache.erase(it) ; - mGrpMetaDataCache_ContainsAllDatabase = false; - } - - // We also take that opportunity to delete old entries. - - auto it2(mOldCachedItems.begin()); - - while(it2!=mOldCachedItems.end() && (*it2).first + CACHE_ENTRY_GRACE_PERIOD < now) - { -#ifdef RS_DATA_SERVICE_DEBUG - std::cerr << "(II) deleting old GXS database cache entry " << (void*)(*it2).second << ", " << now - (*it2).first << " seconds old." << std::endl; -#endif - - delete (*it2).second ; - it2 = mOldCachedItems.erase(it2) ; - } - -} - int RsDataService::updateGroup(const std::list &grp) { @@ -1058,7 +1019,7 @@ int RsDataService::updateGroup(const std::list &grp) mDb->sqlUpdate(GRP_TABLE_NAME, "grpId='" + grpPtr->grpId.toStdString() + "'", cv); - locked_updateGrpMetaCache(*grpMetaPtr); + mGrpMetaDataCache.updateMeta(grpMetaPtr->mGroupId,*grpMetaPtr); } // finish transaction bool ret = mDb->commitTransaction(); @@ -1275,7 +1236,7 @@ void RsDataService::locked_retrieveMessages(RetroCursor *c, std::vectormetaData = locked_getMsgMeta(*c, metaOffset); + m->metaData = locked_getMsgMeta(*c, metaOffset,false); } msgs.push_back(m); } @@ -1285,7 +1246,7 @@ void RsDataService::locked_retrieveMessages(RetroCursor *c, std::vectorfirst; + const std::set& msgIdV = mit->second; // if vector empty then request all messages - const std::set& msgIdV = mit->second; - std::vector metaSet; - if(msgIdV.empty()){ - RetroCursor* c = mDb->sqlQuery(MSG_TABLE_NAME, mMsgMetaColumns, KEY_GRP_ID+ "='" + grpId.toStdString() + "'", ""); + t_MetaDataCache& cache(mMsgMetaDataCache[grpId]); - if (c) - { - locked_retrieveMsgMeta(c, metaSet); + if(msgIdV.empty()) + { + if(cache.isCacheUpToDate()) + cache.getFullMetaList(msgMeta[grpId]); + else + { + RetroCursor* c = mDb->sqlQuery(MSG_TABLE_NAME, mMsgMetaColumns, KEY_GRP_ID+ "='" + grpId.toStdString() + "'", ""); + + if (c) + { + locked_retrieveMsgMetaList(c, msgMeta[grpId]); + cache.setCacheUpToDate(true); + } + delete c; + } #ifdef RS_DATA_SERVICE_DEBUG_CACHE - std::cerr << "Retrieving (all) Msg metadata grpId=" << grpId << ", " << std::dec << metaSet.size() << " messages" << std::endl; + std::cerr << mDbName << ": Retrieving (all) Msg metadata grpId=" << grpId << ", " << std::dec << metaSet.size() << " messages" << std::endl; #endif - } - }else{ - - // request each grp - std::set::const_iterator sit = msgIdV.begin(); - - for(; sit!=msgIdV.end(); ++sit){ - const RsGxsMessageId& msgId = *sit; - RetroCursor* c = mDb->sqlQuery(MSG_TABLE_NAME, mMsgMetaColumns, KEY_GRP_ID+ "='" + grpId.toStdString() - + "' AND " + KEY_MSG_ID + "='" + msgId.toStdString() + "'", ""); - - if (c) - { - locked_retrieveMsgMeta(c, metaSet); -#ifdef RS_DATA_SERVICE_DEBUG_CACHE - std::cerr << "Retrieving Msg metadata grpId=" << grpId << ", " << std::dec << metaSet.size() << " messages" << std::endl; -#endif - } - } } + else + { + // request each msg meta + auto& metaSet(msgMeta[grpId]); -#ifdef RS_DATA_SERVICE_DEBUG_TIME - resultCount += metaSet.size(); + for(auto sit(msgIdV.begin()); sit!=msgIdV.end(); ++sit) + { + const RsGxsMessageId& msgId = *sit; + + RsGxsMsgMetaData *meta = cache.getMeta(msgId); + + if(meta) + metaSet.push_back(meta); + else + { + RetroCursor* c = mDb->sqlQuery(MSG_TABLE_NAME, mMsgMetaColumns, KEY_GRP_ID+ "='" + grpId.toStdString() + "' AND " + KEY_MSG_ID + "='" + msgId.toStdString() + "'", ""); + + c->moveToFirst(); + RsGxsMsgMetaData* meta = locked_getMsgMeta(*c, 0,true); + + if(meta) + metaSet.push_back(meta); + + delete c; + } + } +#ifdef RS_DATA_SERVICE_DEBUG_CACHE + std::cerr << mDbName << ": Retrieving Msg metadata grpId=" << grpId << ", " << std::dec << metaSet.size() << " messages" << std::endl; #endif - - msgMeta[grpId] = metaSet; + } } #ifdef RS_DATA_SERVICE_DEBUG_TIME + if(mDbName==std::string("gxsforums_db")) std::cerr << "RsDataService::retrieveGxsMsgMetaData() " << mDbName << ", Requests: " << reqIds.size() << ", Results: " << resultCount << ", Time: " << timer.duration() << std::endl; #endif return 1; } -void RsDataService::locked_retrieveMsgMeta(RetroCursor *c, std::vector &msgMeta) +void RsDataService::locked_retrieveGrpMetaList(RetroCursor *c, std::map& grpMeta) { + if(!c) + { + RsErr() << __PRETTY_FUNCTION__ << ": attempt to retrieve Group Meta data from the DB with null cursor!" << std::endl; + return; + } - if(c) - { - bool valid = c->moveToFirst(); - while(valid){ - RsGxsMsgMetaData* m = locked_getMsgMeta(*c, 0); + bool valid = c->moveToFirst(); - if(m != NULL) - msgMeta.push_back(m); + while(valid) + { + RsGxsGrpMetaData* m = locked_getGrpMeta(*c, 0,true); - valid = c->moveToNext(); - } - delete c; - } + if(m) + grpMeta[m->mGroupId] = m; + + valid = c->moveToNext(); + } +} +void RsDataService::locked_retrieveMsgMetaList(RetroCursor *c, std::vector& msgMeta) +{ + if(!c) + { + RsErr() << __PRETTY_FUNCTION__ << ": attempt to retrieve Msg Meta data from the DB with null cursor!" << std::endl; + return; + } + + bool valid = c->moveToFirst(); + while(valid){ + const RsGxsMsgMetaData* m = locked_getMsgMeta(*c, 0,true); + + if(m != NULL) + msgMeta.push_back(m); + + valid = c->moveToNext(); + } } int RsDataService::retrieveGxsGrpMetaData(RsGxsGrpMetaTemporaryMap& grp) @@ -1384,13 +1380,13 @@ int RsDataService::retrieveGxsGrpMetaData(RsGxsGrpMetaTemporaryMap& grp) if(grp.empty()) { - if(mGrpMetaDataCache_ContainsAllDatabase) // grab all the stash from the cache, so as to avoid decryption costs. + if(mGrpMetaDataCache.isCacheUpToDate()) // grab all the stash from the cache, so as to avoid decryption costs. { #ifdef RS_DATA_SERVICE_DEBUG_CACHE std::cerr << (void*)this << ": RsDataService::retrieveGxsGrpMetaData() retrieving all from cache!" << std::endl; #endif - grp = mGrpMetaDataCache ; + mGrpMetaDataCache.getFullMetaList(grp) ; } else { @@ -1401,93 +1397,103 @@ int RsDataService::retrieveGxsGrpMetaData(RsGxsGrpMetaTemporaryMap& grp) RetroCursor* c = mDb->sqlQuery(GRP_TABLE_NAME, mGrpMetaColumns, "", ""); - if(c) + if(c) { - bool valid = c->moveToFirst(); + locked_retrieveGrpMetaList(c,grp); - while(valid) - { - RsGxsGrpMetaData* g = locked_getGrpMeta(*c, 0,true); - - if(g) - { - grp[g->mGroupId] = g; -#ifdef RS_DATA_SERVICE_DEBUG_CACHE - std::cerr << (void *)this << ": Retrieving (all) Grp metadata grpId=" << g->mGroupId << std::endl; -#endif - } - valid = c->moveToNext(); - -#ifdef RS_DATA_SERVICE_DEBUG_TIME - ++resultCount; -#endif - } - delete c; + mGrpMetaDataCache.setCacheUpToDate(true); } + delete c; +#ifdef RS_DATA_SERVICE_DEBUG_TIME + resultCount += grp.size(); +#endif - mGrpMetaDataCache_ContainsAllDatabase = true ; +// if(c) +// { +// bool valid = c->moveToFirst(); +// +// while(valid) +// { +// RsGxsGrpMetaData* g = locked_getGrpMeta(*c, 0,true); +// +// if(g) +// { +// grp[g->mGroupId] = g; +//#ifdef RS_DATA_SERVICE_DEBUG_CACHE +// std::cerr << (void *)this << " " << mDbName << ": Retrieving (all) Grp metadata grpId=" << g->mGroupId << std::endl; +//#endif +// } +// valid = c->moveToNext(); +// +// } +// delete c; +// } } - } else - { - std::map::iterator mit = grp.begin(); + { + for(auto mit(grp.begin()); mit != grp.end(); ++mit) + { + RsGxsGrpMetaData *meta = mGrpMetaDataCache.getMeta(mit->first) ; - for(; mit != grp.end(); ++mit) - { - std::map::const_iterator itt = mGrpMetaDataCache.find(mit->first) ; - - if(itt != mGrpMetaDataCache.end()) - { + if(meta) + mit->second = meta; + else + { #ifdef RS_DATA_SERVICE_DEBUG_CACHE - std::cerr << "Retrieving Grp metadata grpId=" << mit->first << " from cache!" << std::endl; -#endif - grp[mit->first] = itt->second ; - } - else - { -#ifdef RS_DATA_SERVICE_DEBUG_CACHE - std::cerr << "Retrieving Grp metadata grpId=" << mit->first ; + std::cerr << mDbName << ": Retrieving Grp metadata grpId=" << mit->first ; #endif - const RsGxsGroupId& grpId = mit->first; - RetroCursor* c = mDb->sqlQuery(GRP_TABLE_NAME, mGrpMetaColumns, "grpId='" + grpId.toStdString() + "'", ""); + const RsGxsGroupId& grpId = mit->first; + RetroCursor* c = mDb->sqlQuery(GRP_TABLE_NAME, mGrpMetaColumns, "grpId='" + grpId.toStdString() + "'", ""); - if(c) - { - bool valid = c->moveToFirst(); + c->moveToFirst(); + RsGxsGrpMetaData* meta = locked_getGrpMeta(*c, 0,true); -#ifdef RS_DATA_SERVICE_DEBUG_CACHE - if(!valid) - std::cerr << " Empty query! GrpId " << grpId << " is not in database" << std::endl; -#endif - while(valid) - { - RsGxsGrpMetaData* g = locked_getGrpMeta(*c, 0,true); - - if(g) - { - grp[g->mGroupId] = g; -#ifdef RS_DATA_SERVICE_DEBUG_CACHE - std::cerr << ". Got it. Updating cache." << std::endl; -#endif - } - valid = c->moveToNext(); + if(meta) + mit->second = meta; #ifdef RS_DATA_SERVICE_DEBUG_TIME - ++resultCount; + ++resultCount; #endif - } - delete c; - } -#ifdef RS_DATA_SERVICE_DEBUG_CACHE - else - std::cerr << ". not found!" << std::endl; -#endif - } - } - } + delete c; + + // if(c) + // { + // bool valid = c->moveToFirst(); + // + //#ifdef RS_DATA_SERVICE_DEBUG_CACHE + // if(!valid) + // std::cerr << " Empty query! GrpId " << grpId << " is not in database" << std::endl; + //#endif + // while(valid) + // { + // RsGxsGrpMetaData* g = locked_getGrpMeta(*c, 0,true); + // + // if(g) + // { + // grp[g->mGroupId] = g; + //#ifdef RS_DATA_SERVICE_DEBUG_CACHE + // std::cerr << ". Got it. Updating cache." << std::endl; + //#endif + // } + // valid = c->moveToNext(); + // + //#ifdef RS_DATA_SERVICE_DEBUG_TIME + // ++resultCount; + //#endif + // } + // delete c; + // } +#ifdef RS_DATA_SERVICE_DEBUG_CACHE + else + std::cerr << ". not found!" << std::endl; +#endif + } + } + + } #ifdef RS_DATA_SERVICE_DEBUG_TIME std::cerr << "RsDataService::retrieveGxsGrpMetaData() " << mDbName << ", Requests: " << requestedGroups << ", Results: " << resultCount << ", Time: " << timer.duration() << std::endl; @@ -1524,35 +1530,37 @@ int RsDataService::resetDataStore() return 1; } -int RsDataService::updateGroupMetaData(GrpLocMetaData &meta) +int RsDataService::updateGroupMetaData(const GrpLocMetaData& meta) { #ifdef RS_DATA_SERVICE_DEBUG_CACHE std::cerr << (void*)this << ": Updating Grp Meta data: grpId = " << meta.grpId << std::endl; #endif RsStackMutex stack(mDbMutex); - RsGxsGroupId& grpId = meta.grpId; + const RsGxsGroupId& grpId = meta.grpId; #ifdef RS_DATA_SERVICE_DEBUG_CACHE std::cerr << (void*)this << ": erasing old entry from cache." << std::endl; #endif - locked_clearGrpMetaCache(meta.grpId); + mGrpMetaDataCache.clear(meta.grpId); return mDb->sqlUpdate(GRP_TABLE_NAME, KEY_GRP_ID+ "='" + grpId.toStdString() + "'", meta.val) ? 1 : 0; } -int RsDataService::updateMessageMetaData(MsgLocMetaData &metaData) +int RsDataService::updateMessageMetaData(const MsgLocMetaData& metaData) { #ifdef RS_DATA_SERVICE_DEBUG_CACHE std::cerr << (void*)this << ": Updating Msg Meta data: grpId = " << metaData.msgId.first << " msgId = " << metaData.msgId.second << std::endl; #endif RsStackMutex stack(mDbMutex); - RsGxsGroupId& grpId = metaData.msgId.first; - RsGxsMessageId& msgId = metaData.msgId.second; - return mDb->sqlUpdate(MSG_TABLE_NAME, KEY_GRP_ID+ "='" + grpId.toStdString() - + "' AND " + KEY_MSG_ID + "='" + msgId.toStdString() + "'", metaData.val) ? 1 : 0; + const RsGxsGroupId& grpId = metaData.msgId.first; + const RsGxsMessageId& msgId = metaData.msgId.second; + + mMsgMetaDataCache[grpId].clear(msgId); + + return mDb->sqlUpdate(MSG_TABLE_NAME, KEY_GRP_ID+ "='" + grpId.toStdString() + "' AND " + KEY_MSG_ID + "='" + msgId.toStdString() + "'", metaData.val) ? 1 : 0; } int RsDataService::removeMsgs(const GxsMsgReq& msgIds) @@ -1677,13 +1685,13 @@ bool RsDataService::locked_removeMessageEntries(const GxsMsgReq& msgIds) { const RsGxsGroupId& grpId = mit->first; const std::set& msgsV = mit->second; - std::set::const_iterator vit = msgsV.begin(); + auto& cache(mMsgMetaDataCache[grpId]); - for(; vit != msgsV.end(); ++vit) + for(auto& msgId:msgsV) { - const RsGxsMessageId& msgId = *vit; - mDb->sqlDelete(MSG_TABLE_NAME, KEY_GRP_ID+ "='" + grpId.toStdString() - + "' AND " + KEY_MSG_ID + "='" + msgId.toStdString() + "'", ""); + mDb->sqlDelete(MSG_TABLE_NAME, KEY_GRP_ID+ "='" + grpId.toStdString() + "' AND " + KEY_MSG_ID + "='" + msgId.toStdString() + "'", ""); + + cache.clear(msgId); } } @@ -1697,23 +1705,18 @@ bool RsDataService::locked_removeGroupEntries(const std::vector& g // start a transaction bool ret = mDb->beginTransaction(); - std::vector::const_iterator vit = grpIds.begin(); - - for(; vit != grpIds.end(); ++vit) + for(auto grpId:grpIds) { - - const RsGxsGroupId& grpId = *vit; mDb->sqlDelete(GRP_TABLE_NAME, KEY_GRP_ID+ "='" + grpId.toStdString() + "'", ""); // also remove the group meta from cache. - locked_clearGrpMetaCache(*vit) ; + mGrpMetaDataCache.clear(grpId) ; } ret &= mDb->commitTransaction(); - - mGrpMetaDataCache_ContainsAllDatabase = false ; return ret; } + uint32_t RsDataService::cacheSize() const { return 0; } @@ -1723,3 +1726,38 @@ int RsDataService::setCacheSize(uint32_t /* size */) return 0; } +void RsDataService::debug_printCacheSize() const +{ + uint32_t nb_items,nb_items_on_deadlist; + uint64_t total_size,total_size_of_deadlist; + + mGrpMetaDataCache.debug_computeSize(nb_items, nb_items_on_deadlist, total_size,total_size_of_deadlist); + + RsDbg() << "Cache size: " << std::endl; + RsDbg() << " Groups: " << " total: " << nb_items << " (dead: " << nb_items_on_deadlist << "), size: " << total_size << " (Dead: " << total_size_of_deadlist << ")" << std::endl; + + nb_items = 0,nb_items_on_deadlist = 0; + total_size = 0,total_size_of_deadlist = 0; + + for(auto it:mMsgMetaDataCache) + { + uint32_t tmp_nb_items,tmp_nb_items_on_deadlist; + uint64_t tmp_total_size,tmp_total_size_of_deadlist; + + it.second.debug_computeSize(tmp_nb_items, tmp_nb_items_on_deadlist, tmp_total_size,tmp_total_size_of_deadlist); + + nb_items += tmp_nb_items; + nb_items_on_deadlist += tmp_nb_items_on_deadlist; + total_size += tmp_total_size; + total_size_of_deadlist += tmp_total_size_of_deadlist; + } + RsDbg() << " Msgs: " << " total: " << nb_items << " (dead: " << nb_items_on_deadlist << "), size: " << total_size << " (Dead: " << total_size_of_deadlist << ")" << std::endl; +} + + + + + + + + diff --git a/libretroshare/src/gxs/rsdataservice.h b/libretroshare/src/gxs/rsdataservice.h index 8b4e2aac9..bf532afae 100644 --- a/libretroshare/src/gxs/rsdataservice.h +++ b/libretroshare/src/gxs/rsdataservice.h @@ -35,6 +35,124 @@ public: ContentValue cv; }; +template class t_MetaDataCache +{ +public: + t_MetaDataCache() : mCache_ContainsAllMetas(false) {} + virtual ~t_MetaDataCache() + { + for(auto it: mMetas) + delete it.second; + + for(auto it: mOldCachedItems) + delete it.second; + } + + bool isCacheUpToDate() const { return mCache_ContainsAllMetas ; } + void setCacheUpToDate(bool b) { mCache_ContainsAllMetas = b; } + + void getFullMetaList(std::map& mp) const { mp = mMetas ; } + void getFullMetaList(std::vector& mp) const { for(auto& m:mMetas) mp.push_back(m.second) ; } + + MetaDataClass *getMeta(const ID& id) + { + auto itt = mMetas.find(id); + + if(itt != mMetas.end()) + return itt->second ; + else + return NULL; + } + + MetaDataClass *getOrCreateMeta(const ID& id) + { + MetaDataClass *meta = nullptr; + auto it = mMetas.find(id) ; + + if(it != mMetas.end()) + { +#ifdef RS_DATA_SERVICE_DEBUG + RsDbg() << __PRETTY_FUNCTION__ << ": getting group meta " << grpId << " from cache." << std::endl; +#endif + meta = it->second ; + } + else + { +#ifdef RS_DATA_SERVICE_DEBUG + RsDbg() << __PRETTY_FUNCTION__ << ": group meta " << grpId << " not in cache. Loading it from DB..." << std::endl; +#endif + meta = new MetaDataClass(); + mMetas[id] = meta ; + } + + return meta; + } + + void updateMeta(const ID& id,const MetaDataClass& meta) + { + auto it = mMetas.find(id) ; + + if(it != mMetas.end()) + *(it->second) = meta ; + else + mMetas[id] = new MetaDataClass(meta) ; + } + + void clear(const ID& id) + { + rstime_t now = time(NULL) ; + auto it = mMetas.find(id) ; + + // We dont actually delete the item, because it might be used by a calling client. + // In this case, the memory will not be used for long, so we keep it into a list for a safe amount + // of time and delete it later. Using smart pointers here would be more elegant, but that would need + // to be implemented thread safe, which is difficult in this case. + + if(it != mMetas.end()) + { +#ifdef RS_DATA_SERVICE_DEBUG + std::cerr << "(II) moving database cache entry " << (void*)(*it).second << " to dead list." << std::endl; +#endif + + mOldCachedItems.push_back(std::make_pair(now,it->second)) ; + + mMetas.erase(it) ; + mCache_ContainsAllMetas = false; + } + + // We also take that opportunity to delete old entries. + + auto it2(mOldCachedItems.begin()); + + while(it2!=mOldCachedItems.end() && (*it2).first + CACHE_ENTRY_GRACE_PERIOD < now) + { +#ifdef RS_DATA_SERVICE_DEBUG + std::cerr << "(II) deleting old GXS database cache entry " << (void*)(*it2).second << ", " << now - (*it2).first << " seconds old." << std::endl; +#endif + delete (*it2).second ; + it2 = mOldCachedItems.erase(it2) ; + } + } + + void debug_computeSize(uint32_t& nb_items, uint32_t& nb_items_on_deadlist, uint64_t& total_size,uint64_t& total_size_of_deadlist) const + { + nb_items = mMetas.size(); + nb_items_on_deadlist = mOldCachedItems.size(); + total_size = 0; + total_size_of_deadlist = 0; + + for(auto it:mMetas) total_size += it.second->serial_size(); + for(auto it:mOldCachedItems) total_size_of_deadlist += it.second->serial_size(); + } +private: + std::map mMetas; + std::list > mOldCachedItems ; // dead list, where items get deleted after being unused for a while. This is due to not using smart ptrs. + + static const uint32_t CACHE_ENTRY_GRACE_PERIOD = 600 ; // Unused items are deleted 10 minutes after last usage. + + bool mCache_ContainsAllMetas ; +}; + class RsDataService : public RsGeneralDataService { public: @@ -147,13 +265,13 @@ public: * @param metaData The meta data item to update * @return error code */ - int updateMessageMetaData(MsgLocMetaData& metaData); + int updateMessageMetaData(const MsgLocMetaData& metaData); /*! * @param metaData The meta data item to update * @return error code */ - int updateGroupMetaData(GrpLocMetaData& meta); + int updateGroupMetaData(const GrpLocMetaData &meta); /*! * Completely clear out data stored in @@ -174,6 +292,8 @@ public: int updateGroupKeys(const RsGxsGroupId& grpId,const RsTlvSecurityKeySet& keys, uint32_t subscribe_flags) ; + void debug_printCacheSize() const; + private: /*! @@ -194,15 +314,22 @@ private: /*! * Retrieves all the msg meta results from a cursor * @param c cursor to result set - * @param metaSet message metadata retrieved from cursor are stored here + * @param msgMeta message metadata retrieved from cursor are stored here */ - void locked_retrieveMsgMeta(RetroCursor* c, std::vector& msgMeta); + void locked_retrieveMsgMetaList(RetroCursor* c, std::vector& msgMeta); + + /*! + * Retrieves all the grp meta results from a cursor + * @param c cursor to result set + * @param grpMeta group metadata retrieved from cursor are stored here + */ + void locked_retrieveGrpMetaList(RetroCursor *c, std::map& grpMeta); /*! * extracts a msg meta item from a cursor at its * current position */ - RsGxsMsgMetaData* locked_getMsgMeta(RetroCursor& c, int colOffset); + RsGxsMsgMetaData* locked_getMsgMeta(RetroCursor& c, int colOffset, bool use_cache); /*! * extracts a grp meta item from a cursor at its @@ -348,10 +475,8 @@ private: void locked_clearGrpMetaCache(const RsGxsGroupId& gid); void locked_updateGrpMetaCache(const RsGxsGrpMetaData& meta); - std::map mGrpMetaDataCache ; - std::list > mOldCachedItems ; - - bool mGrpMetaDataCache_ContainsAllDatabase ; + t_MetaDataCache mGrpMetaDataCache; + std::map > mMsgMetaDataCache; }; #endif // RSDATASERVICE_H diff --git a/libretroshare/src/gxs/rsgds.h b/libretroshare/src/gxs/rsgds.h index 4e67f9d8c..fb985b9f7 100644 --- a/libretroshare/src/gxs/rsgds.h +++ b/libretroshare/src/gxs/rsgds.h @@ -149,7 +149,7 @@ public: bool withMeta = false ) = 0; /*! - * Retrieves all groups stored + * Retrieves all groups stored. Caller owns the memory and is supposed to delete the RsNxsGrp pointers after use. * @param grp retrieved groups * @param withMeta if true the meta handle of nxs grps is intitialised * @param cache whether to store retrieval in mem for faster later retrieval @@ -239,12 +239,12 @@ public: /*! * @param metaData */ - virtual int updateMessageMetaData(MsgLocMetaData& metaData) = 0; + virtual int updateMessageMetaData(const MsgLocMetaData& metaData) = 0; /*! * @param metaData */ - virtual int updateGroupMetaData(GrpLocMetaData& meta) = 0; + virtual int updateGroupMetaData(const GrpLocMetaData& meta) = 0; virtual int updateGroupKeys(const RsGxsGroupId& grpId,const RsTlvSecurityKeySet& keys,uint32_t subscribed_flags) = 0 ; diff --git a/libretroshare/src/gxs/rsgenexchange.cc b/libretroshare/src/gxs/rsgenexchange.cc index a973bc1ef..2d77e6da7 100644 --- a/libretroshare/src/gxs/rsgenexchange.cc +++ b/libretroshare/src/gxs/rsgenexchange.cc @@ -61,7 +61,7 @@ static const uint32_t INDEX_AUTHEN_ADMIN = 0x00000040; // admin key #define GXS_MASK "GXS_MASK_HACK" -//#define GEN_EXCH_DEBUG 1 +#define GEN_EXCH_DEBUG 1 static const uint32_t MSG_CLEANUP_PERIOD = 60*59; // 59 minutes static const uint32_t INTEGRITY_CHECK_PERIOD = 60*31; // 31 minutes @@ -236,14 +236,17 @@ void RsGenExchange::tick() if (!grpIds.empty()) { - RsGxsGroupChange* gc = new RsGxsGroupChange(RsGxsNotify::TYPE_PROCESSED, false); - gc->mGrpIdList = grpIds; + for(auto& groupId:grpIds) + { + RsGxsGroupChange* gc = new RsGxsGroupChange(RsGxsNotify::TYPE_GROUP_DELETED,groupId, false); + #ifdef GEN_EXCH_DEBUG - std::cerr << " adding the following grp ids to notification: " << std::endl; - for(std::list::const_iterator it(grpIds.begin());it!=grpIds.end();++it) - std::cerr << " " << *it << std::endl; + std::cerr << " adding the following grp ids to notification: " << std::endl; + for(std::list::const_iterator it(grpIds.begin());it!=grpIds.end();++it) + std::cerr << " " << *it << std::endl; #endif - mNotifications.push_back(gc); + mNotifications.push_back(gc); + } // also notify the network exchange service that these groups no longer exist. @@ -251,12 +254,12 @@ void RsGenExchange::tick() mNetService->removeGroups(grpIds) ; } - if (!msgIds.empty()) - { - RsGxsMsgChange* c = new RsGxsMsgChange(RsGxsNotify::TYPE_PROCESSED, false); - c->msgChangeMap = msgIds; - mNotifications.push_back(c); - } + for(auto it(msgIds.begin());it!=msgIds.end();++it) + for(auto& msgId:it->second) + { + RsGxsMsgChange* c = new RsGxsMsgChange(RsGxsNotify::TYPE_MESSAGE_DELETED,it->first, msgId, false); + mNotifications.push_back(c); + } delete mIntegrityCheck; mIntegrityCheck = NULL; @@ -279,7 +282,7 @@ bool RsGenExchange::messagePublicationTest(const RsGxsMsgMetaData& meta) rstime_t storageTimeLimit = meta.mPublishTs + st; - return meta.mMsgStatus & GXS_SERV::GXS_MSG_STATUS_KEEP || st == 0 || storageTimeLimit >= time(NULL); + return meta.mMsgStatus & GXS_SERV::GXS_MSG_STATUS_KEEP_FOREVER || st == 0 || storageTimeLimit >= time(NULL); } bool RsGenExchange::acknowledgeTokenMsg(const uint32_t& token, @@ -509,7 +512,7 @@ int RsGenExchange::createGroupSignatures(RsTlvKeySignatureSet& signSet, RsTlvBin if(GxsSecurity::getSignature((char*)grpData.bin_data, grpData.bin_len, authorKey, sign)) { id_ret = SIGN_SUCCESS; - mGixs->timeStampKey(grpMeta.mAuthorId,RsIdentityUsage(mServType,RsIdentityUsage::GROUP_AUTHOR_SIGNATURE_CREATION,grpMeta.mGroupId)) ; + mGixs->timeStampKey(grpMeta.mAuthorId,RsIdentityUsage(RsServiceType(mServType),RsIdentityUsage::GROUP_AUTHOR_SIGNATURE_CREATION,grpMeta.mGroupId)) ; signSet.keySignSet[INDEX_AUTHEN_IDENTITY] = sign; } else @@ -677,7 +680,7 @@ int RsGenExchange::createMsgSignatures(RsTlvKeySignatureSet& signSet, RsTlvBinar if(GxsSecurity::getSignature((char*)msgData.bin_data, msgData.bin_len, authorKey, sign)) { id_ret = SIGN_SUCCESS; - mGixs->timeStampKey(msgMeta.mAuthorId,RsIdentityUsage(mServType,RsIdentityUsage::MESSAGE_AUTHOR_SIGNATURE_CREATION,msgMeta.mGroupId,msgMeta.mMsgId)) ; + mGixs->timeStampKey(msgMeta.mAuthorId,RsIdentityUsage(RsServiceType(mServType),RsIdentityUsage::MESSAGE_AUTHOR_SIGNATURE_CREATION,msgMeta.mGroupId,msgMeta.mMsgId,msgMeta.mParentId,msgMeta.mThreadId)) ; signSet.keySignSet[INDEX_AUTHEN_IDENTITY] = sign; } else @@ -770,7 +773,7 @@ int RsGenExchange::createMessage(RsNxsMsg* msg) hash.addData(allMsgData, allMsgDataLen); RsFileHash hashId; hash.Complete(hashId); - msg->msgId = hashId; + msg->msgId = RsGxsMessageId(hashId); // assign msg id to msg meta msg->metaData->mMsgId = msg->msgId; @@ -902,7 +905,11 @@ int RsGenExchange::validateMsg(RsNxsMsg *msg, const uint32_t& grpFlag, const uin { RsTlvKeySignature sign = metaData.signSet.keySignSet[INDEX_AUTHEN_IDENTITY]; idValidate &= GxsSecurity::validateNxsMsg(*msg, sign, authorKey); - mGixs->timeStampKey(metaData.mAuthorId,RsIdentityUsage(mServType,RsIdentityUsage::MESSAGE_AUTHOR_SIGNATURE_VALIDATION,metaData.mGroupId,metaData.mMsgId)) ; + mGixs->timeStampKey(metaData.mAuthorId,RsIdentityUsage(RsServiceType(mServType),RsIdentityUsage::MESSAGE_AUTHOR_SIGNATURE_VALIDATION, + metaData.mGroupId, + metaData.mMsgId, + metaData.mParentId, + metaData.mThreadId)) ; } else { @@ -934,7 +941,7 @@ int RsGenExchange::validateMsg(RsNxsMsg *msg, const uint32_t& grpFlag, const uin RsReputationLevel::LOCALLY_NEGATIVE ) { #ifdef GEN_EXCH_DEBUG - std::cerr << "RsGenExchange::validateMsg(): message from " << metaData.mAuthorId << ", rejected because reputation level (" << details.mReputation.mOverallReputationLevel <<") indicate that you banned this ID." << std::endl; + std::cerr << "RsGenExchange::validateMsg(): message from " << metaData.mAuthorId << ", rejected because reputation level (" << static_cast(details.mReputation.mOverallReputationLevel) <<") indicate that you banned this ID." << std::endl; #endif idValidate = false ; } @@ -946,7 +953,12 @@ int RsGenExchange::validateMsg(RsNxsMsg *msg, const uint32_t& grpFlag, const uin { std::list peers; peers.push_back(msg->PeerId()); - mGixs->requestKey(metaData.mAuthorId, peers, RsIdentityUsage(serviceType(),RsIdentityUsage::MESSAGE_AUTHOR_SIGNATURE_VALIDATION,metaData.mGroupId,metaData.mMsgId)); + mGixs->requestKey(metaData.mAuthorId, peers, RsIdentityUsage((RsServiceType)serviceType(), + RsIdentityUsage::MESSAGE_AUTHOR_SIGNATURE_VALIDATION, + metaData.mGroupId, + metaData.mMsgId, + metaData.mParentId, + metaData.mThreadId)); #ifdef GEN_EXCH_DEBUG std::cerr << ", Key missing. Retry later." << std::endl; @@ -1023,7 +1035,7 @@ int RsGenExchange::validateGrp(RsNxsGrp* grp) #ifdef GEN_EXCH_DEBUG std::cerr << " key ID validation result: " << idValidate << std::endl; #endif - mGixs->timeStampKey(metaData.mAuthorId,RsIdentityUsage(mServType,RsIdentityUsage::GROUP_AUTHOR_SIGNATURE_VALIDATION,metaData.mGroupId)); + mGixs->timeStampKey(metaData.mAuthorId,RsIdentityUsage(RsServiceType(mServType),RsIdentityUsage::GROUP_AUTHOR_SIGNATURE_VALIDATION,metaData.mGroupId)); } else { @@ -1041,7 +1053,7 @@ int RsGenExchange::validateGrp(RsNxsGrp* grp) #endif std::list peers; peers.push_back(grp->PeerId()); - mGixs->requestKey(metaData.mAuthorId, peers,RsIdentityUsage(mServType,RsIdentityUsage::GROUP_AUTHOR_SIGNATURE_VALIDATION,metaData.mGroupId)); + mGixs->requestKey(metaData.mAuthorId, peers,RsIdentityUsage(RsServiceType(mServType),RsIdentityUsage::GROUP_AUTHOR_SIGNATURE_VALIDATION,metaData.mGroupId)); return VALIDATE_FAIL_TRY_LATER; } } @@ -1110,8 +1122,10 @@ static void addMessageChanged(std::map > } } +#ifdef TO_REMOVE void RsGenExchange::receiveChanges(std::vector& changes) { + std::cerr << "*********************************** RsGenExchange::receiveChanges()" << std::endl; #ifdef GEN_EXCH_DEBUG std::cerr << "RsGenExchange::receiveChanges()" << std::endl; #endif @@ -1154,6 +1168,7 @@ void RsGenExchange::receiveChanges(std::vector& changes) if(rsEvents) rsEvents->postEvent(std::move(evt)); } +#endif bool RsGenExchange::subscribeToGroup(uint32_t& token, const RsGxsGroupId& grpId, bool subscribe) { @@ -1192,7 +1207,7 @@ bool RsGenExchange::getGroupList(const uint32_t &token, std::list bool RsGenExchange::getMsgList(const uint32_t &token, GxsMsgIdResult &msgIds) { - return mDataAccess->getMsgList(token, msgIds); + return mDataAccess->getMsgIdList(token, msgIds); } bool RsGenExchange::getMsgRelatedList(const uint32_t &token, MsgRelatedIdResult &msgIds) @@ -1268,18 +1283,18 @@ bool RsGenExchange::getMsgMeta(const uint32_t &token, for(; mit != result.end(); ++mit) { - std::vector& metaV = mit->second; + std::vector& metaV = mit->second; std::vector& msgInfoV = msgInfo[mit->first]; - std::vector::iterator vit = metaV.begin(); + std::vector::iterator vit = metaV.begin(); RsMsgMetaData meta; for(; vit != metaV.end(); ++vit) { - RsGxsMsgMetaData& m = *(*vit); + const RsGxsMsgMetaData& m = *(*vit); meta = m; msgInfoV.push_back(meta); - delete *vit; + //delete *vit; } metaV.clear(); } @@ -1296,18 +1311,18 @@ bool RsGenExchange::getMsgRelatedMeta(const uint32_t &token, GxsMsgRelatedMetaMa for(; mit != result.end(); ++mit) { - std::vector& metaV = mit->second; + std::vector& metaV = mit->second; std::vector& msgInfoV = msgMeta[mit->first]; - std::vector::iterator vit = metaV.begin(); + std::vector::iterator vit = metaV.begin(); RsMsgMetaData meta; for(; vit != metaV.end(); ++vit) { - RsGxsMsgMetaData& m = *(*vit); + const RsGxsMsgMetaData& m = *(*vit); meta = m; msgInfoV.push_back(meta); - delete *vit; + //delete *vit; } metaV.clear(); } @@ -1669,21 +1684,14 @@ void RsGenExchange::receiveNewMessages(std::vector& messages) void RsGenExchange::receiveDistantSearchResults(TurtleRequestId id,const RsGxsGroupId &grpId) { - std::cerr << __PRETTY_FUNCTION__ << " received result for request " - << std::hex << id << std::dec << std::endl; - - RS_STACK_MUTEX(mGenMtx); - - RsGxsDistantSearchResultChange* gc = new RsGxsDistantSearchResultChange(id,grpId); - mNotifications.push_back(gc); + std::cerr << __PRETTY_FUNCTION__ << " received result for request " << std::hex << id << std::dec << ": this method should be overloaded in the client service, but it is not. This is a bug!" << std::endl; } void RsGenExchange::notifyReceivePublishKey(const RsGxsGroupId &grpId) { RS_STACK_MUTEX(mGenMtx); - RsGxsGroupChange* gc = new RsGxsGroupChange(RsGxsNotify::TYPE_RECEIVED_PUBLISHKEY, true); - gc->mGrpIdList.push_back(grpId); + RsGxsGroupChange* gc = new RsGxsGroupChange(RsGxsNotify::TYPE_RECEIVED_PUBLISHKEY,grpId, true); mNotifications.push_back(gc); } @@ -1691,8 +1699,8 @@ void RsGenExchange::notifyChangedGroupStats(const RsGxsGroupId &grpId) { RS_STACK_MUTEX(mGenMtx); - RsGxsGroupChange* gc = new RsGxsGroupChange(RsGxsNotify::TYPE_PROCESSED, false); - gc->mGrpIdList.push_back(grpId); + RsGxsGroupChange* gc = new RsGxsGroupChange(RsGxsNotify::TYPE_STATISTICS_CHANGED,grpId, false); + mNotifications.push_back(gc); } @@ -1789,19 +1797,20 @@ void RsGenExchange::publishGroup(uint32_t& token, RsGxsGrpItem *grpItem) void RsGenExchange::updateGroup(uint32_t& token, RsGxsGrpItem* grpItem) { - if(!checkGroupMetaConsistency(grpItem->meta)) - { - std::cerr << "(EE) Cannot update group. Some information was not supplied." << std::endl; - return ; - } + if(!checkGroupMetaConsistency(grpItem->meta)) + { + std::cerr << "(EE) Cannot update group. Some information was not supplied." << std::endl; + delete grpItem; + return ; + } - RS_STACK_MUTEX(mGenMtx) ; + RS_STACK_MUTEX(mGenMtx) ; token = mDataAccess->generatePublicToken(); - mGroupUpdatePublish.push_back(GroupUpdatePublish(grpItem, token)); + mGroupUpdatePublish.push_back(GroupUpdatePublish(grpItem, token)); #ifdef GEN_EXCH_DEBUG - std::cerr << "RsGenExchange::updateGroup() token: " << token; - std::cerr << std::endl; + std::cerr << "RsGenExchange::updateGroup() token: " << token; + std::cerr << std::endl; #endif } @@ -2010,15 +2019,15 @@ void RsGenExchange::processMsgMetaChanges() if(mit != result.end()) { - std::vector& msgMetaV = mit->second; + std::vector& msgMetaV = mit->second; if(!msgMetaV.empty()) { - RsGxsMsgMetaData* meta = *(msgMetaV.begin()); + const RsGxsMsgMetaData* meta = *(msgMetaV.begin()); value = (meta->mMsgStatus & ~mask) | (mask & value); changed = (static_cast(meta->mMsgStatus) != value); m.val.put(RsGeneralDataService::MSG_META_STATUS, value); - delete meta; + //delete meta; ok = true; } } @@ -2048,11 +2057,13 @@ void RsGenExchange::processMsgMetaChanges() } } - if (!msgIds.empty()) { + if (!msgIds.empty()) + { RS_STACK_MUTEX(mGenMtx); - RsGxsMsgChange* c = new RsGxsMsgChange(RsGxsNotify::TYPE_PROCESSED, false); - c->msgChangeMap = msgIds; - mNotifications.push_back(c); + + for(auto it(msgIds.begin());it!=msgIds.end();++it) + for(auto& msg_id:it->second) + mNotifications.push_back(new RsGxsMsgChange(RsGxsNotify::TYPE_PROCESSED, it->first, msg_id, false)); } } @@ -2098,17 +2109,11 @@ void RsGenExchange::processGrpMetaChanges() } } - if(!grpChanged.empty()) + for(auto& groupId:grpChanged) { RS_STACK_MUTEX(mGenMtx); - RsGxsGroupChange* gc = new RsGxsGroupChange(RsGxsNotify::TYPE_PROCESSED, true); - gc->mGrpIdList = grpChanged; - mNotifications.push_back(gc); -#ifdef GEN_EXCH_DEBUG - std::cerr << " adding the following grp ids to notification: " << std::endl; - for(std::list::const_iterator it(grpChanged.begin());it!=grpChanged.end();++it) - std::cerr << " " << *it << std::endl; -#endif + + mNotifications.push_back(new RsGxsGroupChange(RsGxsNotify::TYPE_PROCESSED,groupId, true)); } } @@ -2185,7 +2190,7 @@ void RsGenExchange::publishMsgs() mMsgsToPublish.insert(std::make_pair(sign_it->first, item.mItem)); } - std::map > msgChangeMap; + std::map > msgChangeMap; std::map::iterator mit = mMsgsToPublish.begin(); for(; mit != mMsgsToPublish.end(); ++mit) @@ -2314,9 +2319,12 @@ void RsGenExchange::publishMsgs() mPublishedMsgs[token] = *msg->metaData; + RsGxsMsgItem *msg_item = dynamic_cast(mSerialiser->deserialise(msg->msg.bin_data,&msg->msg.bin_len)) ; + msg_item->meta = *msg->metaData; + delete msg ; - msgChangeMap[grpId].insert(msgId); + msgChangeMap[grpId].push_back(msg_item); delete[] metaDataBuff; @@ -2355,13 +2363,14 @@ void RsGenExchange::publishMsgs() // entries are invalid mMsgsToPublish.clear(); - if(!msgChangeMap.empty()) - { - RsGxsMsgChange* ch = new RsGxsMsgChange(RsGxsNotify::TYPE_PUBLISHED, false); - ch->msgChangeMap = msgChangeMap; - mNotifications.push_back(ch); - } + for(auto it(msgChangeMap.begin());it!=msgChangeMap.end();++it) + for(auto& msg_item: it->second) + { + RsGxsMsgChange* ch = new RsGxsMsgChange(RsGxsNotify::TYPE_PUBLISHED,msg_item->meta.mGroupId, msg_item->meta.mMsgId, false); + ch->mNewMsgItem = msg_item; + mNotifications.push_back(ch); + } } RsGenExchange::ServiceCreate_Return RsGenExchange::service_CreateGroup(RsGxsGrpItem* /* grpItem */, @@ -2380,15 +2389,14 @@ RsGenExchange::ServiceCreate_Return RsGenExchange::service_CreateGroup(RsGxsGrpI void RsGenExchange::processGroupUpdatePublish() { - RS_STACK_MUTEX(mGenMtx) ; + RS_STACK_MUTEX(mGenMtx) ; // get keys for group update publish // first build meta request map for groups to be updated RsGxsGrpMetaTemporaryMap grpMeta; - std::vector::iterator vit = mGroupUpdatePublish.begin(); - for(; vit != mGroupUpdatePublish.end(); ++vit) + for(auto vit = mGroupUpdatePublish.begin(); vit != mGroupUpdatePublish.end(); ++vit) { GroupUpdatePublish& gup = *vit; const RsGxsGroupId& groupId = gup.grpItem->meta.mGroupId; @@ -2401,8 +2409,7 @@ void RsGenExchange::processGroupUpdatePublish() mDataStore->retrieveGxsGrpMetaData(grpMeta); // now - vit = mGroupUpdatePublish.begin(); - for(; vit != mGroupUpdatePublish.end(); ++vit) + for(auto vit = mGroupUpdatePublish.begin(); vit != mGroupUpdatePublish.end(); ++vit) { GroupUpdatePublish& gup = *vit; const RsGxsGroupId& groupId = gup.grpItem->meta.mGroupId; @@ -2420,14 +2427,14 @@ void RsGenExchange::processGroupUpdatePublish() meta = mit->second; //gup.grpItem->meta = *meta; - GxsGrpPendingSign ggps(gup.grpItem, gup.mToken); + GxsGrpPendingSign ggps(gup.grpItem, gup.mToken); if(checkKeys(meta->keys)) { ggps.mKeys = meta->keys; - - GxsSecurity::createPublicKeysFromPrivateKeys(ggps.mKeys) ; - + + GxsSecurity::createPublicKeysFromPrivateKeys(ggps.mKeys) ; + ggps.mHaveKeys = true; ggps.mStartTS = time(NULL); ggps.mLastAttemptTS = 0; @@ -2437,8 +2444,8 @@ void RsGenExchange::processGroupUpdatePublish() } else { - std::cerr << "(EE) publish group fails because RS cannot find the private publish and author keys" << std::endl; - + std::cerr << "(EE) publish group fails because RS cannot find the private publish and author keys" << std::endl; + delete gup.grpItem; mDataAccess->updatePublicRequestStatus(gup.mToken, RsTokenService::FAILED); } @@ -2493,15 +2500,15 @@ void RsGenExchange::processGroupDelete() grpDeleted.push_back(note.second); } - if(!grpDeleted.empty()) + for(auto& groupId:grpDeleted) { - RsGxsGroupChange* gc = new RsGxsGroupChange(RsGxsNotify::TYPE_PUBLISHED, false); - gc->mGrpIdList = grpDeleted; + RsGxsGroupChange* gc = new RsGxsGroupChange(RsGxsNotify::TYPE_GROUP_DELETED, groupId,false); mNotifications.push_back(gc); } mGroupDeletePublish.clear(); } + void RsGenExchange::processMessageDelete() { RS_STACK_MUTEX(mGenMtx) ; @@ -2520,31 +2527,24 @@ void RsGenExchange::processMessageDelete() mDataStore->removeMsgs( (*vit).mMsgs ); } +// std::list grpDeleted; +// std::map::iterator mit = toNotify.begin(); +// for(; mit != toNotify.end(); ++mit) +// { +// GrpNote& note = mit->second; +// uint8_t status = note.first ? RsTokenService::GXS_REQUEST_V2_STATUS_COMPLETE +// : RsTokenService::GXS_REQUEST_V2_STATUS_FAILED; +// +// mGrpNotify.insert(std::make_pair(mit->first, note.second)); +// mDataAccess->updatePublicRequestStatus(mit->first, status); +// +// if(note.first) +// grpDeleted.push_back(note.second); +// } -#warning csoler: TODO: notify for deleted messages -#ifdef SUSPENDED - std::list grpDeleted; - std::map::iterator mit = toNotify.begin(); - for(; mit != toNotify.end(); ++mit) - { - GrpNote& note = mit->second; - uint8_t status = note.first ? RsTokenService::GXS_REQUEST_V2_STATUS_COMPLETE - : RsTokenService::GXS_REQUEST_V2_STATUS_FAILED; - - mGrpNotify.insert(std::make_pair(mit->first, note.second)); - mDataAccess->updatePublicRequestStatus(mit->first, status); - - if(note.first) - grpDeleted.push_back(note.second); - } - - if(!grpDeleted.empty()) - { - RsGxsGroupChange* gc = new RsGxsGroupChange(RsGxsNotify::TYPE_PUBLISH, false); - gc->mGrpIdList = grpDeleted; - mNotifications.push_back(gc); - } -#endif + for(uint32_t i=0;ifirst, false)); mMsgDeletePublish.clear(); } @@ -2804,17 +2804,8 @@ void RsGenExchange::publishGrps() grpChanged.push_back(note.second); } - if(!grpChanged.empty()) - { - RsGxsGroupChange* gc = new RsGxsGroupChange(RsGxsNotify::TYPE_RECEIVED_NEW, true); - gc->mGrpIdList = grpChanged; - mNotifications.push_back(gc); -#ifdef GEN_EXCH_DEBUG - std::cerr << " adding the following grp ids to notification: " << std::endl; - for(std::list::const_iterator it(grpChanged.begin());it!=grpChanged.end();++it) - std::cerr << " " << *it << std::endl; -#endif - } + for(auto& groupId:grpChanged) + mNotifications.push_back(new RsGxsGroupChange(RsGxsNotify::TYPE_RECEIVED_NEW,groupId, true)); } // This is done off-mutex to avoid possible cross deadlocks with the net service. @@ -3071,11 +3062,19 @@ void RsGenExchange::processRecvdMessages() #ifdef GEN_EXCH_DEBUG std::cerr << " storing remaining messages" << std::endl; #endif - mDataStore->storeMessage(msgs_to_store); - RsGxsMsgChange* c = new RsGxsMsgChange(RsGxsNotify::TYPE_RECEIVED_NEW, false); - c->msgChangeMap = msgIds; - mNotifications.push_back(c); + for(auto& nxs_msg: msgs_to_store) + { + RsGxsMsgItem *item = dynamic_cast(mSerialiser->deserialise(nxs_msg->msg.bin_data,&nxs_msg->msg.bin_len)); + item->meta = *nxs_msg->metaData; + + RsGxsMsgChange* c = new RsGxsMsgChange(RsGxsNotify::TYPE_RECEIVED_NEW, item->meta.mGroupId, item->meta.mMsgId,false); + c->mNewMsgItem = item; + + mNotifications.push_back(c); + } + + mDataStore->storeMessage(msgs_to_store); // we do that late because it destroys the items in msgs_to_store } } @@ -3204,11 +3203,17 @@ void RsGenExchange::processRecvdGroups() vit = tmp ; } - if(!grpIds.empty()) + if(!grps_to_store.empty()) { - RsGxsGroupChange* c = new RsGxsGroupChange(RsGxsNotify::TYPE_RECEIVED_NEW, false); - c->mGrpIdList = grpIds; - mNotifications.push_back(c); + for(auto Grp:grps_to_store) + { + RsGxsGroupChange* c = new RsGxsGroupChange(RsGxsNotify::TYPE_RECEIVED_NEW, Grp->grpId, false); + + c->mNewGroupItem = dynamic_cast(mSerialiser->deserialise(Grp->grp.bin_data,&Grp->grp.bin_len)); + + mNotifications.push_back(c); + } + mDataStore->storeGroup(grps_to_store); #ifdef GEN_EXCH_DEBUG std::cerr << " adding the following grp ids to notification: " << std::endl; @@ -3229,39 +3234,42 @@ void RsGenExchange::performUpdateValidation() std::cerr << "RsGenExchange::performUpdateValidation() " << std::endl; #endif - RsGxsGrpMetaTemporaryMap grpMetas; + RsNxsGrpDataTemporaryMap grpDatas; - std::vector::iterator vit = mGroupUpdates.begin(); - for(; vit != mGroupUpdates.end(); ++vit) - grpMetas.insert(std::make_pair(vit->newGrp->grpId, (RsGxsGrpMetaData*)NULL)); + for(auto vit(mGroupUpdates.begin()); vit != mGroupUpdates.end(); ++vit) + grpDatas.insert(std::make_pair(vit->newGrp->grpId, (RsNxsGrp*)NULL)); + + if(grpDatas.empty() || !mDataStore->retrieveNxsGrps(grpDatas,true,false)) + { + if(grpDatas.empty()) + RsErr() << __PRETTY_FUNCTION__ << " Validation of multiple group updates failed: no group in list!" << std::endl; + else + RsErr() << __PRETTY_FUNCTION__ << " Validation of multiple group updates failed: cannot retrieve froup data for these groups!" << std::endl; - if(!grpMetas.empty()) - mDataStore->retrieveGxsGrpMetaData(grpMetas); - else return; - - vit = mGroupUpdates.begin(); - for(; vit != mGroupUpdates.end(); ++vit) - { - GroupUpdate& gu = *vit; - std::map::iterator mit = grpMetas.find(gu.newGrp->grpId); - gu.oldGrpMeta = mit->second; - gu.validUpdate = updateValid(*(gu.oldGrpMeta), *(gu.newGrp)); - } + } #ifdef GEN_EXCH_DEBUG std::cerr << "RsGenExchange::performUpdateValidation() " << std::endl; #endif - vit = mGroupUpdates.begin(); - RsNxsGrpDataTemporaryList grps ; - for(; vit != mGroupUpdates.end(); ++vit) + for(auto vit(mGroupUpdates.begin()); vit != mGroupUpdates.end(); ++vit) { GroupUpdate& gu = *vit; - if(gu.validUpdate) + auto mit = grpDatas.find(gu.newGrp->grpId); + + if(mit == grpDatas.end()) + { + RsErr() << __PRETTY_FUNCTION__ << " Validation of group update failed for group " << gu.newGrp->grpId << " because previous grp version cannot be found." << std::endl; + continue; + } + RsGxsGrpMetaData *oldGrpMeta(mit->second->metaData); + RsNxsGrp *oldGrp(mit->second); + + if(updateValid(*oldGrpMeta, *gu.newGrp)) { if(gu.newGrp->metaData->mCircleType == GXS_CIRCLE_TYPE_YOUR_FRIENDS_ONLY) gu.newGrp->metaData->mOriginator = gu.newGrp->PeerId(); @@ -3269,42 +3277,39 @@ void RsGenExchange::performUpdateValidation() // Keep subscriptionflag to what it was. This avoids clearing off the flag when updates to group meta information // is received. - gu.newGrp->metaData->mSubscribeFlags = gu.oldGrpMeta->mSubscribeFlags ; + gu.newGrp->metaData->mSubscribeFlags = oldGrpMeta->mSubscribeFlags ; // Also keep private keys if present if(!gu.newGrp->metaData->keys.private_keys.empty()) std::cerr << "(EE) performUpdateValidation() group " <metaData->mGroupId << " has been received with private keys. This is very unexpected!" << std::endl; else - gu.newGrp->metaData->keys.private_keys = gu.oldGrpMeta->keys.private_keys ; + gu.newGrp->metaData->keys.private_keys = oldGrpMeta->keys.private_keys ; + + // Now prepare notification of the client + + RsGxsGroupChange *c = new RsGxsGroupChange(RsGxsNotify::TYPE_UPDATED,gu.newGrp->metaData->mGroupId,false); + + c->mNewGroupItem = dynamic_cast(mSerialiser->deserialise(gu.newGrp->grp.bin_data,&gu.newGrp->grp.bin_len)); + c->mNewGroupItem->meta = *gu.newGrp->metaData; // gu.newGrp will be deleted because mDataStore will destroy it on update + + c->mOldGroupItem = dynamic_cast(mSerialiser->deserialise(oldGrp->grp.bin_data,&oldGrp->grp.bin_len)); + c->mOldGroupItem->meta = *oldGrpMeta; // no need to delete mit->second, as it will be deleted automatically in the temporary map + + mNotifications.push_back(c); + + // finally, add the group to the list to send to mDataStore grps.push_back(gu.newGrp); } else { - delete gu.newGrp; + delete gu.newGrp; // delete here because mDataStore will not take care of this one. no need to delete mit->second, as it will be deleted automatically in the temporary map gu.newGrp = NULL ; } - - gu.oldGrpMeta = NULL ; } - // notify the client - - RsGxsGroupChange* c = new RsGxsGroupChange(RsGxsNotify::TYPE_RECEIVED_NEW, true); - - for(uint32_t i=0;imGrpIdList.push_back(mGroupUpdates[i].newGrp->grpId) ; -#ifdef GEN_EXCH_DEBUG - std::cerr << " " << mGroupUpdates[i].newGrp->grpId << std::endl; -#endif - } - - mNotifications.push_back(c); // Warning: updateGroup will destroy the objects in grps. Dont use it afterwards! - mDataStore->updateGroup(grps); #ifdef GEN_EXCH_DEBUG @@ -3350,7 +3355,7 @@ bool RsGenExchange::updateValid(const RsGxsGrpMetaData& oldGrpMeta, const RsNxsG // also check this is the latest published group bool latest = newGrp.metaData->mPublishTs > oldGrpMeta.mPublishTs; - mGixs->timeStampKey(newGrp.metaData->mAuthorId, RsIdentityUsage(mServType,RsIdentityUsage::GROUP_ADMIN_SIGNATURE_CREATION, oldGrpMeta.mGroupId)) ; + mGixs->timeStampKey(newGrp.metaData->mAuthorId, RsIdentityUsage(RsServiceType(mServType),RsIdentityUsage::GROUP_ADMIN_SIGNATURE_CREATION, oldGrpMeta.mGroupId)) ; return GxsSecurity::validateNxsGrp(newGrp, adminSign, keyMit->second) && latest; } @@ -3460,20 +3465,32 @@ bool RsGenExchange::exportGroupBase64( if(groupId.isNull()) return failure("groupId cannot be null"); + // We have no blocking API here, so we need to make a blocking request manually. const std::list groupIds({groupId}); RsTokReqOptions opts; opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; uint32_t token; - mDataAccess->requestGroupInfo( - token, RS_TOKREQ_ANSTYPE_DATA, opts, groupIds); - RsTokenService::GxsRequestStatus wtStatus = mDataAccess->waitToken(token); - if(wtStatus != RsTokenService::COMPLETE) - return failure( "waitToken(...) failed with: " + - std::to_string(wtStatus) ); + mDataAccess->requestGroupInfo( token, RS_TOKREQ_ANSTYPE_DATA, opts, groupIds); + + // provide a sync response: actually wait for the token. + std::chrono::milliseconds maxWait = std::chrono::milliseconds(10000); + std::chrono::milliseconds checkEvery = std::chrono::milliseconds(100); + + auto timeout = std::chrono::steady_clock::now() + maxWait; // wait for 10 secs at most + auto st = mDataAccess->requestStatus(token); + + while( !(st == RsTokenService::FAILED || st >= RsTokenService::COMPLETE) && std::chrono::steady_clock::now() < timeout ) + { + std::this_thread::sleep_for(checkEvery); + st = mDataAccess->requestStatus(token); + } + if(st != RsTokenService::COMPLETE) + return failure( "waitToken(...) failed with: " + std::to_string(st) ); uint8_t* buf = nullptr; uint32_t size; RsGxsGroupId grpId; + if(!getSerializedGroupData(token, grpId, buf, size)) return failure("failed retrieving GXS data"); diff --git a/libretroshare/src/gxs/rsgenexchange.h b/libretroshare/src/gxs/rsgenexchange.h index 49fd8b2e2..4a05e61c4 100644 --- a/libretroshare/src/gxs/rsgenexchange.h +++ b/libretroshare/src/gxs/rsgenexchange.h @@ -33,6 +33,7 @@ #include "rsnxsobserver.h" #include "retroshare/rsgxsservice.h" #include "rsitems/rsnxsitems.h" +#include "gxs/rsgxsnotify.h" #include "rsgxsutil.h" template @@ -262,12 +263,14 @@ public: */ bool getPublishedMsgMeta(const uint32_t& token,RsMsgMetaData& meta); +#ifdef TO_REMOVE /*! * Gxs services should call this for automatic handling of * changes, send * @param changes */ virtual void receiveChanges(std::vector& changes); +#endif /*! * \brief acceptNewGroup @@ -726,7 +729,7 @@ public: virtual void setSyncPeriod(const RsGxsGroupId& grpId,uint32_t age_in_secs) ; virtual bool getGroupNetworkStats(const RsGxsGroupId& grpId,RsGroupNetworkStats& stats); - uint16_t serviceType() const { return mServType ; } + uint16_t serviceType() const override { return mServType ; } uint32_t serviceFullType() const { return RsServiceInfo::RsServiceInfoUIn16ToFullServiceId(mServType); } virtual RsReputationLevel minReputationForForwardingMessages( @@ -748,9 +751,6 @@ protected: */ virtual void notifyChanges(std::vector& changes) = 0; - - - private: void processRecvdData(); diff --git a/libretroshare/src/gxs/rsgxs.h b/libretroshare/src/gxs/rsgxs.h index 3f25b087b..fd7f693f9 100644 --- a/libretroshare/src/gxs/rsgxs.h +++ b/libretroshare/src/gxs/rsgxs.h @@ -29,8 +29,8 @@ /* data types used throughout Gxs from netservice to genexchange */ -typedef std::map > GxsMsgMetaResult; -typedef std::map > MsgRelatedMetaResult; +typedef std::map > GxsMsgMetaResult; +typedef std::map > MsgRelatedMetaResult; // Default values that are used throughout GXS code diff --git a/libretroshare/src/gxs/rsgxsdata.cc b/libretroshare/src/gxs/rsgxsdata.cc index 0ec61de8e..b83096575 100644 --- a/libretroshare/src/gxs/rsgxsdata.cc +++ b/libretroshare/src/gxs/rsgxsdata.cc @@ -196,7 +196,6 @@ bool RsGxsGrpMetaData::deserialise(void *data, uint32_t &pktsize) return ok; } -int RsGxsMsgMetaData::refcount = 0; RsGxsMsgMetaData::RsGxsMsgMetaData(){ clear(); @@ -209,7 +208,7 @@ RsGxsMsgMetaData::~RsGxsMsgMetaData(){ return; } -uint32_t RsGxsMsgMetaData::serial_size() +uint32_t RsGxsMsgMetaData::serial_size() const { uint32_t s = 8; // header size diff --git a/libretroshare/src/gxs/rsgxsdata.h b/libretroshare/src/gxs/rsgxsdata.h index 282f063fb..acc0c71d8 100644 --- a/libretroshare/src/gxs/rsgxsdata.h +++ b/libretroshare/src/gxs/rsgxsdata.h @@ -48,6 +48,7 @@ public: bool deserialise(void *data, uint32_t &pktsize); bool serialise(void* data, uint32_t &pktsize, uint32_t api_version); uint32_t serial_size(uint32_t api_version) const; + uint32_t serial_size() const { return serial_size(RS_GXS_GRP_META_DATA_CURRENT_API_VERSION); } void clear(); void operator =(const RsGroupMetaData& rMeta); @@ -94,12 +95,10 @@ public: ~RsGxsMsgMetaData(); bool deserialise(void *data, uint32_t *size); bool serialise(void* data, uint32_t *size); - uint32_t serial_size(); + uint32_t serial_size() const; void clear(); void operator =(const RsMsgMetaData& rMeta); - static int refcount; - //Sort data in same order than serialiser and deserializer RsGxsGroupId mGroupId; RsGxsMessageId mMsgId; diff --git a/libretroshare/src/gxs/rsgxsdataaccess.cc b/libretroshare/src/gxs/rsgxsdataaccess.cc index 59c828e60..bfd2ce3a3 100644 --- a/libretroshare/src/gxs/rsgxsdataaccess.cc +++ b/libretroshare/src/gxs/rsgxsdataaccess.cc @@ -30,27 +30,29 @@ * #define DATA_DEBUG 1 **********/ +bool operator<(const std::pair& p1,const std::pair& p2) +{ + return p1.second->Options.mPriority <= p2.second->Options.mPriority ; // <= so that new elements with same priority are inserted before +} + RsGxsDataAccess::RsGxsDataAccess(RsGeneralDataService* ds) : mDataStore(ds), mDataMutex("RsGxsDataAccess"), mNextToken(0) {} RsGxsDataAccess::~RsGxsDataAccess() { - for(std::map::const_iterator it(mRequests.begin());it!=mRequests.end();++it) - delete it->second ; + for(auto& it:mRequestQueue) + delete it.second; } -bool RsGxsDataAccess::requestGroupInfo( - uint32_t &token, uint32_t ansType, const RsTokReqOptions &opts, - const std::list &groupIds ) +bool RsGxsDataAccess::requestGroupInfo( uint32_t &token, uint32_t ansType, const RsTokReqOptions &opts, const std::list &groupIds ) { if(groupIds.empty()) { - std::cerr << __PRETTY_FUNCTION__ << " (WW) Group Id list is empty!" - << std::endl; + RsErr() << __PRETTY_FUNCTION__ << " (WW) Group Id list is empty!" << std::endl; return false; } - GxsRequest* req = NULL; + GxsRequest* req = nullptr; uint32_t reqType = opts.mReqType; if(reqType & GXS_REQUEST_TYPE_GROUP_META) @@ -80,16 +82,14 @@ bool RsGxsDataAccess::requestGroupInfo( if(!req) { - std::cerr << __PRETTY_FUNCTION__ << " request type not recognised, " - << "reqType: " << reqType << std::endl; + RsErr() << __PRETTY_FUNCTION__ << " request type not recognised, " << "reqType: " << reqType << std::endl; return false; } generateToken(token); #ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::requestGroupInfo() gets token: " << token - << std::endl; + RsErr() << "RsGxsDataAccess::requestGroupInfo() gets token: " << token << std::endl; #endif setReq(req, token, ansType, opts); @@ -101,7 +101,7 @@ bool RsGxsDataAccess::requestGroupInfo( bool RsGxsDataAccess::requestGroupInfo(uint32_t &token, uint32_t ansType, const RsTokReqOptions &opts) { - GxsRequest* req = NULL; + GxsRequest* req = nullptr; uint32_t reqType = opts.mReqType; if(reqType & GXS_REQUEST_TYPE_GROUP_META) @@ -114,14 +114,13 @@ bool RsGxsDataAccess::requestGroupInfo(uint32_t &token, uint32_t ansType, const req = new GroupSerializedDataReq(); else { - std::cerr << "RsGxsDataAccess::requestGroupInfo() request type not recognised, type " - << reqType << std::endl; + RsErr() << "RsGxsDataAccess::requestGroupInfo() request type not recognised, type " << reqType << std::endl; return false; } generateToken(token); #ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::requestGroupInfo() gets Token: " << token << std::endl; + RsErr() << "RsGxsDataAccess::requestGroupInfo() gets Token: " << token << std::endl; #endif setReq(req, token, ansType, opts); @@ -137,11 +136,10 @@ void RsGxsDataAccess::generateToken(uint32_t &token) } -bool RsGxsDataAccess::requestMsgInfo(uint32_t &token, uint32_t ansType, - const RsTokReqOptions &opts, const GxsMsgReq &msgIds) +bool RsGxsDataAccess::requestMsgInfo(uint32_t &token, uint32_t ansType, const RsTokReqOptions &opts, const GxsMsgReq &msgIds) { - GxsRequest* req = NULL; + GxsRequest* req = nullptr; uint32_t reqType = opts.mReqType; // remove all empty grpId entries @@ -181,16 +179,15 @@ bool RsGxsDataAccess::requestMsgInfo(uint32_t &token, uint32_t ansType, } - if(req == NULL) + if(req == nullptr) { - std::cerr << "RsGxsDataAccess::requestMsgInfo() request type not recognised, type " - << reqType << std::endl; + RsErr() << "RsGxsDataAccess::requestMsgInfo() request type not recognised, type " << reqType << std::endl; return false; }else { generateToken(token); #ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::requestMsgInfo() gets Token: " << token << std::endl; + RsErr() << "RsGxsDataAccess::requestMsgInfo() gets Token: " << token << std::endl; #endif } @@ -199,10 +196,9 @@ bool RsGxsDataAccess::requestMsgInfo(uint32_t &token, uint32_t ansType, return true; } -bool RsGxsDataAccess::requestMsgInfo(uint32_t &token, uint32_t ansType, - const RsTokReqOptions &opts, const std::list& grpIds) +bool RsGxsDataAccess::requestMsgInfo(uint32_t &token, uint32_t ansType, const RsTokReqOptions &opts, const std::list& grpIds) { - GxsRequest* req = NULL; + GxsRequest* req = nullptr; uint32_t reqType = opts.mReqType; std::list::const_iterator lit = grpIds.begin(); @@ -215,7 +211,8 @@ bool RsGxsDataAccess::requestMsgInfo(uint32_t &token, uint32_t ansType, mmr->mMsgIds[*lit] = std::set(); req = mmr; - }else if(reqType & GXS_REQUEST_TYPE_MSG_DATA) + } + else if(reqType & GXS_REQUEST_TYPE_MSG_DATA) { MsgDataReq* mdr = new MsgDataReq(); @@ -223,7 +220,8 @@ bool RsGxsDataAccess::requestMsgInfo(uint32_t &token, uint32_t ansType, mdr->mMsgIds[*lit] = std::set(); req = mdr; - }else if(reqType & GXS_REQUEST_TYPE_MSG_IDS) + } + else if(reqType & GXS_REQUEST_TYPE_MSG_IDS) { MsgIdReq* mir = new MsgIdReq(); @@ -233,16 +231,15 @@ bool RsGxsDataAccess::requestMsgInfo(uint32_t &token, uint32_t ansType, req = mir; } - if(req == NULL) + if(req == nullptr) { - std::cerr << "RsGxsDataAccess::requestMsgInfo() request type not recognised, type " - << reqType << std::endl; + RsErr() << __PRETTY_FUNCTION__ << " request type not recognised, type " << reqType << std::endl; return false; }else { generateToken(token); #ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::requestMsgInfo() gets Token: " << token << std::endl; + RsErr() << __PRETTY_FUNCTION__ << " gets Token: " << token << std::endl; #endif } @@ -252,33 +249,40 @@ bool RsGxsDataAccess::requestMsgInfo(uint32_t &token, uint32_t ansType, } -void RsGxsDataAccess::requestServiceStatistic(uint32_t& token) +void RsGxsDataAccess::requestServiceStatistic(uint32_t& token,const RsTokReqOptions& opts) { ServiceStatisticRequest* req = new ServiceStatisticRequest(); generateToken(token); - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_SERVICE_STATS; + if(opts.mReqType != GXS_REQUEST_TYPE_SERVICE_STATS) + { + RsErr() << "Expected opts.mReqType to be GXS_REQUEST_TYPE_SERVICE_STATS requestServiceStatistic()" << std::endl; + return; + } + setReq(req, token, 0, opts); storeRequest(req); } -void RsGxsDataAccess::requestGroupStatistic(uint32_t& token, const RsGxsGroupId& grpId) +void RsGxsDataAccess::requestGroupStatistic(uint32_t& token, const RsGxsGroupId& grpId,const RsTokReqOptions& opts) { GroupStatisticRequest* req = new GroupStatisticRequest(); req->mGrpId = grpId; + if(opts.mReqType != GXS_REQUEST_TYPE_GROUP_STATS) + { + RsErr() << "Expected opts.mReqType to be GXS_REQUEST_TYPE_SERVICE_STATS requestServiceStatistic()" << std::endl; + return; + } + generateToken(token); - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_GROUP_STATS; - setReq(req, token, 0, opts); + setReq(req, token,0, opts); storeRequest(req); } -bool RsGxsDataAccess::requestMsgRelatedInfo(uint32_t &token, uint32_t ansType, const RsTokReqOptions &opts, - const std::vector &msgIds) +bool RsGxsDataAccess::requestMsgRelatedInfo(uint32_t &token, uint32_t ansType, const RsTokReqOptions &opts, const std::vector &msgIds) { MsgRelatedInfoReq* req = new MsgRelatedInfoReq(); @@ -296,7 +300,7 @@ bool RsGxsDataAccess::requestMsgRelatedInfo(uint32_t &token, uint32_t ansType, c void RsGxsDataAccess::setReq(GxsRequest* req, uint32_t token, uint32_t ansType, const RsTokReqOptions& opts) const { req->token = token; - req->ansType = ansType; + req->clientAnswerType = ansType; req->Options = opts; return; } @@ -304,8 +308,18 @@ void RsGxsDataAccess::storeRequest(GxsRequest* req) { RS_STACK_MUTEX(mDataMutex); req->status = PENDING; - req->reqTime = time(NULL); - mRequests[req->token] = req; + req->reqTime = time(nullptr); + + mRequestQueue.insert(std::make_pair(req->token,req)); + mPublicToken[req->token] = PENDING; + +#ifdef DATA_DEBUG + RsErr() << "Stored request token=" << req->token << " priority = " << static_cast(req->Options.mPriority) << " Current request Queue is:" ; + for(auto it(mRequestQueue.begin());it!=mRequestQueue.end();++it) + RsErr() << it->first << " (p=" << static_cast(req->Options.mPriority) << ") "; + std::cerr << std::endl; + RsErr() << "Completed requests waiting for client: " << mCompletedRequests.size() << std::endl; +#endif } RsTokenService::GxsRequestStatus RsGxsDataAccess::requestStatus(uint32_t token) @@ -315,14 +329,6 @@ RsTokenService::GxsRequestStatus RsGxsDataAccess::requestStatus(uint32_t token) uint32_t anstype; rstime_t ts; - { - RS_STACK_MUTEX(mDataMutex); - - // first check public tokens - if(mPublicToken.find(token) != mPublicToken.end()) - return mPublicToken[token]; - } - if (!checkRequestStatus(token, status, reqtype, anstype, ts)) return RsTokenService::FAILED; @@ -333,7 +339,7 @@ bool RsGxsDataAccess::cancelRequest(const uint32_t& token) { RsStackMutex stack(mDataMutex); /****** LOCKED *****/ - GxsRequest* req = locked_retrieveRequest(token); + GxsRequest* req = locked_retrieveCompetedRequest(token); if (!req) { return false; @@ -346,58 +352,52 @@ bool RsGxsDataAccess::cancelRequest(const uint32_t& token) bool RsGxsDataAccess::clearRequest(const uint32_t& token) { - RsStackMutex stack(mDataMutex); /****** LOCKED *****/ + RS_STACK_MUTEX(mDataMutex); + return locked_clearRequest(token); +} - std::map::iterator it; +bool RsGxsDataAccess::locked_clearRequest(const uint32_t& token) +{ + auto it = mCompletedRequests.find(token); - it = mRequests.find(token); - if (it == mRequests.end()) - { - return false; - } + if(it == mCompletedRequests.end()) + return false; - delete it->second; - mRequests.erase(it); + delete it->second; + mCompletedRequests.erase(it); - return true; + auto it2 = mPublicToken.find(token); + if(it2 != mPublicToken.end()) + mPublicToken.erase(it2); + + return true; } bool RsGxsDataAccess::getGroupSummary(const uint32_t& token, std::list& groupInfo) { - RS_STACK_MUTEX(mDataMutex); - GxsRequest* req = locked_retrieveRequest(token); + GxsRequest* req = locked_retrieveCompetedRequest(token); - if(req == NULL) + if(req == nullptr) { - std::cerr << "RsGxsDataAccess::getGroupSummary() Unable to retrieve " - << "group summary" << std::endl; + RsErr() << __PRETTY_FUNCTION__ << " Unable to retrieve group summary" << std::endl; return false; } - else if(req->status == COMPLETE) - { - GroupMetaReq* gmreq = dynamic_cast(req); - if(gmreq) - { - groupInfo = gmreq->mGroupMetaData; - gmreq->mGroupMetaData.clear(); - locked_updateRequestStatus(token, DONE); - } - else - { - std::cerr << "RsGxsDataAccess::getGroupSummary() Req found, failed" - << "cast" << std::endl; - return false; - } + GroupMetaReq* gmreq = dynamic_cast(req); + + if(gmreq) + { + groupInfo = gmreq->mGroupMetaData; + gmreq->mGroupMetaData.clear(); } else { - std::cerr << "RsGxsDataAccess::getGroupSummary() Req not ready" - << std::endl; + RsErr() << __PRETTY_FUNCTION__ << " Req found, failed cast" << std::endl; return false; } + locked_clearRequest(token); return true; } @@ -406,44 +406,33 @@ bool RsGxsDataAccess::getGroupData(const uint32_t& token, std::list& { RS_STACK_MUTEX(mDataMutex); - GxsRequest* req = locked_retrieveRequest(token); + GxsRequest* req = locked_retrieveCompetedRequest(token); - if(req == NULL) + if(req == nullptr) { - std::cerr << "RsGxsDataAccess::getGroupData() Unable to retrieve group" - << "data" << std::endl; + RsErr() << "RsGxsDataAccess::getGroupData() Unable to retrieve group data" << std::endl; return false; } - else if(req->status == COMPLETE) + + GroupDataReq* gmreq = dynamic_cast(req); + GroupSerializedDataReq* gsreq = dynamic_cast(req); + + if(gsreq) { - GroupDataReq* gmreq = dynamic_cast(req); - GroupSerializedDataReq* gsreq = dynamic_cast(req); - - if(gsreq) - { - grpData.swap(gsreq->mGroupData); - gsreq->mGroupData.clear(); - - locked_updateRequestStatus(token, DONE); - } - else if(gmreq) - { - grpData.swap(gmreq->mGroupData); - gmreq->mGroupData.clear(); - locked_updateRequestStatus(token, DONE); - } - else - { - std::cerr << "RsGxsDataAccess::getGroupData() Req found, failed cast" - << " req->reqType: " << req->reqType << std::endl; - return false; - } + grpData.swap(gsreq->mGroupData); + gsreq->mGroupData.clear(); + } + else if(gmreq) + { + grpData.swap(gmreq->mGroupData); + gmreq->mGroupData.clear(); } else { - std::cerr << "RsGxsDataAccess::getGroupData() Req not ready" << std::endl; + RsErr() << __PRETTY_FUNCTION__ << " Req found, failed cast req->reqType: " << req->reqType << std::endl; return false; } + locked_clearRequest(token); return true; } @@ -453,32 +442,29 @@ bool RsGxsDataAccess::getMsgData(const uint32_t& token, NxsMsgDataResult& msgDat RsStackMutex stack(mDataMutex); - GxsRequest* req = locked_retrieveRequest(token); + GxsRequest* req = locked_retrieveCompetedRequest(token); - if(req == NULL){ + if(req == nullptr) + { - std::cerr << "RsGxsDataAccess::getMsgData() Unable to retrieve group data" << std::endl; - return false; - }else if(req->status == COMPLETE){ - - MsgDataReq* mdreq = dynamic_cast(req); - - if(mdreq) - { - msgData.swap(mdreq->mMsgData); - mdreq->mMsgData.clear(); - locked_updateRequestStatus(token, DONE); - } - else - { - std::cerr << "RsGxsDataAccess::getMsgData() Req found, failed caste" << std::endl; - return false; - } - }else{ - std::cerr << "RsGxsDataAccess::getMsgData() Req not ready" << std::endl; + RsErr() << __PRETTY_FUNCTION__ << " Unable to retrieve group data" << std::endl; return false; } + MsgDataReq* mdreq = dynamic_cast(req); + + if(mdreq) + { + msgData.swap(mdreq->mMsgData); + mdreq->mMsgData.clear(); + } + else + { + RsErr() << "RsGxsDataAccess::getMsgData() Req found, failed caste" << std::endl; + return false; + } + locked_clearRequest(token); + return true; } @@ -487,34 +473,30 @@ bool RsGxsDataAccess::getMsgRelatedData(const uint32_t &token, NxsMsgRelatedData RsStackMutex stack(mDataMutex); - GxsRequest* req = locked_retrieveRequest(token); + GxsRequest* req = locked_retrieveCompetedRequest(token); - if(req == NULL){ + if(req == nullptr) + { + RsErr() << __PRETTY_FUNCTION__ << " Unable to retrieve group data" << std::endl; + return false; + } + MsgRelatedInfoReq* mrireq = dynamic_cast(req); - std::cerr << "RsGxsDataAccess::getMsgRelatedData() Unable to retrieve group data" << std::endl; - return false; - }else if(req->status == COMPLETE){ + if(req->Options.mReqType != GXS_REQUEST_TYPE_MSG_RELATED_DATA) + return false; - MsgRelatedInfoReq* mrireq = dynamic_cast(req); + if(mrireq) + { + msgData.swap(mrireq->mMsgDataResult); + mrireq->mMsgDataResult.clear(); + } + else + { + RsErr() << __PRETTY_FUNCTION__ << " Req found, failed caste" << std::endl; + return false; + } - if(req->Options.mReqType != GXS_REQUEST_TYPE_MSG_RELATED_DATA) - return false; - - if(mrireq) - { - msgData.swap(mrireq->mMsgDataResult); - mrireq->mMsgDataResult.clear(); - locked_updateRequestStatus(token, DONE); - } - else - { - std::cerr << "RsGxsDataAccess::getMsgRelatedData() Req found, failed caste" << std::endl; - return false; - } - }else{ - std::cerr << "RsGxsDataAccess::getMsgRelatedData() Req not ready" << std::endl; - return false; - } + locked_clearRequest(token); return true; } @@ -524,144 +506,114 @@ bool RsGxsDataAccess::getMsgSummary(const uint32_t& token, GxsMsgMetaResult& msg RsStackMutex stack(mDataMutex); - GxsRequest* req = locked_retrieveRequest(token); + GxsRequest* req = locked_retrieveCompetedRequest(token); - if(req == NULL){ - - std::cerr << "RsGxsDataAccess::getMsgSummary() Unable to retrieve group data" << std::endl; - return false; - }else if(req->status == COMPLETE){ - - MsgMetaReq* mmreq = dynamic_cast(req); - - if(mmreq) - { - msgInfo.swap(mmreq->mMsgMetaData); - mmreq->mMsgMetaData.clear(); - locked_updateRequestStatus(token, DONE); - - } - else - { - std::cerr << "RsGxsDataAccess::getMsgSummary() Req found, failed caste" << std::endl; - return false; - } - }else{ - std::cerr << "RsGxsDataAccess::getMsgSummary() Req not ready" << std::endl; + if(req == nullptr) + { + RsErr() << __PRETTY_FUNCTION__ << " Unable to retrieve group data" << std::endl; return false; } + MsgMetaReq* mmreq = dynamic_cast(req); + if(mmreq) + { + msgInfo.swap(mmreq->mMsgMetaData); + mmreq->mMsgMetaData.clear(); + } + else + { + RsErr() << " Req found, failed caste" << std::endl; + return false; + } + locked_clearRequest(token); return true; } bool RsGxsDataAccess::getMsgRelatedSummary(const uint32_t &token, MsgRelatedMetaResult &msgMeta) { + RsStackMutex stack(mDataMutex); - RsStackMutex stack(mDataMutex); + GxsRequest* req = locked_retrieveCompetedRequest(token); - GxsRequest* req = locked_retrieveRequest(token); + if(req == nullptr) + { + RsErr() << __PRETTY_FUNCTION__ << " Unable to retrieve message summary" << std::endl; + return false; + } + if(req->Options.mReqType != GXS_REQUEST_TYPE_MSG_RELATED_META) + return false; + MsgRelatedInfoReq* mrireq = dynamic_cast(req); - - if(req == NULL){ - - std::cerr << "RsGxsDataAccess::getMsgRelatedSummary() Unable to retrieve message summary" << std::endl; - return false; - }else if(req->status == COMPLETE){ - - if(req->Options.mReqType != GXS_REQUEST_TYPE_MSG_RELATED_META) - return false; - - MsgRelatedInfoReq* mrireq = dynamic_cast(req); - - if(mrireq) - { - msgMeta.swap(mrireq->mMsgMetaResult); - mrireq->mMsgMetaResult.clear(); - locked_updateRequestStatus(token, DONE); - } - else - { - std::cerr << "RsGxsDataAccess::getMsgRelatedSummary() Req found, failed caste" << std::endl; - return false; - } - } - else - { - std::cerr << "RsGxsDataAccess::getMsgRelatedSummary() Req not ready" << std::endl; - return false; - } - - return true; + if(mrireq) + { + msgMeta.swap(mrireq->mMsgMetaResult); + mrireq->mMsgMetaResult.clear(); + } + else + { + RsErr() << __PRETTY_FUNCTION__ << " Req found, failed caste" << std::endl; + return false; + } + locked_clearRequest(token); + return true; } bool RsGxsDataAccess::getMsgRelatedList(const uint32_t &token, MsgRelatedIdResult &msgIds) { - RsStackMutex stack(mDataMutex); + RsStackMutex stack(mDataMutex); - GxsRequest* req = locked_retrieveRequest(token); + GxsRequest* req = locked_retrieveCompetedRequest(token); - if(req == NULL){ + if(req == nullptr) + { + RsErr() << __PRETTY_FUNCTION__ << " Unable to retrieve message data" << std::endl; + return false; + } + if(req->Options.mReqType != GXS_REQUEST_TYPE_MSG_RELATED_IDS) + return false; - std::cerr << "RsGxsDataAccess::getMsgRelatedList() Unable to retrieve message data" << std::endl; - return false; - }else if(req->status == COMPLETE){ + MsgRelatedInfoReq* mrireq = dynamic_cast(req); - if(req->Options.mReqType != GXS_REQUEST_TYPE_MSG_RELATED_IDS) - return false; - - MsgRelatedInfoReq* mrireq = dynamic_cast(req); - - if(mrireq) - { - msgIds.swap(mrireq->mMsgIdResult); - mrireq->mMsgIdResult.clear(); - locked_updateRequestStatus(token, DONE); - } - else{ - std::cerr << "RsGxsDataAccess::getMsgRelatedList() Req found, failed caste" << std::endl; - return false; - } - } - else - { - std::cerr << "RsGxsDataAccess::getMsgRelatedList() Req not ready" << std::endl; - return false; - } - - return true; + if(mrireq) + { + msgIds.swap(mrireq->mMsgIdResult); + mrireq->mMsgIdResult.clear(); + } + else + { + RsErr() << __PRETTY_FUNCTION__ << " Req found, failed caste" << std::endl; + return false; + } + locked_clearRequest(token); + return true; } -bool RsGxsDataAccess::getMsgList(const uint32_t& token, GxsMsgIdResult& msgIds) +bool RsGxsDataAccess::getMsgIdList(const uint32_t& token, GxsMsgIdResult& msgIds) { RsStackMutex stack(mDataMutex); - GxsRequest* req = locked_retrieveRequest(token); + GxsRequest* req = locked_retrieveCompetedRequest(token); - if(req == NULL){ - - std::cerr << "RsGxsDataAccess::getMsgList() Unable to retrieve msg Ids" << std::endl; - return false; - }else if(req->status == COMPLETE){ - - MsgIdReq* mireq = dynamic_cast(req); - - if(mireq) - { - msgIds.swap(mireq->mMsgIdResult); - mireq->mMsgIdResult.clear(); - locked_updateRequestStatus(token, DONE); - } - else{ - std::cerr << "RsGxsDataAccess::getMsgList() Req found, failed caste" << std::endl; - return false; - } - }else{ - std::cerr << "RsGxsDataAccess::getMsgList() Req not ready" << std::endl; + if(req == nullptr) + { + RsErr() << __PRETTY_FUNCTION__ << " Unable to retrieve msg Ids" << std::endl; return false; } + MsgIdReq* mireq = dynamic_cast(req); + if(mireq) + { + msgIds.swap(mireq->mMsgIdResult); + mireq->mMsgIdResult.clear(); + } + else + { + RsErr() << __PRETTY_FUNCTION__ << " Req found, failed cast" << std::endl; + return false; + } + locked_clearRequest(token); return true; } @@ -669,127 +621,135 @@ bool RsGxsDataAccess::getGroupList(const uint32_t& token, std::liststatus == COMPLETE){ - - GroupIdReq* gireq = dynamic_cast(req); - - if(gireq) - { - groupIds.swap(gireq->mGroupIdResult); - gireq->mGroupIdResult.clear(); - locked_updateRequestStatus(token, DONE); - - }else{ - std::cerr << "RsGxsDataAccess::getGroupList() Req found, failed caste" << std::endl; - return false; - } - }else{ - std::cerr << "RsGxsDataAccess::getGroupList() Req not ready" << std::endl; + if(req == nullptr) + { + RsErr() << __PRETTY_FUNCTION__ << " Unable to retrieve group Ids, Request does not exist" << std::endl; return false; } + GroupIdReq* gireq = dynamic_cast(req); + if(gireq) + { + groupIds.swap(gireq->mGroupIdResult); + gireq->mGroupIdResult.clear(); + } + else + { + RsErr() << __PRETTY_FUNCTION__ << " Req found, failed caste" << std::endl; + return false; + } + locked_clearRequest(token); return true; } -GxsRequest* RsGxsDataAccess::locked_retrieveRequest(const uint32_t& token) +bool RsGxsDataAccess::getGroupStatistic(const uint32_t &token, GxsGroupStatistic &grpStatistic) { + RsStackMutex stack(mDataMutex); - if(mRequests.find(token) == mRequests.end()) return NULL; + GxsRequest* req = locked_retrieveCompetedRequest(token); - GxsRequest* req = mRequests[token]; + if(req == nullptr) + { + RsErr() << "RsGxsDataAccess::getGroupStatistic() Unable to retrieve grp stats" << std::endl; + return false; + } + GroupStatisticRequest* gsreq = dynamic_cast(req); - return req; + if(gsreq) + grpStatistic = gsreq->mGroupStatistic; + else + { + RsErr() << __PRETTY_FUNCTION__ << " Req found, failed caste" << std::endl; + return false; + } + locked_clearRequest(token); + return true; +} + +bool RsGxsDataAccess::getServiceStatistic(const uint32_t &token, GxsServiceStatistic &servStatistic) +{ + RsStackMutex stack(mDataMutex); + + GxsRequest* req = locked_retrieveCompetedRequest(token); + + if(req == nullptr) + { + RsErr() << __PRETTY_FUNCTION__ << " Unable to retrieve service stats" << std::endl; + return false; + } + ServiceStatisticRequest* ssreq = dynamic_cast(req); + + if(ssreq) + servStatistic = ssreq->mServiceStatistic; + else + { + RsErr() << __PRETTY_FUNCTION__ << " Req found, failed caste" << std::endl; + return false; + } + locked_clearRequest(token); + return true; +} +GxsRequest* RsGxsDataAccess::locked_retrieveCompetedRequest(const uint32_t& token) +{ + auto it = mCompletedRequests.find(token) ; + + if(it == mCompletedRequests.end()) + return nullptr; + + return it->second; } #define MAX_REQUEST_AGE 120 // 2 minutes void RsGxsDataAccess::processRequests() { - std::list toClear; - rstime_t now = time(NULL); - std::map::iterator it; - - { - RsStackMutex stack(mDataMutex); /******* LOCKED *******/ - - // process status of the requests - for (it = mRequests.begin(); it != mRequests.end(); ++it) - { - GxsRequest* req = it->second; - - switch (req->status) - { - case PENDING: - // process request later - break; - case PARTIAL: - // should not happen - req->status = COMPLETE; - break; - case DONE: -#ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::processrequests() Clearing Done Request Token: " << req->token; - std::cerr << std::endl; -#endif - toClear.push_back(req->token); - break; - case CANCELLED: -#ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::processrequests() Clearing Cancelled Request Token: " << req->token; - std::cerr << std::endl; -#endif - toClear.push_back(req->token); - break; - default: - if (now - req->reqTime > MAX_REQUEST_AGE) - { -#ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::processrequests() Clearing Old Request Token: " << req->token; - std::cerr << std::endl; -#endif - toClear.push_back(req->token); - } - } - } - } // END OF MUTEX. - - // clear requests - std::list::iterator cit; - for (cit = toClear.begin(); cit != toClear.end(); ++cit) - { - clearRequest(*cit); - } - // process requests - while (true) + + while (!mRequestQueue.empty()) { - GxsRequest* req = NULL; + // Extract the first elements from the request queue. cleanup all other elements marked at terminated. + + GxsRequest* req = nullptr; { RsStackMutex stack(mDataMutex); /******* LOCKED *******/ + rstime_t now = time(nullptr); // this is ok while in the loop below - // get the first pending request - for (it = mRequests.begin(); it != mRequests.end(); ++it) - { - GxsRequest* reqCheck = it->second; - if (reqCheck->status == PENDING) - { - req = reqCheck; + while(!mRequestQueue.empty() && req == nullptr) + { + if(now > mRequestQueue.begin()->second->reqTime + MAX_REQUEST_AGE) + { + mRequestQueue.erase(mRequestQueue.begin()); + continue; + } + + switch( mRequestQueue.begin()->second->status ) + { + case PARTIAL: + RsErr() << "Found partial request in mRequestQueue. This is a bug." << std::endl; // fallthrough + case COMPLETE: + case DONE: + case FAILED: + case CANCELLED: +#ifdef DATA_DEBUG + RsDbg() << " request " << mRequestQueue.begin()->second->token << ": status = " << mRequestQueue.begin()->second->status << ": removing from the RequestQueue" << std::endl; +#endif + mRequestQueue.erase(mRequestQueue.begin()); + continue; + break; + case PENDING: + req = mRequestQueue.begin()->second; req->status = PARTIAL; - break; - } - } - } // END OF MUTEX. + mRequestQueue.erase(mRequestQueue.begin()); // remove it right away from the waiting queue. + break; + } - if (!req) { + } + } + + if (!req) break; - } GroupMetaReq* gmr; GroupDataReq* gdr; @@ -804,132 +764,85 @@ void RsGxsDataAccess::processRequests() ServiceStatisticRequest* ssr; #ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::processRequests() Processing Token: " << req->token << " Status: " - << req->status << " ReqType: " << req->reqType << " Age: " - << now - req->reqTime << std::endl; + RsDbg() << "Processing request: " << req->token << " Status: " << req->status << " ReqType: " << req->reqType << " Age: " << time(nullptr) - req->reqTime << std::endl; #endif /* PROCESS REQUEST! */ bool ok = false; - if((gmr = dynamic_cast(req)) != NULL) + if((gmr = dynamic_cast(req)) != nullptr) { ok = getGroupSummary(gmr); } - else if((gdr = dynamic_cast(req)) != NULL) + else if((gdr = dynamic_cast(req)) != nullptr) { ok = getGroupData(gdr); } - else if((gir = dynamic_cast(req)) != NULL) + else if((gir = dynamic_cast(req)) != nullptr) { ok = getGroupList(gir); } - else if((mmr = dynamic_cast(req)) != NULL) + else if((mmr = dynamic_cast(req)) != nullptr) { ok = getMsgSummary(mmr); } - else if((mdr = dynamic_cast(req)) != NULL) + else if((mdr = dynamic_cast(req)) != nullptr) { ok = getMsgData(mdr); } - else if((mir = dynamic_cast(req)) != NULL) + else if((mir = dynamic_cast(req)) != nullptr) { - ok = getMsgList(mir); + ok = getMsgIdList(mir); } - else if((mri = dynamic_cast(req)) != NULL) + else if((mri = dynamic_cast(req)) != nullptr) { ok = getMsgRelatedInfo(mri); } - else if((gsr = dynamic_cast(req)) != NULL) + else if((gsr = dynamic_cast(req)) != nullptr) { ok = getGroupStatistic(gsr); } - else if((ssr = dynamic_cast(req)) != NULL) + else if((ssr = dynamic_cast(req)) != nullptr) { ok = getServiceStatistic(ssr); } - else if((grr = dynamic_cast(req)) != NULL) + else if((grr = dynamic_cast(req)) != nullptr) { ok = getGroupSerializedData(grr); } - else - { - std::cerr << "RsGxsDataAccess::processRequests() Failed to process request, token: " - << req->token << std::endl; - } + RsErr() << __PRETTY_FUNCTION__ << " Failed to process request, token: " << req->token << std::endl; + // We cannot easily remove the request here because the queue may have more elements now and mRequestQueue.begin() is not necessarily the same element. + // but we mark it as COMPLETE/FAILED so that it will be removed in the next loop. { RsStackMutex stack(mDataMutex); /******* LOCKED *******/ - if (req->status == PARTIAL) - { - req->status = ok ? COMPLETE : FAILED; - } - } // END OF MUTEX. - } -} -bool RsGxsDataAccess::getGroupStatistic(const uint32_t &token, GxsGroupStatistic &grpStatistic) -{ - RsStackMutex stack(mDataMutex); + if(ok) + { + // When the request is complete, we move it to the complete list, so that the caller can easily retrieve the request data - GxsRequest* req = locked_retrieveRequest(token); - - if(req == NULL){ - - std::cerr << "RsGxsDataAccess::getGroupStatistic() Unable to retrieve grp stats" << std::endl; - return false; - }else if(req->status == COMPLETE){ - - GroupStatisticRequest* gsreq = dynamic_cast(req); - - if(gsreq) - { - grpStatistic = gsreq->mGroupStatistic; - locked_updateRequestStatus(token, DONE); - } - else{ - std::cerr << "RsGxsDataAccess::getGroupStatistic() Req found, failed caste" << std::endl; - return false; +#ifdef DATA_DEBUG + RsDbg() << " Request completed successfully. Marking as COMPLETE." << std::endl; +#endif + req->status = COMPLETE ; + mCompletedRequests[req->token] = req; + mPublicToken[req->token] = COMPLETE; + } + else + { + req->status = FAILED; + mPublicToken[req->token] = FAILED; +#ifdef DATA_DEBUG + RsDbg() << " Request failed. Marking as FAILED." << std::endl; +#endif + } } - }else{ - std::cerr << "RsGxsDataAccess::getGroupStatistic() Req not ready" << std::endl; - return false; - } - return true; + } // END OF MUTEX. } -bool RsGxsDataAccess::getServiceStatistic(const uint32_t &token, GxsServiceStatistic &servStatistic) -{ - RsStackMutex stack(mDataMutex); - GxsRequest* req = locked_retrieveRequest(token); - - if(req == NULL){ - - std::cerr << "RsGxsDataAccess::getServiceStatistic() Unable to retrieve service stats" << std::endl; - return false; - }else if(req->status == COMPLETE){ - - ServiceStatisticRequest* ssreq = dynamic_cast(req); - - if(ssreq) - { - servStatistic = ssreq->mServiceStatistic; - locked_updateRequestStatus(token, DONE); - } - else{ - std::cerr << "RsGxsDataAccess::getServiceStatistic() Req found, failed caste" << std::endl; - return false; - } - }else{ - std::cerr << "RsGxsDataAccess::getServiceStatistic() Req not ready" << std::endl; - return false; - } - - return true; -} bool RsGxsDataAccess::getGroupSerializedData(GroupSerializedDataReq* req) { @@ -943,7 +856,7 @@ bool RsGxsDataAccess::getGroupSerializedData(GroupSerializedDataReq* req) for(std::list::iterator lit = grpIdsOut.begin();lit != grpIdsOut.end();++lit) - grpData[*lit] = NULL; + grpData[*lit] = nullptr; bool ok = mDataStore->retrieveNxsGrps(grpData, true, true); req->mGroupData.clear(); @@ -970,7 +883,7 @@ bool RsGxsDataAccess::getGroupData(GroupDataReq* req) for(; lit != lit_end; ++lit) { - grpData[*lit] = NULL; + grpData[*lit] = nullptr; } bool ok = mDataStore->retrieveNxsGrps(grpData, true, true); @@ -996,7 +909,7 @@ bool RsGxsDataAccess::getGroupSummary(GroupMetaReq* req) std::list::const_iterator lit = grpIdsOut.begin(); for(; lit != grpIdsOut.end(); ++lit) - grpMeta[*lit] = NULL; + grpMeta[*lit] = nullptr; mDataStore->retrieveGxsGrpMetaData(grpMeta); @@ -1035,7 +948,7 @@ bool RsGxsDataAccess::getMsgData(MsgDataReq* req) const RsTokReqOptions& opts(req->Options); // filter based on options - getMsgList(req->mMsgIds, opts, msgIdOut); + getMsgIdList(req->mMsgIds, opts, msgIdOut); // If the list is empty because of filtering do not retrieve from DB if((opts.mMsgFlagMask || opts.mStatusMask) && msgIdOut.empty()) @@ -1053,24 +966,21 @@ bool RsGxsDataAccess::getMsgSummary(MsgMetaReq* req) const RsTokReqOptions& opts(req->Options); // filter based on options - getMsgList(req->mMsgIds, opts, msgIdOut); + getMsgMetaDataList(req->mMsgIds, opts, req->mMsgMetaData); - // If the list is empty because of filtering do not retrieve from DB - if((opts.mMsgFlagMask || opts.mStatusMask) && msgIdOut.empty()) - return true; - - mDataStore->retrieveGxsMsgMetaData(msgIdOut, req->mMsgMetaData); +// // If the list is empty because of filtering do not retrieve from DB +// if((opts.mMsgFlagMask || opts.mStatusMask) && msgIdOut.empty()) +// return true; +// +// mDataStore->retrieveGxsMsgMetaData(msgIdOut, req->mMsgMetaData); return true; } - -bool RsGxsDataAccess::getMsgList( - const GxsMsgReq& msgIds, const RsTokReqOptions& opts, - GxsMsgReq& msgIdsOut ) +bool RsGxsDataAccess::getMsgMetaDataList( const GxsMsgReq& msgIds, const RsTokReqOptions& opts, GxsMsgMetaResult& result ) { - GxsMsgMetaResult result; - + // First get all message metas, then filter out the ones we want to keep. + result.clear(); mDataStore->retrieveGxsMsgMetaData(msgIds, result); /* CASEs this handles. @@ -1079,8 +989,7 @@ bool RsGxsDataAccess::getMsgList( * */ #ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::getMsgList()"; - std::cerr << std::endl; + RsDbg() << "RsGxsDataAccess::getMsgList()" << std::endl; #endif bool onlyOrigMsgs = false; @@ -1091,16 +1000,14 @@ bool RsGxsDataAccess::getMsgList( if (opts.mOptions & RS_TOKREQOPT_MSG_ORIGMSG) { #ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::getMsgList() MSG_ORIGMSG"; - std::cerr << std::endl; + RsDbg() << "RsGxsDataAccess::getMsgList() MSG_ORIGMSG" << std::endl; #endif onlyOrigMsgs = true; } else if (opts.mOptions & RS_TOKREQOPT_MSG_LATEST) { #ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::getMsgList() MSG_LATEST"; - std::cerr << std::endl; + RsDbg() << "RsGxsDataAccess::getMsgList() MSG_LATEST" << std::endl; #endif onlyLatestMsgs = true; } @@ -1108,133 +1015,179 @@ bool RsGxsDataAccess::getMsgList( if (opts.mOptions & RS_TOKREQOPT_MSG_THREAD) { #ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::getMsgList() MSG_THREAD"; - std::cerr << std::endl; + RsDbg() << "RsGxsDataAccess::getMsgList() MSG_THREAD" << std::endl; #endif onlyThreadHeadMsgs = true; } GxsMsgMetaResult::iterator meta_it; - MsgMetaFilter metaFilter; for(meta_it = result.begin(); meta_it != result.end(); ++meta_it) { const RsGxsGroupId& grpId = meta_it->first; - metaFilter[grpId] = std::map(); + //auto& filter( metaFilter[grpId] ); // does the initialization of metaFilter[grpId] and avoids further O(log(n)) calls - const std::vector& metaV = meta_it->second; - if (onlyLatestMsgs) // THIS ONE IS HARD -> LOTS OF COMP. + std::vector& metaV = meta_it->second; + + if (onlyLatestMsgs) // if we only consider latest messages, we need to first filter out messages with "children" { - std::vector::const_iterator vit = metaV.begin(); + // The strategy is the following: for each msg we only know its direct ancestor. So we build a map to be able to find for a given message + // which messages derive from it. + // Then when this map is fuly build, we follow this map and every message that has no direct follow up will be kept. + // Because msgs are stored in a std::vector we build a map to convert each vector to its position in metaV. + std::vector keep(metaV.size(),true); // this vector will tell wether we keep or not a given Meta + std::map index_in_metaV; // holds the index of each group Id in metaV + + for(uint32_t i=0;imMsgId] = i; + + // Now loop once over message Metas and see if they have a parent. If yes, then mark the parent to be discarded. + + for(uint32_t i=0;imOrigMsgId.isNull() && metaV[i]->mOrigMsgId != metaV[i]->mMsgId) // this one is a follow up + { + auto it = index_in_metaV.find(metaV[i]->mOrigMsgId); + + if(it != index_in_metaV.end()) + keep[it->second] = false; + else + std::cerr << "Found a msg that has a parent that is not locally known. Not an error anyway." << std::endl; + + } + + // Finally we just discard the messages for which the keep flag has been set to false. + + for(uint32_t i=0;i TS. std::map > origMsgTs; - std::map >::iterator oit; - for(; vit != metaV.end(); ++vit) + for(uint32_t i=0;imParentId.isNull())) { - if (!(msgMeta->mParentId.isNull())) + delete msgMeta; + metaV[i] = nullptr; + continue; + } + + auto oit = origMsgTs.find(msgMeta->mOrigMsgId); + + bool addMsg = false; + if (oit != origMsgTs.end()) + { + if(oit->second.second > msgMeta->mPublishTs) { - continue; +#ifdef DATA_DEBUG + std::cerr << "RsGxsDataAccess::getMsgList() Found New OrigMsgId: "; + std::cerr << msgMeta->mOrigMsgId; + std::cerr << " MsgId: " << msgMeta->mMsgId; + std::cerr << " TS: " << msgMeta->mPublishTs; + std::cerr << std::endl; +#endif + origMsgTs[msgMeta->mOrigMsgId] = std::make_pair(msgMeta->mMsgId, msgMeta->mPublishTs); // add as latest. (overwriting if necessary) } } - - - oit = origMsgTs.find(msgMeta->mOrigMsgId); - bool addMsg = false; - if (oit == origMsgTs.end()) + else { -#ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::getMsgList() Found New OrigMsgId: "; - std::cerr << msgMeta->mOrigMsgId; - std::cerr << " MsgId: " << msgMeta->mMsgId; - std::cerr << " TS: " << msgMeta->mPublishTs; - std::cerr << std::endl; -#endif - - addMsg = true; - } - // check timestamps. - else if (oit->second.second < msgMeta->mPublishTs) - { -#ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::getMsgList() Found Later Msg. OrigMsgId: "; - std::cerr << msgMeta->mOrigMsgId; - std::cerr << " MsgId: " << msgMeta->mMsgId; - std::cerr << " TS: " << msgMeta->mPublishTs; -#endif - - addMsg = true; + delete msgMeta; + metaV[i] = nullptr; + continue; } - if (addMsg) - { - // add as latest. (overwriting if necessary) - origMsgTs[msgMeta->mOrigMsgId] = std::make_pair(msgMeta->mMsgId, msgMeta->mPublishTs); - metaFilter[grpId].insert(std::make_pair(msgMeta->mMsgId, msgMeta)); - } + } // Add the discovered Latest Msgs. - for(oit = origMsgTs.begin(); oit != origMsgTs.end(); ++oit) + for(auto oit = origMsgTs.begin(); oit != origMsgTs.end(); ++oit) { - msgIdsOut[grpId].insert(oit->second.first); + msgIdsOut[grpId].insert(oit->second.first); } +#endif - } - else // ALL OTHER CASES. - { - std::vector::const_iterator vit = metaV.begin(); + for(uint32_t i=0;imParentId.isNull()) + { + //delete msgMeta; + metaV[i] = nullptr; + continue; + } - /* if we are grabbing thread Head... then parentId == empty. */ - if (onlyThreadHeadMsgs) - { - if (!(msgMeta->mParentId.isNull())) - { - continue; - } - } - - - if (onlyOrigMsgs) - { - if (msgMeta->mMsgId == msgMeta->mOrigMsgId) - { - add = true; - } - } - else - { - add = true; - } - - if (add) - { - msgIdsOut[grpId].insert(msgMeta->mMsgId); - metaFilter[grpId].insert(std::make_pair(msgMeta->mMsgId, msgMeta)); - } - - } - } + if (onlyOrigMsgs && !msgMeta->mOrigMsgId.isNull() && msgMeta->mMsgId != msgMeta->mOrigMsgId) + { + //delete msgMeta; + metaV[i] = nullptr; + continue; + } + } } - filterMsgList(msgIdsOut, opts, metaFilter); + // collapse results while keeping the order, eliminating empty slots - metaFilter.clear(); + for(auto it(result.begin());it!=result.end();++it) + { + uint32_t j=0; // j is the end of the cleaned-up tab, at the first available place + + for(uint32_t i=0;isecond.size();++i) // i is the index in the tab possibly containing nullptr's + if(it->second[i] != nullptr) + { + it->second[j] = it->second[i]; // move the pointer to the first available place + ++j; + } + + it->second.resize(j); // normally all pointers have been moved forward so there is nothing to delete here. + } + + // filterMsgIdList(msgIdsOut, opts, metaFilter); // this call is absurd: we already have in metaFilter the content we want. + + //metaFilter.clear(); // delete meta data - cleanseMsgMetaMap(result); + //cleanseMsgMetaMap(result); + + return true; +} + +bool RsGxsDataAccess::getMsgIdList( const GxsMsgReq& msgIds, const RsTokReqOptions& opts, GxsMsgReq& msgIdsOut ) +{ + GxsMsgMetaResult result; + + getMsgMetaDataList( msgIds, opts, result ); + + // extract MessageIds + + msgIdsOut.clear(); + + for(auto it(result.begin());it!=result.end();++it) + { + auto& id_set(msgIdsOut[it->first]); + + for(uint32_t i=0;isecond.size();++i) + id_set.insert(it->second[i]->mMsgId); + } + + // delete meta data + //cleanseMsgMetaMap(result); return true; } @@ -1246,8 +1199,7 @@ bool RsGxsDataAccess::getMsgRelatedInfo(MsgRelatedInfoReq *req) * 1) No Flags => return nothing */ #ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::getMsgRelatedList()"; - std::cerr << std::endl; + RsDbg() << "RsGxsDataAccess::getMsgRelatedList()" << std::endl; #endif const RsTokReqOptions& opts = req->Options; @@ -1260,16 +1212,14 @@ bool RsGxsDataAccess::getMsgRelatedInfo(MsgRelatedInfoReq *req) if (opts.mOptions & RS_TOKREQOPT_MSG_LATEST) { #ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::getMsgRelatedList() MSG_LATEST"; - std::cerr << std::endl; + RsDbg() << "RsGxsDataAccess::getMsgRelatedList() MSG_LATEST" << std::endl; #endif onlyLatestMsgs = true; } else if (opts.mOptions & RS_TOKREQOPT_MSG_VERSIONS) { #ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::getMsgRelatedList() MSG_VERSIONS"; - std::cerr << std::endl; + RsDbg() << "RsGxsDataAccess::getMsgRelatedList() MSG_VERSIONS" << std::endl; #endif onlyAllVersions = true; } @@ -1277,8 +1227,7 @@ bool RsGxsDataAccess::getMsgRelatedInfo(MsgRelatedInfoReq *req) if (opts.mOptions & RS_TOKREQOPT_MSG_PARENT) { #ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::getMsgRelatedList() MSG_PARENTS"; - std::cerr << std::endl; + RsDbg() << "RsGxsDataAccess::getMsgRelatedList() MSG_PARENTS" << std::endl; #endif onlyChildMsgs = true; } @@ -1286,77 +1235,49 @@ bool RsGxsDataAccess::getMsgRelatedInfo(MsgRelatedInfoReq *req) if (opts.mOptions & RS_TOKREQOPT_MSG_THREAD) { #ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::getMsgRelatedList() MSG_THREAD"; - std::cerr << std::endl; + RsDbg() << "RsGxsDataAccess::getMsgRelatedList() MSG_THREAD" << std::endl; #endif onlyThreadMsgs = true; } - if (onlyAllVersions && onlyChildMsgs) - { -#ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::getMsgRelatedList() ERROR Incompatible FLAGS (VERSIONS & PARENT)"; - std::cerr << std::endl; -#endif + if(onlyAllVersions && onlyChildMsgs) + { + RS_ERR("Incompatible FLAGS (VERSIONS & PARENT)"); + return false; + } - return false; - } + if(onlyAllVersions && onlyThreadMsgs) + { + RS_ERR("Incompatible FLAGS (VERSIONS & THREAD)"); + return false; + } - if (onlyAllVersions && onlyThreadMsgs) - { -#ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::getMsgRelatedList() ERROR Incompatible FLAGS (VERSIONS & THREAD)"; - std::cerr << std::endl; -#endif + if((!onlyLatestMsgs) && onlyChildMsgs) + { + RS_ERR("Incompatible FLAGS (!LATEST & PARENT)"); + return false; + } - return false; - } + if((!onlyLatestMsgs) && onlyThreadMsgs) + { + RS_ERR("Incompatible FLAGS (!LATEST & THREAD)"); + return false; + } - if ((!onlyLatestMsgs) && onlyChildMsgs) - { -#ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::getMsgRelatedList() ERROR Incompatible FLAGS (!LATEST & PARENT)"; - std::cerr << std::endl; -#endif + if(onlyChildMsgs && onlyThreadMsgs) + { + RS_ERR("Incompatible FLAGS (PARENT & THREAD)"); + return false; + } - return false; - } + if( (!onlyLatestMsgs) && (!onlyAllVersions) && (!onlyChildMsgs) && + (!onlyThreadMsgs) ) + { + RS_WARN("NO FLAGS -> SIMPLY RETURN nothing"); + return true; + } - if ((!onlyLatestMsgs) && onlyThreadMsgs) - { -#ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::getMsgRelatedList() ERROR Incompatible FLAGS (!LATEST & THREAD)"; - std::cerr << std::endl; -#endif - - return false; - } - - if (onlyChildMsgs && onlyThreadMsgs) - { -#ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::getMsgRelatedList() ERROR Incompatible FLAGS (PARENT & THREAD)"; - std::cerr << std::endl; -#endif - - return false; - } - - - /* FALL BACK OPTION */ - if ((!onlyLatestMsgs) && (!onlyAllVersions) && (!onlyChildMsgs) && (!onlyThreadMsgs)) - { -#ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::getMsgRelatedList() FALLBACK -> NO FLAGS -> SIMPLY RETURN nothing"; - std::cerr << std::endl; -#endif - - return true; - } - - std::vector::iterator vit_msgIds = req->mMsgIds.begin(); - - for(; vit_msgIds != req->mMsgIds.end(); ++vit_msgIds) + for(auto vit_msgIds(req->mMsgIds.begin()); vit_msgIds != req->mMsgIds.end(); ++vit_msgIds) { MsgMetaFilter filterMap; @@ -1368,8 +1289,8 @@ bool RsGxsDataAccess::getMsgRelatedInfo(MsgRelatedInfoReq *req) GxsMsgReq msgIds; msgIds.insert(std::make_pair(grpMsgIdPair.first, std::set())); mDataStore->retrieveGxsMsgMetaData(msgIds, result); - std::vector& metaV = result[grpMsgIdPair.first]; - std::vector::iterator vit_meta; + std::vector& metaV = result[grpMsgIdPair.first]; + std::vector::iterator vit_meta; // msg id to relate to const RsGxsMessageId& msgId = grpMsgIdPair.second; @@ -1377,10 +1298,11 @@ bool RsGxsDataAccess::getMsgRelatedInfo(MsgRelatedInfoReq *req) std::set outMsgIds; - RsGxsMsgMetaData* origMeta = NULL; + const RsGxsMsgMetaData* origMeta = nullptr; + for(vit_meta = metaV.begin(); vit_meta != metaV.end(); ++vit_meta) { - RsGxsMsgMetaData* meta = *vit_meta; + const RsGxsMsgMetaData* meta = *vit_meta; if(msgId == meta->mMsgId) { @@ -1389,18 +1311,14 @@ bool RsGxsDataAccess::getMsgRelatedInfo(MsgRelatedInfoReq *req) } } - if(!origMeta) - { -#ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::getMsgRelatedInfo(): Cannot find meta of msgId (to relate to)!" - << std::endl; -#endif - cleanseMsgMetaMap(result); - return false; - } + if(!origMeta) + { + RS_ERR("Cannot find meta of msgId: ", msgId, " to relate to"); + return false; + } const RsGxsMessageId& origMsgId = origMeta->mOrigMsgId; - std::map& metaMap = filterMap[grpId]; + std::map& metaMap = filterMap[grpId]; if (onlyLatestMsgs) { @@ -1412,7 +1330,7 @@ bool RsGxsDataAccess::getMsgRelatedInfo(MsgRelatedInfoReq *req) for(vit_meta = metaV.begin(); vit_meta != metaV.end(); ++vit_meta) { - RsGxsMsgMetaData* meta = *vit_meta; + const RsGxsMsgMetaData* meta = *vit_meta; // skip msgs that aren't children. if (onlyChildMsgs) @@ -1437,11 +1355,11 @@ bool RsGxsDataAccess::getMsgRelatedInfo(MsgRelatedInfoReq *req) if (oit == origMsgTs.end()) { #ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::getMsgRelatedList() Found New OrigMsgId: "; - std::cerr << meta->mOrigMsgId; - std::cerr << " MsgId: " << meta->mMsgId; - std::cerr << " TS: " << meta->mPublishTs; - std::cerr << std::endl; + RsDbg() << "RsGxsDataAccess::getMsgRelatedList() Found New OrigMsgId: " + << meta->mOrigMsgId + << " MsgId: " << meta->mMsgId + << " TS: " << meta->mPublishTs + << std::endl; #endif addMsg = true; @@ -1450,10 +1368,11 @@ bool RsGxsDataAccess::getMsgRelatedInfo(MsgRelatedInfoReq *req) else if (oit->second.second < meta->mPublishTs) { #ifdef DATA_DEBUG - std::cerr << "RsGxsDataAccess::getMsgRelatedList() Found Later Msg. OrigMsgId: "; - std::cerr << meta->mOrigMsgId; - std::cerr << " MsgId: " << meta->mMsgId; - std::cerr << " TS: " << meta->mPublishTs; + RsDbg() << "RsGxsDataAccess::getMsgRelatedList() Found Later Msg. OrigMsgId: " + << meta->mOrigMsgId + << " MsgId: " << meta->mMsgId + << " TS: " << meta->mPublishTs + << std::endl; #endif addMsg = true; @@ -1479,11 +1398,11 @@ bool RsGxsDataAccess::getMsgRelatedInfo(MsgRelatedInfoReq *req) /* first guess is potentially better than Orig (can't be worse!) */ rstime_t latestTs = 0; RsGxsMessageId latestMsgId; - RsGxsMsgMetaData* latestMeta=NULL; + const RsGxsMsgMetaData* latestMeta=nullptr; for(vit_meta = metaV.begin(); vit_meta != metaV.end(); ++vit_meta) { - RsGxsMsgMetaData* meta = *vit_meta; + const RsGxsMsgMetaData* meta = *vit_meta; if (meta->mOrigMsgId == origMsgId) { @@ -1503,7 +1422,7 @@ bool RsGxsDataAccess::getMsgRelatedInfo(MsgRelatedInfoReq *req) { for(vit_meta = metaV.begin(); vit_meta != metaV.end(); ++vit_meta) { - RsGxsMsgMetaData* meta = *vit_meta; + const RsGxsMsgMetaData* meta = *vit_meta; if (meta->mOrigMsgId == origMsgId) { @@ -1515,7 +1434,7 @@ bool RsGxsDataAccess::getMsgRelatedInfo(MsgRelatedInfoReq *req) GxsMsgIdResult filteredOutMsgIds; filteredOutMsgIds[grpId] = outMsgIds; - filterMsgList(filteredOutMsgIds, opts, filterMap); + filterMsgIdList(filteredOutMsgIds, opts, filterMap); if(!filteredOutMsgIds[grpId].empty()) { @@ -1539,8 +1458,6 @@ bool RsGxsDataAccess::getMsgRelatedInfo(MsgRelatedInfoReq *req) outMsgIds.clear(); filteredOutMsgIds.clear(); - - cleanseMsgMetaMap(result); } return true; } @@ -1553,7 +1470,7 @@ bool RsGxsDataAccess::getGroupStatistic(GroupStatisticRequest *req) GxsMsgMetaResult metaResult; mDataStore->retrieveGxsMsgMetaData(metaReq, metaResult); - std::vector& msgMetaV = metaResult[req->mGrpId]; + const std::vector& msgMetaV = metaResult[req->mGrpId]; req->mGroupStatistic.mGrpId = req->mGrpId; req->mGroupStatistic.mNumMsgs = msgMetaV.size(); @@ -1571,7 +1488,7 @@ bool RsGxsDataAccess::getGroupStatistic(GroupStatisticRequest *req) for(uint32_t i = 0; i < msgMetaV.size(); ++i) { - RsGxsMsgMetaData* m = msgMetaV[i]; + const RsGxsMsgMetaData* m = msgMetaV[i]; req->mGroupStatistic.mTotalSizeOfMsgs += m->mMsgSize + m->serial_size(); if(obsolete_msgs.find(m->mMsgId) != obsolete_msgs.end()) // skip obsolete messages. @@ -1597,7 +1514,7 @@ bool RsGxsDataAccess::getGroupStatistic(GroupStatisticRequest *req) } } - cleanseMsgMetaMap(metaResult); + //cleanseMsgMetaMap(metaResult); return true; } @@ -1645,65 +1562,60 @@ bool RsGxsDataAccess::getServiceStatistic(ServiceStatisticRequest *req) return true; } -bool RsGxsDataAccess::getMsgList(MsgIdReq* req) +bool RsGxsDataAccess::getMsgIdList(MsgIdReq* req) { GxsMsgMetaResult result; mDataStore->retrieveGxsMsgMetaData(req->mMsgIds, result); - GxsMsgMetaResult::iterator mit = result.begin(), mit_end = result.end(); for(; mit != mit_end; ++mit) { const RsGxsGroupId grpId = mit->first; - std::vector& metaV = mit->second; - std::vector::iterator vit = metaV.begin(), + std::vector& metaV = mit->second; + std::vector::iterator vit = metaV.begin(), vit_end = metaV.end(); for(; vit != vit_end; ++vit) { - RsGxsMsgMetaData* meta = *vit; + const RsGxsMsgMetaData* meta = *vit; req->mMsgIdResult[grpId].insert(meta->mMsgId); - delete meta; // discard meta data mem } } GxsMsgReq msgIdOut; // filter based on options - getMsgList(req->mMsgIdResult, req->Options, msgIdOut); + getMsgIdList(req->mMsgIdResult, req->Options, msgIdOut); req->mMsgIdResult = msgIdOut; return true; } -void RsGxsDataAccess::cleanseMsgMetaMap(GxsMsgMetaResult& result) +// void RsGxsDataAccess::cleanseMsgMetaMap(GxsMsgMetaResult& result) +// { +// GxsMsgMetaResult::iterator mit = result.begin(); +// +// for(; mit !=result.end(); ++mit) +// { +// +// std::vector& msgMetaV = mit->second; +// std::vector::iterator vit = msgMetaV.begin(); +// for(; vit != msgMetaV.end(); ++vit) +// { +// delete *vit; +// } +// } +// +// result.clear(); +// return; +// } + +void RsGxsDataAccess::filterMsgIdList( GxsMsgIdResult& resultsMap, const RsTokReqOptions& opts, const MsgMetaFilter& msgMetas ) const { - GxsMsgMetaResult::iterator mit = result.begin(); - - for(; mit !=result.end(); ++mit) - { - - std::vector& msgMetaV = mit->second; - std::vector::iterator vit = msgMetaV.begin(); - for(; vit != msgMetaV.end(); ++vit) - { - delete *vit; - } - } - - result.clear(); - return; -} - -void RsGxsDataAccess::filterMsgList( - GxsMsgIdResult& resultsMap, const RsTokReqOptions& opts, - const MsgMetaFilter& msgMetas ) const -{ - for( GxsMsgIdResult::iterator grpIt = resultsMap.begin(); - grpIt != resultsMap.end(); ++grpIt ) + for( GxsMsgIdResult::iterator grpIt = resultsMap.begin(); grpIt != resultsMap.end(); ++grpIt ) { const RsGxsGroupId& groupId(grpIt->first); std::set& msgsIdSet(grpIt->second); @@ -1711,32 +1623,33 @@ void RsGxsDataAccess::filterMsgList( MsgMetaFilter::const_iterator cit = msgMetas.find(groupId); if(cit == msgMetas.end()) continue; #ifdef DATA_DEBUG - std::cerr << __PRETTY_FUNCTION__ << " " << msgsIdSet.size() + RsDbg() << __PRETTY_FUNCTION__ << " " << msgsIdSet.size() << " for group: " << groupId << " before filtering" << std::endl; #endif - for( std::set::iterator msgIdIt = msgsIdSet.begin(); - msgIdIt != msgsIdSet.end(); ) + for( std::set::iterator msgIdIt = msgsIdSet.begin(); msgIdIt != msgsIdSet.end(); ) { const RsGxsMessageId& msgId(*msgIdIt); - const std::map& msgsMetaMap = + const std::map& msgsMetaMap = cit->second; bool keep = false; - std::map::const_iterator msgsMetaMapIt; + std::map::const_iterator msgsMetaMapIt; if( (msgsMetaMapIt = msgsMetaMap.find(msgId)) != msgsMetaMap.end() ) { keep = checkMsgFilter(opts, msgsMetaMapIt->second); } - if(keep) ++msgIdIt; - else msgIdIt = msgsIdSet.erase(msgIdIt); + if(keep) + ++msgIdIt; + else + msgIdIt = msgsIdSet.erase(msgIdIt); } #ifdef DATA_DEBUG - std::cerr << __PRETTY_FUNCTION__ << " " << msgsIdSet.size() + RsDbg() << __PRETTY_FUNCTION__ << " " << msgsIdSet.size() << " for group: " << groupId << " after filtering" << std::endl; #endif @@ -1770,23 +1683,44 @@ void RsGxsDataAccess::filterGrpList(std::list &grpIds, const RsTok } -bool RsGxsDataAccess::checkRequestStatus( - uint32_t token, GxsRequestStatus& status, uint32_t& reqtype, - uint32_t& anstype, rstime_t& ts ) +bool RsGxsDataAccess::checkRequestStatus( uint32_t token, GxsRequestStatus& status, uint32_t& reqtype, uint32_t& anstype, rstime_t& ts ) { RS_STACK_MUTEX(mDataMutex); - GxsRequest* req = locked_retrieveRequest(token); + GxsRequest* req = locked_retrieveCompetedRequest(token); - if (req == NULL || req->status == CANCELLED) - return false; +#ifdef DATA_DEBUG + RsDbg() << "CheckRequestStatus: token=" << token ; +#endif - anstype = req->ansType; - reqtype = req->reqType; - status = req->status; - ts = req->reqTime; + if(req != nullptr) + { + anstype = req->clientAnswerType; + reqtype = req->reqType; + status = COMPLETE; + ts = req->reqTime; +#ifdef DATA_DEBUG + RsDbg() << __PRETTY_FUNCTION__ << " Returning status = COMPLETE" << std::endl; +#endif + return true; + } - return true; + auto it = mPublicToken.find(token); + + if(it != mPublicToken.end()) + { + status = it->second; +#ifdef DATA_DEBUG + RsDbg() << __PRETTY_FUNCTION__ << " Returning status = " << status << std::endl; +#endif + return true; + } + + status = FAILED; +#ifdef DATA_DEBUG + RsDbg() << " Token not found. Returning FAILED" << std::endl; +#endif + return false; } bool RsGxsDataAccess::addGroupData(RsNxsGrp* grp) { @@ -1813,7 +1747,7 @@ bool RsGxsDataAccess::getGroupData(const RsGxsGroupId& grpId, RsNxsGrp *& grp_da std::map grps ; - grps[grpId] = NULL ; + grps[grpId] = nullptr ; if(mDataStore->retrieveNxsGrps(grps, false, true)) // the false here is very important: it removes the private key parts. { @@ -1840,18 +1774,16 @@ void RsGxsDataAccess::tokenList(std::list& tokens) RsStackMutex stack(mDataMutex); - std::map::iterator mit = mRequests.begin(); + for(auto& it:mRequestQueue) + tokens.push_back(it.second->token); - for(; mit != mRequests.end(); ++mit) - { - tokens.push_back(mit->first); - } + for(auto& it:mCompletedRequests) + tokens.push_back(it.first); } -bool RsGxsDataAccess::locked_updateRequestStatus( - uint32_t token, RsTokenService::GxsRequestStatus status ) +bool RsGxsDataAccess::locked_updateRequestStatus( uint32_t token, RsTokenService::GxsRequestStatus status ) { - GxsRequest* req = locked_retrieveRequest(token); + GxsRequest* req = locked_retrieveCompetedRequest(token); if(req) req->status = status; else return false; @@ -1866,7 +1798,7 @@ uint32_t RsGxsDataAccess::generatePublicToken() { RS_STACK_MUTEX(mDataMutex); - mPublicToken[token] = RsTokenService::PENDING; + mPublicToken[token] = PENDING ; } return token; @@ -1874,15 +1806,19 @@ uint32_t RsGxsDataAccess::generatePublicToken() -bool RsGxsDataAccess::updatePublicRequestStatus( - uint32_t token, RsTokenService::GxsRequestStatus status ) +bool RsGxsDataAccess::updatePublicRequestStatus( uint32_t token, RsTokenService::GxsRequestStatus status ) { RS_STACK_MUTEX(mDataMutex); - std::map::iterator mit = - mPublicToken.find(token); - if(mit != mPublicToken.end()) mit->second = status; - else return false; - return true; + + auto mit = mPublicToken.find(token); + + if(mit != mPublicToken.end()) + { + mit->second = status; + return true; + } + else + return false; } @@ -1890,11 +1826,14 @@ bool RsGxsDataAccess::updatePublicRequestStatus( bool RsGxsDataAccess::disposeOfPublicToken(uint32_t token) { RS_STACK_MUTEX(mDataMutex); - std::map::iterator mit = - mPublicToken.find(token); - if(mit != mPublicToken.end()) mPublicToken.erase(mit); - else return false; - return true; + auto mit = mPublicToken.find(token); + if(mit != mPublicToken.end()) + { + mPublicToken.erase(mit); + return true; + } + else + return false; } bool RsGxsDataAccess::checkGrpFilter(const RsTokReqOptions &opts, const RsGxsGrpMetaData *meta) const @@ -1927,7 +1866,7 @@ bool RsGxsDataAccess::checkMsgFilter( (opts.mStatusMask & meta->mMsgStatus) ) { #ifdef DATA_DEBUG - std::cerr << __PRETTY_FUNCTION__ + RsDbg() << __PRETTY_FUNCTION__ << " Continue checking Msg as StatusMatches: " << " Mask: " << opts.mStatusMask << " StatusFilter: " << opts.mStatusFilter @@ -1938,7 +1877,7 @@ bool RsGxsDataAccess::checkMsgFilter( else { #ifdef DATA_DEBUG - std::cerr << __PRETTY_FUNCTION__ + RsDbg() << __PRETTY_FUNCTION__ << " Dropping Msg due to !StatusMatch " << " Mask: " << opts.mStatusMask << " StatusFilter: " << opts.mStatusFilter @@ -1952,7 +1891,7 @@ bool RsGxsDataAccess::checkMsgFilter( else { #ifdef DATA_DEBUG - std::cerr << __PRETTY_FUNCTION__ + RsDbg() << __PRETTY_FUNCTION__ << " Status check not requested" << " mStatusMask: " << opts.mStatusMask << " MsgId: " << meta->mMsgId << std::endl; @@ -1966,7 +1905,7 @@ bool RsGxsDataAccess::checkMsgFilter( (opts.mMsgFlagMask & meta->mMsgFlags) ) { #ifdef DATA_DEBUG - std::cerr << __PRETTY_FUNCTION__ + RsDbg() << __PRETTY_FUNCTION__ << " Accepting Msg as FlagMatches: " << " Mask: " << opts.mMsgFlagMask << " FlagFilter: " << opts.mMsgFlagFilter @@ -1977,7 +1916,7 @@ bool RsGxsDataAccess::checkMsgFilter( else { #ifdef DATA_DEBUG - std::cerr << __PRETTY_FUNCTION__ + RsDbg() << __PRETTY_FUNCTION__ << " Dropping Msg due to !FlagMatch " << " Mask: " << opts.mMsgFlagMask << " FlagFilter: " << opts.mMsgFlagFilter @@ -1991,7 +1930,7 @@ bool RsGxsDataAccess::checkMsgFilter( else { #ifdef DATA_DEBUG - std::cerr << __PRETTY_FUNCTION__ + RsDbg() << __PRETTY_FUNCTION__ << " Flags check not requested" << " mMsgFlagMask: " << opts.mMsgFlagMask << " MsgId: " << meta->mMsgId << std::endl; @@ -2001,3 +1940,5 @@ bool RsGxsDataAccess::checkMsgFilter( return true; } +GxsGroupStatistic::~GxsGroupStatistic() = default; +GxsServiceStatistic::~GxsServiceStatistic() = default; diff --git a/libretroshare/src/gxs/rsgxsdataaccess.h b/libretroshare/src/gxs/rsgxsdataaccess.h index c5bfdf084..727ebe605 100644 --- a/libretroshare/src/gxs/rsgxsdataaccess.h +++ b/libretroshare/src/gxs/rsgxsdataaccess.h @@ -22,14 +22,17 @@ #ifndef RSGXSDATAACCESS_H #define RSGXSDATAACCESS_H +#include #include "retroshare/rstokenservice.h" #include "rsgxsrequesttypes.h" #include "rsgds.h" -typedef std::map< RsGxsGroupId, std::map > MsgMetaFilter; +typedef std::map< RsGxsGroupId, std::map > MsgMetaFilter; typedef std::map< RsGxsGroupId, RsGxsGrpMetaData* > GrpMetaFilter; +bool operator<(const std::pair& p1,const std::pair& p2); + class RsGxsDataAccess : public RsTokenService { public: @@ -56,7 +59,7 @@ public: * @param groupIds group id to request info for * @return */ - bool requestGroupInfo(uint32_t &token, uint32_t ansType, const RsTokReqOptions &opts, const std::list &groupIds); + bool requestGroupInfo(uint32_t &token, uint32_t ansType, const RsTokReqOptions &opts, const std::list &groupIds) override; /*! * Use this to request all group related info @@ -65,7 +68,7 @@ public: * @param opts Additional option that affect outcome of request. Please see specific services, for valid values * @return */ - bool requestGroupInfo(uint32_t &token, uint32_t ansType, const RsTokReqOptions &opts); + bool requestGroupInfo(uint32_t &token, uint32_t ansType, const RsTokReqOptions &opts) override; /*! * Use this to get msg information (id, meta, or data), store token value to poll for request completion @@ -75,7 +78,7 @@ public: * @param groupIds The ids of the groups to get, second entry of map empty to query for all msgs * @return true if request successful false otherwise */ - bool requestMsgInfo(uint32_t &token, uint32_t ansType, const RsTokReqOptions &opts, const GxsMsgReq& msgIds); + bool requestMsgInfo(uint32_t &token, uint32_t ansType, const RsTokReqOptions &opts, const GxsMsgReq& msgIds) override; /*! * Use this to get message information (id, meta, or data), store token value to poll for request completion @@ -86,7 +89,7 @@ public: * all messages for all groups are retrieved * @return true if request successful false otherwise */ - bool requestMsgInfo(uint32_t &token, uint32_t ansType, const RsTokReqOptions &opts, const std::list& grpIds); + bool requestMsgInfo(uint32_t &token, uint32_t ansType, const RsTokReqOptions &opts, const std::list& grpIds) override; /*! * For requesting msgs related to a given msg id within a group @@ -96,7 +99,7 @@ public: * @param groupIds The ids of the groups to get, second entry of map empty to query for all msgs * @return true if request successful false otherwise */ - bool requestMsgRelatedInfo(uint32_t &token, uint32_t ansType, const RsTokReqOptions &opts, const std::vector &msgIds); + bool requestMsgRelatedInfo(uint32_t &token, uint32_t ansType, const RsTokReqOptions &opts, const std::vector &msgIds) override; /*! * This request statistics on amount of data held @@ -107,19 +110,20 @@ public: * total size of messages * total size of groups * @param token + * @param opts Additional option that affect outcome of request. Please see specific services, for valid values */ - void requestServiceStatistic(uint32_t& token); + void requestServiceStatistic(uint32_t& token, const RsTokReqOptions &opts) override; /*! * To request statistic on a group * @param token set to value to be redeemed to get statistic * @param grpId the id of the group + * @param opts Additional option that affect outcome of request. Please see specific services, for valid values */ - void requestGroupStatistic(uint32_t& token, const RsGxsGroupId& grpId); - + void requestGroupStatistic(uint32_t& token, const RsGxsGroupId& grpId, const RsTokReqOptions &opts) override; /* Poll */ - GxsRequestStatus requestStatus(const uint32_t token); + GxsRequestStatus requestStatus(uint32_t token); /* Cancel Request */ bool cancelRequest(const uint32_t &token); @@ -200,7 +204,8 @@ public: * @param token request token to be redeemed * @param msgIds */ - bool getMsgList(const uint32_t &token, GxsMsgIdResult &msgIds); + bool getMsgIdList(const uint32_t &token, GxsMsgIdResult &msgIds); + /*! * Retrieve msg list for a given token for message related info @@ -271,7 +276,7 @@ private: * @param token the value of the token for the request object handle wanted * @return the request associated to this token */ - GxsRequest* locked_retrieveRequest(const uint32_t& token); + GxsRequest* locked_retrieveCompetedRequest(const uint32_t& token); /*! * Add a gxs request to queue @@ -323,11 +328,11 @@ private: */ void tokenList(std::list &tokens); - /*! - * Convenience function to delete the ids - * @param filter the meta filter to clean - */ - void cleanseMsgMetaMap(GxsMsgMetaResult& result); + // /*! + // * Convenience function to delete the ids + // * @param filter the meta filter to clean + // */ + // void cleanseMsgMetaMap(GxsMsgMetaResult& result); public: @@ -378,8 +383,18 @@ private: * @param req * @return false if unsuccessful, true otherwise */ - bool getMsgList(MsgIdReq* req); + bool getMsgIdList(MsgIdReq* req); + /*! + * Attempts to retrieve msg Meta list from data store + * Computationally/CPU-Bandwidth expensive + * + * @param msgIds List of message Ids for the Message Metas to retrieve + * @param opts GxsRequest options + * @param result Map of Meta information for messages + * + */ + bool getMsgMetaDataList( const GxsMsgReq& msgIds, const RsTokReqOptions& opts, GxsMsgMetaResult& result ); /*! * Attempts to retrieve group meta data from data store @@ -445,7 +460,7 @@ private: * @param opts the request options set by user * @param meta The accompanying meta information for msg, ids */ - void filterMsgList(GxsMsgIdResult& msgIds, const RsTokReqOptions& opts, const MsgMetaFilter& meta) const; + void filterMsgIdList(GxsMsgIdResult& msgIds, const RsTokReqOptions& opts, const MsgMetaFilter& meta) const; /*! * This filter msgs based of options supplied (at the moment just status masks) @@ -482,9 +497,10 @@ private: * @param opts the options used to parameterise the id filter * @param msgIdsOut the left overs ids after filter is applied to msgIds */ - bool getMsgList(const GxsMsgReq& msgIds, const RsTokReqOptions& opts, GxsMsgReq& msgIdsOut); + bool getMsgIdList(const GxsMsgReq& msgIds, const RsTokReqOptions& opts, GxsMsgReq& msgIdsOut); private: + bool locked_clearRequest(const uint32_t &token); RsGeneralDataService* mDataStore; @@ -492,10 +508,9 @@ private: uint32_t mNextToken; std::map mPublicToken; - std::map mRequests; - - + std::set > mRequestQueue; + std::map mCompletedRequests; }; #endif // RSGXSDATAACCESS_H diff --git a/libretroshare/src/gxs/rsgxsnetservice.cc b/libretroshare/src/gxs/rsgxsnetservice.cc index 5cebfbfb3..8ed657bfb 100644 --- a/libretroshare/src/gxs/rsgxsnetservice.cc +++ b/libretroshare/src/gxs/rsgxsnetservice.cc @@ -272,6 +272,7 @@ NXS_NET_DEBUG_6 group sync statistics (e.g. number of posts at nighbour nodes, etc) NXS_NET_DEBUG_7 encryption/decryption of transactions NXS_NET_DEBUG_8 gxs distant sync + NXS_NET_DEBUG_9 gxs distant search ***/ //#define NXS_NET_DEBUG_0 1 @@ -283,6 +284,7 @@ //#define NXS_NET_DEBUG_6 1 //#define NXS_NET_DEBUG_7 1 //#define NXS_NET_DEBUG_8 1 +//#define NXS_NET_DEBUG_9 1 //#define NXS_FRAG @@ -306,6 +308,7 @@ static const uint32_t GROUP_STATS_UPDATE_DELAY = 240; // static const uint32_t GROUP_STATS_UPDATE_NB_PEERS = 2; // number of peers to which the group stats are asked static const uint32_t MAX_ALLOWED_GXS_MESSAGE_SIZE = 199000; // 200,000 bytes including signature and headers static const uint32_t MIN_DELAY_BETWEEN_GROUP_SEARCH = 40; // dont search same group more than every 40 secs. +static const uint32_t SAFETY_DELAY_FOR_UNSUCCESSFUL_UPDATE = 1800; // avoid re-sending the same msg list to a peer who asks twice for the same update in less than this time static const uint32_t RS_NXS_ITEM_ENCRYPTION_STATUS_UNKNOWN = 0x00 ; static const uint32_t RS_NXS_ITEM_ENCRYPTION_STATUS_NO_ERROR = 0x01 ; @@ -318,11 +321,11 @@ static const uint32_t RS_NXS_ITEM_ENCRYPTION_STATUS_GXS_KEY_MISSING = 0x05 ; #if defined(NXS_NET_DEBUG_0) || defined(NXS_NET_DEBUG_1) || defined(NXS_NET_DEBUG_2) || defined(NXS_NET_DEBUG_3) \ || defined(NXS_NET_DEBUG_4) || defined(NXS_NET_DEBUG_5) || defined(NXS_NET_DEBUG_6) || defined(NXS_NET_DEBUG_7) \ - || defined(NXS_NET_DEBUG_8) + || defined(NXS_NET_DEBUG_8) || defined(NXS_NET_DEBUG_9) -static const RsPeerId peer_to_print = RsPeerId(std::string("")) ; -static const RsGxsGroupId group_id_to_print = RsGxsGroupId(std::string("")) ; // use this to allow to this group id only, or "" for all IDs -static const uint32_t service_to_print = RS_SERVICE_GXS_TYPE_CHANNELS ; // use this to allow to this service id only, or 0 for all services +static const RsPeerId peer_to_print = RsPeerId();//std::string("a97fef0e2dc82ddb19200fb30f9ac575")) ; +static const RsGxsGroupId group_id_to_print = RsGxsGroupId();//std::string("66052380f5d1d0c5992e2b55dc402ce6")) ; // use this to allow to this group id only, or "" for all IDs +static const uint32_t service_to_print = RS_SERVICE_GXS_TYPE_GXSID; // use this to allow to this service id only, or 0 for all services // warning. Numbers should be SERVICE IDS (see serialiser/rsserviceids.h. E.g. 0x0215 for forums) class nullstream: public std::ostream {}; @@ -590,32 +593,30 @@ void RsGxsNetService::syncWithPeers() return; } - std::set::iterator sit = peers.begin(); + // for now just grps + for(auto sit = peers.begin(); sit != peers.end(); ++sit) + { - // for now just grps - for(; sit != peers.end(); ++sit) - { + const RsPeerId peerId = *sit; - const RsPeerId peerId = *sit; + ClientGrpMap::const_iterator cit = mClientGrpUpdateMap.find(peerId); + uint32_t updateTS = 0; - ClientGrpMap::const_iterator cit = mClientGrpUpdateMap.find(peerId); - uint32_t updateTS = 0; - - if(cit != mClientGrpUpdateMap.end()) - { - const RsGxsGrpUpdate *gui = &cit->second; - updateTS = gui->grpUpdateTS; - } - RsNxsSyncGrpReqItem *grp = new RsNxsSyncGrpReqItem(mServType); - grp->clear(); - grp->PeerId(*sit); - grp->updateTS = updateTS; + if(cit != mClientGrpUpdateMap.end()) + { + const RsGxsGrpUpdate *gui = &cit->second; + updateTS = gui->grpUpdateTS; + } + RsNxsSyncGrpReqItem *grp = new RsNxsSyncGrpReqItem(mServType); + grp->clear(); + grp->PeerId(*sit); + grp->updateTS = updateTS; #ifdef NXS_NET_DEBUG_5 - GXSNETDEBUG_P_(*sit) << "Service "<< std::hex << ((mServiceInfo.mServiceType >> 8)& 0xffff) << std::dec << " sending global group TS of peer id: " << *sit << " ts=" << nice_time_stamp(time(NULL),updateTS) << " (secs ago) to himself" << std::endl; + GXSNETDEBUG_P_(*sit) << "Service "<< std::hex << ((mServiceInfo.mServiceType >> 8)& 0xffff) << std::dec << " sending global group TS of peer id: " << *sit << " ts=" << nice_time_stamp(time(NULL),updateTS) << " (secs ago) to himself" << std::endl; #endif - generic_sendItem(grp); - } + generic_sendItem(grp); + } if(!mAllowMsgSync) return ; @@ -641,15 +642,13 @@ void RsGxsNetService::syncWithPeers() } } - sit = peers.begin(); - // Synchronise group msg for groups which we're subscribed to // For each peer and each group, we send to the peer the time stamp of the most // recent modification the peer has sent. If the peer has more recent messages he will send them, because its latest // modifications will be more recent. This ensures that we always compare timestamps all taken in the same // computer (the peer's computer in this case) - for(; sit != peers.end(); ++sit) + for(auto sit = peers.begin(); sit != peers.end(); ++sit) { const RsPeerId& peerId = *sit; @@ -953,7 +952,7 @@ void RsGxsNetService::handleRecvSyncGrpStatistics(RsNxsSyncGrpStatsItem *grs) #endif mDataStore->retrieveGxsMsgMetaData(reqIds, result); - const std::vector& vec(result[grs->grpId]) ; + const std::vector& vec(result[grs->grpId]) ; if(vec.empty()) // that means we don't have any, or there isn't any, but since the default is always 0, no need to send. return ; @@ -970,12 +969,9 @@ void RsGxsNetService::handleRecvSyncGrpStatistics(RsNxsSyncGrpStatsItem *grs) // be used to discard groups that are not used. for(uint32_t i=0;ilast_post_TS < vec[i]->mPublishTs) grs_resp->last_post_TS = vec[i]->mPublishTs; - delete vec[i] ; - } #ifdef NXS_NET_DEBUG_6 GXSNETDEBUG_PG(grs->PeerId(),grs->grpId) << " sending back statistics item with " << vec.size() << " elements." << std::endl; #endif @@ -2953,21 +2949,19 @@ void RsGxsNetService::locked_genReqMsgTransaction(NxsTransaction* tr) reqIds[grpId] = std::set(); GxsMsgMetaResult result; mDataStore->retrieveGxsMsgMetaData(reqIds, result); - std::vector &msgMetaV = result[grpId]; + std::vector &msgMetaV = result[grpId]; #ifdef NXS_NET_DEBUG_1 GXSNETDEBUG_PG(item->PeerId(),grpId) << " retrieving grp message list..." << std::endl; GXSNETDEBUG_PG(item->PeerId(),grpId) << " grp locally contains " << msgMetaV.size() << " messsages." << std::endl; #endif - std::vector::const_iterator vit = msgMetaV.begin(); + std::vector::const_iterator vit = msgMetaV.begin(); std::set msgIdSet; // put ids in set for each searching for(; vit != msgMetaV.end(); ++vit) - { msgIdSet.insert((*vit)->mMsgId); - delete(*vit); - } + msgMetaV.clear(); #ifdef NXS_NET_DEBUG_1 @@ -3185,7 +3179,8 @@ void RsGxsNetService::locked_genReqGrpTransaction(NxsTransaction* tr) { grpItemL.push_back(item); grpMetaMap[item->grpId] = NULL; - }else + } + else { #ifdef NXS_NET_DEBUG_0 GXSNETDEBUG_PG(tr->mTransaction->PeerId(),item->grpId) << "RsGxsNetService::genReqGrpTransaction(): item failed to caste to RsNxsSyncMsgItem* " << std::endl; @@ -3228,7 +3223,7 @@ void RsGxsNetService::locked_genReqGrpTransaction(NxsTransaction* tr) RsNxsSyncGrpItem*& grpSyncItem = *llit; const RsGxsGroupId& grpId = grpSyncItem->grpId; - std::map::const_iterator metaIter = grpMetaMap.find(grpId); + std::map::const_iterator metaIter = grpMetaMap.find(grpId); bool haveItem = false; bool latestVersion = false; @@ -3239,19 +3234,21 @@ void RsGxsNetService::locked_genReqGrpTransaction(NxsTransaction* tr) } // FIXTESTS global variable rsReputations not available in unittests! -#warning csoler 2016-12-23: Update the code below to correctly send/recv dependign on reputation - if( !grpSyncItem->authorId.isNull() && - mReputations->overallReputationLevel(grpSyncItem->authorId) == - RsReputationLevel::LOCALLY_NEGATIVE ) + if( mReputations->overallReputationLevel(RsGxsId(grpSyncItem->grpId)) == RsReputationLevel::LOCALLY_NEGATIVE ) { #ifdef NXS_NET_DEBUG_0 - GXSNETDEBUG_PG(tr->mTransaction->PeerId(),grpId) << " Identity " << grpSyncItem->authorId << " is banned. Not syncing group." << std::endl; + GXSNETDEBUG_PG(tr->mTransaction->PeerId(),grpId) << " Identity " << grpSyncItem->grpId << " is banned. Not GXS-syncing group." << std::endl; #endif continue ; } if( (mGrpAutoSync && !haveItem) || latestVersion) + { +#ifdef NXS_NET_DEBUG_0 + GXSNETDEBUG_PG(tr->mTransaction->PeerId(),grpId) << " Identity " << grpId << " will be sync-ed using GXS. mGrpAutoSync:" << mGrpAutoSync << " haveItem:" << haveItem << " latest_version: " << latestVersion << std::endl; +#endif addGroupItemToList(tr, grpId, transN, reqList); + } } if(!reqList.empty()) @@ -3598,6 +3595,10 @@ void RsGxsNetService::locked_genSendMsgsTransaction(NxsTransaction* tr) msg->count = 1; // only one piece. This is to keep compatibility if we ever implement fragmenting in the future. msg->pos = 0; +#ifdef NXS_NET_DEBUG_0 + GXSNETDEBUG_PG(tr->mTransaction->PeerId(),msg->grpId) << " sending msg Id " << msg->msgId << " in Group " << msg->grpId << std::endl; +#endif + newTr->mItems.push_back(msg); msgSize++; #endif @@ -3994,7 +3995,7 @@ bool RsGxsNetService::locked_CanReceiveUpdate(const RsNxsSyncGrpReqItem *item) GXSNETDEBUG_P_(item->PeerId()) << " local modification time stamp: " << std::dec<< time(NULL) - mGrpServerUpdate.grpUpdateTS << " secs ago. Update sent: " << ((item->updateTS < mGrpServerUpdate.grpUpdateTS)?"YES":"NO") << std::endl; #endif - return item->updateTS < mGrpServerUpdate.grpUpdateTS; + return item->updateTS < mGrpServerUpdate.grpUpdateTS && locked_checkResendingOfUpdates(item->PeerId(),RsGxsGroupId(),item->updateTS,mGrpServerUpdate.grpUpdateTsRecords[item->PeerId()]) ; } void RsGxsNetService::handleRecvSyncGroup(RsNxsSyncGrpReqItem *item) @@ -4239,10 +4240,33 @@ bool RsGxsNetService::checkCanRecvMsgFromPeer(const RsPeerId& sslId, const RsGxs return true; } +bool RsGxsNetService::locked_checkResendingOfUpdates(const RsPeerId& pid,const RsGxsGroupId& grpId,rstime_t incoming_ts,RsPeerUpdateTsRecord& rec) +{ + rstime_t now = time(NULL); + + // Now we check if the peer is sending the same outdated TS for the same time in a short while. This would mean the peer + // hasn't finished processing the updates we're sending and we shouldn't send new data anymore. Of course the peer might + // have disconnected or so, which means that we need to be careful about not sending. As a compromise we still send, but + // after waiting for a while (See + + if(rec.mLastTsReceived == incoming_ts && rec.mTs + SAFETY_DELAY_FOR_UNSUCCESSFUL_UPDATE > now) + { +#ifdef NXS_NET_DEBUG_0 + GXSNETDEBUG_PG(pid,grpId) << "(II) peer " << pid << " already sent the same TS " << (long int)now-(long int)rec.mTs << " secs ago for that group ID. Will not send msg list again for a while to prevent clogging..." << std::endl; +#endif + return false; + } + + rec.mLastTsReceived = incoming_ts; + rec.mTs = now; + + return true; +} + bool RsGxsNetService::locked_CanReceiveUpdate(RsNxsSyncMsgReqItem *item,bool& grp_is_known) { // Do we have new updates for this peer? - // Here we compare times in the same clock: the friend's clock, so it should be fine. + // Here we compare times in the same clock: our own clock, so it should be fine. grp_is_known = false ; @@ -4251,7 +4275,7 @@ bool RsGxsNetService::locked_CanReceiveUpdate(RsNxsSyncMsgReqItem *item,bool& gr // Item contains the hashed group ID in order to protect is from friends who don't know it. So we de-hash it using bruteforce over known group IDs for this peer. // We could save the de-hash result. But the cost is quite light, since the number of encrypted groups per service is usually low. - for(ServerMsgMap::const_iterator it(mServerMsgUpdateMap.begin());it!=mServerMsgUpdateMap.end();++it) + for(ServerMsgMap::iterator it(mServerMsgUpdateMap.begin());it!=mServerMsgUpdateMap.end();++it) if(item->grpId == hashGrpId(it->first,item->PeerId())) { item->grpId = it->first ; @@ -4261,20 +4285,25 @@ bool RsGxsNetService::locked_CanReceiveUpdate(RsNxsSyncMsgReqItem *item,bool& gr #endif grp_is_known = true ; - return item->updateTS < it->second.msgUpdateTS ; + // The order of tests below is important because we want to only modify the map of requests records if the request actually is a valid requests instead of + // a simple check that nothing's changed. + + return item->updateTS < it->second.msgUpdateTS && locked_checkResendingOfUpdates(item->PeerId(),item->grpId,item->updateTS,it->second.msgUpdateTsRecords[item->PeerId()]) ; } return false ; } - ServerMsgMap::const_iterator cit = mServerMsgUpdateMap.find(item->grpId); + ServerMsgMap::iterator cit = mServerMsgUpdateMap.find(item->grpId); + if(cit != mServerMsgUpdateMap.end()) { #ifdef NXS_NET_DEBUG_0 GXSNETDEBUG_PG(item->PeerId(),item->grpId) << " local time stamp: " << std::dec<< time(NULL) - cit->second.msgUpdateTS << " secs ago. Update sent: " << (item->updateTS < cit->second.msgUpdateTS) << std::endl; #endif grp_is_known = true ; - return item->updateTS < cit->second.msgUpdateTS ; + + return item->updateTS < cit->second.msgUpdateTS && locked_checkResendingOfUpdates(item->PeerId(),item->grpId,item->updateTS,cit->second.msgUpdateTsRecords[item->PeerId()]) ; } #ifdef NXS_NET_DEBUG_0 @@ -4295,6 +4324,9 @@ void RsGxsNetService::handleRecvSyncMessage(RsNxsSyncMsgReqItem *item,bool item_ bool grp_is_known = false; bool was_circle_protected = item_was_encrypted || bool(item->flag & RsNxsSyncMsgReqItem::FLAG_USE_HASHED_GROUP_ID); + // This call determines if the peer can receive updates from us, meaning that our last TS is larger than what the peer sent. + // It also changes the items' group id into the un-hashed group ID if the group is a distant group. + bool peer_can_receive_update = locked_CanReceiveUpdate(item, grp_is_known); if(item_was_encrypted) @@ -4310,7 +4342,7 @@ void RsGxsNetService::handleRecvSyncMessage(RsNxsSyncMsgReqItem *item,bool item_ // We update suppliers in two cases: // Case 1: the grp is known because it is the hash of an existing group, but it's not yet in the server config map - // Case 2: the gtp is not known, possibly because it was deleted, but there's an entry in mServerGrpConfigMap due to statistics gathering. Still, statistics are only + // Case 2: the grp is not known, possibly because it was deleted, but there's an entry in mServerGrpConfigMap due to statistics gathering. Still, statistics are only // gathered from known suppliers. So statistics never add new suppliers. These are only added here. if(grp_is_known || mServerGrpConfigMap.find(item->grpId)!=mServerGrpConfigMap.end()) @@ -4363,7 +4395,7 @@ void RsGxsNetService::handleRecvSyncMessage(RsNxsSyncMsgReqItem *item,bool item_ GxsMsgMetaResult metaResult; mDataStore->retrieveGxsMsgMetaData(req, metaResult); - std::vector& msgMetas = metaResult[item->grpId]; + std::vector& msgMetas = metaResult[item->grpId]; #ifdef NXS_NET_DEBUG_0 GXSNETDEBUG_PG(item->PeerId(),item->grpId) << " retrieving message meta data." << std::endl; @@ -4391,9 +4423,9 @@ void RsGxsNetService::handleRecvSyncMessage(RsNxsSyncMsgReqItem *item,bool item_ if(canSendMsgIds(msgMetas, *grpMeta, peer, should_encrypt_to_this_circle_id)) { - for(std::vector::iterator vit = msgMetas.begin();vit != msgMetas.end(); ++vit) + for(auto vit = msgMetas.begin();vit != msgMetas.end(); ++vit) { - RsGxsMsgMetaData* m = *vit; + const RsGxsMsgMetaData* m = *vit; // Check reputation @@ -4412,7 +4444,7 @@ void RsGxsNetService::handleRecvSyncMessage(RsNxsSyncMsgReqItem *item,bool item_ if(details.mReputation.mOverallReputationLevel < minReputationForForwardingMessages(grpMeta->mSignFlags, details.mFlags)) { #ifdef NXS_NET_DEBUG_0 - GXSNETDEBUG_PG(item->PeerId(),item->grpId) << " not sending item ID " << (*vit)->mMsgId << ", because the author is flags " << std::hex << details.mFlags << std::dec << " and reputation level " << details.mReputation.mOverallReputationLevel << std::endl; + GXSNETDEBUG_PG(item->PeerId(),item->grpId) << " not sending item ID " << (*vit)->mMsgId << ", because the author is flags " << std::hex << details.mFlags << std::dec << " and reputation level " << (int) details.mReputation.mOverallReputationLevel << std::endl; #endif continue ; } @@ -4493,8 +4525,8 @@ void RsGxsNetService::handleRecvSyncMessage(RsNxsSyncMsgReqItem *item,bool item_ // release meta resource - for(std::vector::iterator vit = msgMetas.begin(); vit != msgMetas.end(); ++vit) - delete *vit; + // for(std::vector::iterator vit = msgMetas.begin(); vit != msgMetas.end(); ++vit) + // delete *vit; } void RsGxsNetService::locked_pushMsgRespFromList(std::list& itemL, const RsPeerId& sslId, const RsGxsGroupId& grp_id,const uint32_t& transN) @@ -4538,7 +4570,7 @@ void RsGxsNetService::locked_pushMsgRespFromList(std::list& itemL, c } } -bool RsGxsNetService::canSendMsgIds(std::vector& msgMetas, const RsGxsGrpMetaData& grpMeta, const RsPeerId& sslId,RsGxsCircleId& should_encrypt_id) +bool RsGxsNetService::canSendMsgIds(std::vector& msgMetas, const RsGxsGrpMetaData& grpMeta, const RsPeerId& sslId,RsGxsCircleId& should_encrypt_id) { #ifdef NXS_NET_DEBUG_4 GXSNETDEBUG_PG(sslId,grpMeta.mGroupId) << "RsGxsNetService::canSendMsgIds() CIRCLE VETTING" << std::endl; @@ -4605,7 +4637,7 @@ bool RsGxsNetService::canSendMsgIds(std::vector& msgMetas, co GXSNETDEBUG_PG(sslId,grpMeta.mGroupId) << " deleting MsgMeta entry for msg ID " << msgMetas[i]->mMsgId << " signed by " << msgMetas[i]->mAuthorId << " who is not in group circle " << circleId << std::endl; #endif - delete msgMetas[i] ; + //delete msgMetas[i] ; msgMetas[i] = msgMetas[msgMetas.size()-1] ; msgMetas.pop_back() ; } @@ -5156,7 +5188,7 @@ static bool termSearch(const std::string& src, const std::string& substring) } #endif // ndef RS_DEEP_CHANNEL_INDEX -bool RsGxsNetService::retrieveDistantSearchResults(TurtleRequestId req,std::map& group_infos) +bool RsGxsNetService::retrieveDistantSearchResults(TurtleRequestId req,std::map& group_infos) { RS_STACK_MUTEX(mNxsMutex) ; @@ -5168,7 +5200,7 @@ bool RsGxsNetService::retrieveDistantSearchResults(TurtleRequestId req,std::map< group_infos = it->second; return true ; } -bool RsGxsNetService::retrieveDistantGroupSummary(const RsGxsGroupId& group_id,RsGxsGroupSummary& gs) +bool RsGxsNetService::retrieveDistantGroupSummary(const RsGxsGroupId& group_id,RsGxsGroupSearchResults& gs) { RS_STACK_MUTEX(mNxsMutex) ; for(auto it(mDistantSearchResults.begin());it!=mDistantSearchResults.end();++it) @@ -5190,8 +5222,7 @@ bool RsGxsNetService::clearDistantSearchResults(const TurtleRequestId& id) return true ; } -void RsGxsNetService::receiveTurtleSearchResults( - TurtleRequestId req, const std::list& group_infos ) +void RsGxsNetService::receiveTurtleSearchResults( TurtleRequestId req, const std::list& group_infos ) { std::set groupsToNotifyResults; @@ -5199,20 +5230,43 @@ void RsGxsNetService::receiveTurtleSearchResults( RS_STACK_MUTEX(mNxsMutex); RsGxsGrpMetaTemporaryMap grpMeta; - std::map& - search_results_map(mDistantSearchResults[req]); + std::map& search_results_map(mDistantSearchResults[req]); + +#ifdef NXS_NET_DEBUG_9 + std::cerr << "Received group summary through turtle search for the following groups:" << std::endl; +#endif for(const RsGxsGroupSummary& gps : group_infos) - if(search_results_map.find(gps.mGroupId) == search_results_map.end()) - grpMeta[gps.mGroupId] = nullptr; + { + std::cerr <<" " << gps.mGroupId << " \"" << gps.mGroupName << "\"" << std::endl; + grpMeta[gps.mGroupId] = nullptr; + } + mDataStore->retrieveGxsGrpMetaData(grpMeta); +#ifdef NXS_NET_DEBUG_9 + std::cerr << "Retrieved data store group data for the following groups:" <mGroupName << std::endl; +#endif + for (const RsGxsGroupSummary& gps : group_infos) { #ifndef RS_DEEP_CHANNEL_INDEX /* Only keep groups that are not locally known, and groups that are - * not already in the mDistantSearchResults structure. */ - if(grpMeta[gps.mGroupId]) continue; + * not already in the mDistantSearchResults structure. + * mDataStore may in some situations allocate an empty group meta data, so it's important + * to test that the group meta is both non null and actually corresponds to the group id we seek. */ + + auto& meta(grpMeta[gps.mGroupId]); + + if(meta != nullptr && meta->mGroupId == gps.mGroupId) + continue; + +#ifdef NXS_NET_DEBUG_9 + std::cerr << " group " << gps.mGroupId << " is not known. Adding it to search results..." << std::endl; +#endif + #else // ndef RS_DEEP_CHANNEL_INDEX /* When deep search is enabled search results may bring more info * then we already have also about post that are indexed by xapian, @@ -5221,22 +5275,32 @@ void RsGxsNetService::receiveTurtleSearchResults( const RsGxsGroupId& grpId(gps.mGroupId); groupsToNotifyResults.insert(grpId); - auto it2 = search_results_map.find(grpId); - if(it2 != search_results_map.end()) - { - // update existing data - RsGxsGroupSummary& eGpS(it2->second); - eGpS.mPopularity++; - eGpS.mNumberOfMessages = std::max( - eGpS.mNumberOfMessages, - gps.mNumberOfMessages ); - } - else - { - search_results_map[grpId] = gps; - // number of results so far - search_results_map[grpId].mPopularity = 1; - } + + // Find search results place for this particular group + +#ifdef NXS_NET_DEBUG_9 + std::cerr << " Adding gps=" << gps.mGroupId << " name=\"" << gps.mGroupName << "\" gps.mSearchContext=\"" << gps.mSearchContext << "\"" << std::endl; +#endif + RsGxsGroupSearchResults& eGpS(search_results_map[grpId]); + + if(eGpS.mGroupId != grpId) // not initialized yet. So we do it now. + { + eGpS.mGroupId = gps.mGroupId; + eGpS.mGroupName = gps.mGroupName; + eGpS.mAuthorId = gps.mAuthorId; + eGpS.mPublishTs = gps.mPublishTs; + eGpS.mSignFlags = gps.mSignFlags; + } + // We should check that the above values are always the same for all info that is received. In the end, we'll + // request the group meta and check the signature, but it may be misleading to receive a forged information + // that is not the real one. + + ++eGpS.mPopularity; // increase popularity. This is not a real counting, but therefore some heuristic estimate. + eGpS.mNumberOfMessages = std::max( eGpS.mNumberOfMessages, gps.mNumberOfMessages ); + eGpS.mLastMessageTs = std::max( eGpS.mLastMessageTs, gps.mLastMessageTs ); + + if(gps.mSearchContext != gps.mGroupName) // this is a bit of a hack. We should have flags to tell where the search hit happens + eGpS.mSearchContexts.insert(gps.mSearchContext); } } // end RS_STACK_MUTEX(mNxsMutex); diff --git a/libretroshare/src/gxs/rsgxsnetservice.h b/libretroshare/src/gxs/rsgxsnetservice.h index 39fd2982e..6b3e39d30 100644 --- a/libretroshare/src/gxs/rsgxsnetservice.h +++ b/libretroshare/src/gxs/rsgxsnetservice.h @@ -140,9 +140,9 @@ public: virtual void receiveTurtleSearchResults(TurtleRequestId req,const std::list& group_infos); virtual void receiveTurtleSearchResults(TurtleRequestId req,const unsigned char *encrypted_group_data,uint32_t encrypted_group_data_len); - virtual bool retrieveDistantSearchResults(TurtleRequestId req, std::map &group_infos); + virtual bool retrieveDistantSearchResults(TurtleRequestId req, std::map &group_infos); virtual bool clearDistantSearchResults(const TurtleRequestId& id); - virtual bool retrieveDistantGroupSummary(const RsGxsGroupId&,RsGxsGroupSummary&); + virtual bool retrieveDistantGroupSummary(const RsGxsGroupId&, RsGxsGroupSearchResults &); /*! * pauses synchronisation of subscribed groups and request for group id @@ -395,7 +395,7 @@ private: * @return false, if you cannot send to this peer, true otherwise */ bool canSendGrpId(const RsPeerId& sslId, const RsGxsGrpMetaData& grpMeta, std::vector& toVet, bool &should_encrypt); - bool canSendMsgIds(std::vector& msgMetas, const RsGxsGrpMetaData&, const RsPeerId& sslId, RsGxsCircleId &should_encrypt_id); + bool canSendMsgIds(std::vector& msgMetas, const RsGxsGrpMetaData&, const RsPeerId& sslId, RsGxsCircleId &should_encrypt_id); /*! * \brief checkPermissionsForFriendGroup @@ -439,6 +439,7 @@ private: bool locked_CanReceiveUpdate(const RsNxsSyncGrpReqItem *item); bool locked_CanReceiveUpdate(RsNxsSyncMsgReqItem *item, bool &grp_is_known); void locked_resetClientTS(const RsGxsGroupId& grpId); + bool locked_checkResendingOfUpdates(const RsPeerId& pid, const RsGxsGroupId &grpId, rstime_t incoming_ts, RsPeerUpdateTsRecord& rec); static RsGxsGroupId hashGrpId(const RsGxsGroupId& gid,const RsPeerId& pid) ; @@ -609,7 +610,7 @@ private: std::set mNewPublishKeysToNotify ; // Distant search result map - std::map > mDistantSearchResults ; + std::map > mDistantSearchResults ; void debugDump(); diff --git a/libretroshare/src/gxs/rsgxsnettunnel.cc b/libretroshare/src/gxs/rsgxsnettunnel.cc index a58d20f5f..968ab0b6f 100644 --- a/libretroshare/src/gxs/rsgxsnettunnel.cc +++ b/libretroshare/src/gxs/rsgxsnettunnel.cc @@ -167,7 +167,12 @@ public: class RsGxsNetTunnelTurtleSearchGroupDataItem: public RsGxsNetTunnelItem { public: - explicit RsGxsNetTunnelTurtleSearchGroupDataItem(): RsGxsNetTunnelItem(RS_PKT_SUBTYPE_GXS_NET_TUNNEL_TURTLE_SEARCH_GROUP_DATA) {} + explicit RsGxsNetTunnelTurtleSearchGroupDataItem() + : RsGxsNetTunnelItem(RS_PKT_SUBTYPE_GXS_NET_TUNNEL_TURTLE_SEARCH_GROUP_DATA), + encrypted_group_data(NULL), + encrypted_group_data_len(0) + {} + virtual ~RsGxsNetTunnelTurtleSearchGroupDataItem() {} uint16_t service ; @@ -1090,8 +1095,10 @@ void RsGxsNetTunnelService::receiveSearchResult(TurtleSearchRequestId request_id { GXS_NET_TUNNEL_DEBUG() << " : result is of type group summary result for service " << result_gs->service << std::dec << ": " << std::endl; +#ifdef DEBUG_RSGXSNETTUNNEL for(auto it(result_gs->group_infos.begin());it!=result_gs->group_infos.end();++it) std::cerr << " group " << (*it).mGroupId << ": " << (*it).mGroupName << ", " << (*it).mNumberOfMessages << " messages, last is " << time(NULL)-(*it).mLastMessageTs << " secs ago." << std::endl; +#endif auto it = mSearchableServices.find(result_gs->service) ; diff --git a/libretroshare/src/gxs/rsgxsnotify.h b/libretroshare/src/gxs/rsgxsnotify.h new file mode 100644 index 000000000..a6fd4c6fb --- /dev/null +++ b/libretroshare/src/gxs/rsgxsnotify.h @@ -0,0 +1,97 @@ +/******************************************************************************* + * libretroshare/src/gxs/: rsgxsnotify.h * + * * + * libretroshare: retroshare core library * + * * + * Copyright (C) 2015 Retroshare Team * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#pragma once + +/*! + * The aim of this class is to implement notifications internally to GXS, which are + * mostly used by RsGenExchange to send information to specific services. These services + * then interpret these changes and turn them into human-readable/processed service-specific changes. + */ + +#include "retroshare/rsids.h" + +class RsGxsNotify +{ +public: + RsGxsNotify(const RsGxsGroupId& gid): mGroupId(gid){} + virtual ~RsGxsNotify()=default; + + enum NotifyType + { + TYPE_UNKNOWN = 0x00, + TYPE_PUBLISHED = 0x01, + TYPE_RECEIVED_NEW = 0x02, + TYPE_PROCESSED = 0x03, + TYPE_RECEIVED_PUBLISHKEY = 0x04, + TYPE_RECEIVED_DISTANT_SEARCH_RESULTS = 0x05, + TYPE_STATISTICS_CHANGED = 0x06, + TYPE_UPDATED = 0x07, + TYPE_MESSAGE_DELETED = 0x08, + TYPE_GROUP_DELETED = 0x09, + }; + + virtual NotifyType getType() = 0; + + RsGxsGroupId mGroupId; // Group id of the group we're talking about. When the group is deleted, it's useful to know which group + // that was although there is no pointers to the actual group data anymore. +}; + +/*! + * Relevant to group changes + */ +class RsGxsGroupChange : public RsGxsNotify +{ +public: + RsGxsGroupChange(NotifyType type, const RsGxsGroupId& gid,bool metaChange) : RsGxsNotify(gid),mNewGroupItem(nullptr),mOldGroupItem(nullptr), mNotifyType(type), mMetaChange(metaChange) {} + virtual ~RsGxsGroupChange() override { delete mOldGroupItem; delete mNewGroupItem ; } + + NotifyType getType() override { return mNotifyType;} + bool metaChange() { return mMetaChange; } + + RsGxsGrpItem *mNewGroupItem; // Valid when a group has changed, or a new group is received. + RsGxsGrpItem *mOldGroupItem; // only valid when mNotifyType is TYPE_UPDATED + +protected: + NotifyType mNotifyType; + bool mMetaChange; +}; + +/*! + * Relevant to message changes + */ +class RsGxsMsgChange : public RsGxsNotify +{ +public: + RsGxsMsgChange(NotifyType type, const RsGxsGroupId& gid, const RsGxsMessageId& msg_id,bool metaChange) + : RsGxsNotify(gid), mMsgId(msg_id), mNewMsgItem(nullptr),NOTIFY_TYPE(type), mMetaChange(metaChange) {} + + RsGxsMessageId mMsgId; + RsGxsMsgItem *mNewMsgItem; + + NotifyType getType(){ return NOTIFY_TYPE;} + bool metaChange() { return mMetaChange; } +private: + const NotifyType NOTIFY_TYPE; + bool mMetaChange; +}; + diff --git a/libretroshare/src/gxs/rsgxsrequesttypes.cc b/libretroshare/src/gxs/rsgxsrequesttypes.cc index d57b4d353..6c3925974 100644 --- a/libretroshare/src/gxs/rsgxsrequesttypes.cc +++ b/libretroshare/src/gxs/rsgxsrequesttypes.cc @@ -23,6 +23,128 @@ #include "rsgxsrequesttypes.h" #include "util/rsstd.h" +std::ostream& operator<<(std::ostream& o,const GxsRequest& g) +{ + return g.print(o); +} + + +std::ostream& GroupMetaReq::print(std::ostream& o) const +{ + o << "[Request type=GroupMeta groupIds (size=" << mGroupIds.size() << "): " ; + + if(!mGroupIds.empty()) + { + o << *mGroupIds.begin() ; + + if(mGroupIds.size() > 1) + o << " ..." ; + } + + o << "]" ; + + return o; +} +std::ostream& GroupIdReq::print(std::ostream& o) const +{ + return o << "[Request type=GroupIdReq" << "]" ; +} + +std::ostream& GroupSerializedDataReq::print(std::ostream& o) const +{ + return o << "[Request type=GroupSerializedData" << "]" ; +} + +std::ostream& GroupDataReq::print(std::ostream& o) const +{ + o << "[Request type=GroupDataReq groupIds (size=" << mGroupIds.size() << "): " ; + + if(!mGroupIds.empty()) + { + o << *mGroupIds.begin() ; + + if(mGroupIds.size() > 1) + o << " ..." ; + } + + o << "]" ; + + return o; +} + +std::ostream& MsgIdReq::print(std::ostream& o) const +{ + return o << "[Request type=MsgId" << "]" ; +} + +std::ostream& MsgMetaReq::print(std::ostream& o) const +{ + o << "[Request type=MsgMetaReq groups (size=" << mMsgIds.size() << "): " ; + + if(!mMsgIds.empty()) + { + o << mMsgIds.begin()->first << " (" << mMsgIds.begin()->second.size() << " messages)"; + + if(mMsgIds.size() > 1) + o << " ..." ; + } + + o << "]" ; + + return o; +} + +std::ostream& MsgDataReq::print(std::ostream& o) const +{ + o << "[Request type=MsgDataReq groups (size=" << mMsgIds.size() << "): " ; + + if(!mMsgIds.empty()) + { + o << mMsgIds.begin()->first << " (" << mMsgIds.begin()->second.size() << " messages)"; + + if(mMsgIds.size() > 1) + o << " ..." ; + } + + o << "]" ; + + return o; +} + +std::ostream& MsgRelatedInfoReq::print(std::ostream& o) const +{ + o << "[Request type=MsgRelatedInfo msgIds (size=" << mMsgIds.size() << "): " ; + + if(!mMsgIds.empty()) + { + o << mMsgIds.begin()->first ; + + if(mMsgIds.size() > 1) + o << " ..." ; + } + + o << "]" ; + + return o; +} + +std::ostream& GroupSetFlagReq::print(std::ostream& o) const +{ + return o << "[Request type=GroupFlagSet grpId=" << grpId << "]" ; +} + + + +std::ostream& ServiceStatisticRequest::print(std::ostream& o) const +{ + return o << "[Request type=ServiceStatistics" << "]" ; +} + +std::ostream& GroupStatisticRequest::print(std::ostream& o) const +{ + return o << "[Request type=GroupStatistics grpId=" << mGrpId << "]" ; +} + GroupMetaReq::~GroupMetaReq() { //rsstd::delete_all(mGroupMetaData.begin(), mGroupMetaData.end()); // now memory ownership is kept by the cache. @@ -57,3 +179,8 @@ MsgRelatedInfoReq::~MsgRelatedInfoReq() rsstd::delete_all(dataIt->second.begin(), dataIt->second.end()); } } +std::ostream& MessageSetFlagReq::print(std::ostream& o) const +{ + return o << "[Request type=MsgFlagSet" << "]" ; +} + diff --git a/libretroshare/src/gxs/rsgxsrequesttypes.h b/libretroshare/src/gxs/rsgxsrequesttypes.h index 7af50b135..e738a1923 100644 --- a/libretroshare/src/gxs/rsgxsrequesttypes.h +++ b/libretroshare/src/gxs/rsgxsrequesttypes.h @@ -29,25 +29,30 @@ struct GxsRequest { GxsRequest() : - token(0), reqTime(0), ansType(0), reqType(0), + token(0), reqTime(0), clientAnswerType(0), reqType(0), status(RsTokenService::FAILED) {} virtual ~GxsRequest() {} uint32_t token; uint32_t reqTime; - RS_DEPRECATED uint32_t ansType; /// G10h4ck: This is of no use + uint32_t clientAnswerType; /// This is made available to the clients in order to keep track of why specific requests where sent.. uint32_t reqType; RsTokReqOptions Options; RsTokenService::GxsRequestStatus status; + + virtual std::ostream& print(std::ostream& o) const = 0; }; +std::ostream& operator<<(std::ostream& o,const GxsRequest& g); + class GroupMetaReq : public GxsRequest { public: virtual ~GroupMetaReq(); + virtual std::ostream& print(std::ostream& o) const override; public: std::list mGroupIds; std::list mGroupMetaData; @@ -56,12 +61,16 @@ public: class GroupIdReq : public GxsRequest { public: + virtual std::ostream& print(std::ostream& o) const override ; + std::list mGroupIds; std::list mGroupIdResult; }; class GroupSerializedDataReq : public GxsRequest { public: + virtual std::ostream& print(std::ostream& o) const override ; + std::list mGroupIds; std::list mGroupData; }; @@ -71,6 +80,7 @@ class GroupDataReq : public GxsRequest public: virtual ~GroupDataReq(); + virtual std::ostream& print(std::ostream& o) const override; public: std::list mGroupIds; std::list mGroupData; @@ -79,6 +89,8 @@ public: class MsgIdReq : public GxsRequest { public: + virtual std::ostream& print(std::ostream& o) const override ; + GxsMsgReq mMsgIds; GxsMsgIdResult mMsgIdResult; }; @@ -88,6 +100,8 @@ class MsgMetaReq : public GxsRequest public: virtual ~MsgMetaReq(); + virtual std::ostream& print(std::ostream& o) const override; + public: GxsMsgReq mMsgIds; GxsMsgMetaResult mMsgMetaData; @@ -98,6 +112,7 @@ class MsgDataReq : public GxsRequest public: virtual ~MsgDataReq(); + virtual std::ostream& print(std::ostream& o) const override; public: GxsMsgReq mMsgIds; NxsMsgDataResult mMsgData; @@ -106,12 +121,15 @@ public: class ServiceStatisticRequest: public GxsRequest { public: + virtual std::ostream& print(std::ostream& o) const override ; GxsServiceStatistic mServiceStatistic; }; struct GroupStatisticRequest: public GxsRequest { public: + virtual std::ostream& print(std::ostream& o) const override ; + RsGxsGroupId mGrpId; GxsGroupStatistic mGroupStatistic; }; @@ -121,6 +139,7 @@ class MsgRelatedInfoReq : public GxsRequest public: virtual ~MsgRelatedInfoReq(); + std::ostream& print(std::ostream& o) const override; public: std::vector mMsgIds; MsgRelatedIdResult mMsgIdResult; @@ -131,6 +150,8 @@ public: class GroupSetFlagReq : public GxsRequest { public: + virtual std::ostream& print(std::ostream& o) const override ; + const static uint32_t FLAG_SUBSCRIBE; const static uint32_t FLAG_STATUS; @@ -145,6 +166,7 @@ class MessageSetFlagReq : public GxsRequest public: const static uint32_t FLAG_STATUS; + virtual std::ostream& print(std::ostream& o) const override ; uint8_t type; uint32_t flag; uint32_t flagMask; diff --git a/libretroshare/src/gxs/rsgxsutil.cc b/libretroshare/src/gxs/rsgxsutil.cc index 2396618a8..491c6e3c2 100644 --- a/libretroshare/src/gxs/rsgxsutil.cc +++ b/libretroshare/src/gxs/rsgxsutil.cc @@ -85,7 +85,7 @@ bool RsGxsMessageCleanUp::clean() for(; mit != result.end(); ++mit) { - std::vector& metaV = mit->second; + std::vector& metaV = mit->second; // First, make a map of which message have a child message. This allows to only delete messages that dont have child messages. // A more accurate way to go would be to compute the time of the oldest message and possibly delete all the branch, but in the @@ -99,7 +99,7 @@ bool RsGxsMessageCleanUp::clean() for( uint32_t i=0;imMsgId)!=messages_with_kids.end()); @@ -107,7 +107,7 @@ bool RsGxsMessageCleanUp::clean() bool remove = store_period > 0 && ((meta->mPublishTs + store_period) < now) && !have_kids; // check client does not want the message kept regardless of age - remove &= !(meta->mMsgStatus & GXS_SERV::GXS_MSG_STATUS_KEEP); + remove &= !(meta->mMsgStatus & GXS_SERV::GXS_MSG_STATUS_KEEP_FOREVER); // if not subscribed remove messages (can optimise this really) remove = remove || (grpMeta->mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_NOT_SUBSCRIBED); @@ -132,7 +132,7 @@ bool RsGxsMessageCleanUp::clean() std::cerr << std::endl; #endif - delete meta; + //delete meta; } } @@ -215,7 +215,8 @@ bool RsGxsIntegrityCheck::check() rsReputations->overallReputationLevel( grp->metaData->mAuthorId ) > RsReputationLevel::LOCALLY_NEGATIVE ) - used_gxs_ids.insert(std::make_pair(grp->metaData->mAuthorId, RsIdentityUsage(mGenExchangeClient->serviceType(), RsIdentityUsage::GROUP_AUTHOR_KEEP_ALIVE,grp->grpId))); + used_gxs_ids.insert(std::make_pair(grp->metaData->mAuthorId, RsIdentityUsage(RsServiceType(mGenExchangeClient->serviceType()), + RsIdentityUsage::GROUP_AUTHOR_KEEP_ALIVE,grp->grpId))); } } } @@ -404,7 +405,12 @@ bool RsGxsIntegrityCheck::check() rsReputations->overallReputationLevel( msg->metaData->mAuthorId ) > RsReputationLevel::LOCALLY_NEGATIVE ) - used_gxs_ids.insert(std::make_pair(msg->metaData->mAuthorId,RsIdentityUsage(mGenExchangeClient->serviceType(),RsIdentityUsage::MESSAGE_AUTHOR_KEEP_ALIVE,msg->metaData->mGroupId,msg->metaData->mMsgId))) ; + used_gxs_ids.insert(std::make_pair(msg->metaData->mAuthorId,RsIdentityUsage(RsServiceType(mGenExchangeClient->serviceType()), + RsIdentityUsage::MESSAGE_AUTHOR_KEEP_ALIVE, + msg->metaData->mGroupId, + msg->metaData->mMsgId, + msg->metaData->mParentId, + msg->metaData->mThreadId))) ; } } diff --git a/libretroshare/src/gxs/rsgxsutil.h b/libretroshare/src/gxs/rsgxsutil.h index f8782d861..50e749f3a 100644 --- a/libretroshare/src/gxs/rsgxsutil.h +++ b/libretroshare/src/gxs/rsgxsutil.h @@ -111,27 +111,6 @@ typedef t_RsGxsGenericDataTemporaryMapVector RsNxsMsgDa typedef t_RsGxsGenericDataTemporaryList RsNxsGrpDataTemporaryList ; typedef t_RsGxsGenericDataTemporaryList RsNxsMsgDataTemporaryList ; -#ifdef UNUSED -template -class RsGxsMetaDataTemporaryMapVector: public std::vector -{ -public: - virtual ~RsGxsMetaDataTemporaryMapVector() - { - clear() ; - } - - virtual void clear() - { - for(typename RsGxsMetaDataTemporaryMapVector::iterator it = this->begin();it!=this->end();++it) - if(it->second != NULL) - delete it->second ; - std::vector::clear() ; - } -}; -#endif - - inline RsGxsGrpMsgIdPair getMsgIdPair(RsNxsMsg& msg) { return RsGxsGrpMsgIdPair(std::make_pair(msg.grpId, msg.msgId)); @@ -146,7 +125,7 @@ inline RsGxsGrpMsgIdPair getMsgIdPair(RsGxsMsgItem& msg) * Does message clean up based on individual group expirations first * if avialable. If not then deletion s */ -class RsGxsMessageCleanUp //: public RsThread +class RsGxsMessageCleanUp { public: @@ -166,11 +145,6 @@ public: */ bool clean(); - /*! - * TODO: Rather than manual progressions consider running through a thread - */ - //virtual void data_tick(){} - private: RsGeneralDataService* const mDs; diff --git a/libretroshare/src/gxs/rsnxs.h b/libretroshare/src/gxs/rsnxs.h index 2752fa6bf..7276f3421 100644 --- a/libretroshare/src/gxs/rsnxs.h +++ b/libretroshare/src/gxs/rsnxs.h @@ -128,7 +128,7 @@ public: * \return * false when the request is unknown. */ - virtual bool retrieveDistantSearchResults(TurtleRequestId req, std::map &group_infos)=0; + virtual bool retrieveDistantSearchResults(TurtleRequestId req, std::map &group_infos)=0; /*! * \brief getDistantSearchResults * \param id @@ -136,7 +136,7 @@ public: * \return */ virtual bool clearDistantSearchResults(const TurtleRequestId& id)=0; - virtual bool retrieveDistantGroupSummary(const RsGxsGroupId&,RsGxsGroupSummary&)=0; + virtual bool retrieveDistantGroupSummary(const RsGxsGroupId&,RsGxsGroupSearchResults&)=0; virtual bool search(const std::string& substring,std::list& group_infos) =0; virtual bool search(const Sha1CheckSum& hashed_group_id,unsigned char *& encrypted_group_data,uint32_t& encrypted_group_data_len)=0; diff --git a/libretroshare/src/gxstrans/p3gxstrans.cc b/libretroshare/src/gxstrans/p3gxstrans.cc index 44ecccbd1..391a38e50 100644 --- a/libretroshare/src/gxstrans/p3gxstrans.cc +++ b/libretroshare/src/gxstrans/p3gxstrans.cc @@ -43,7 +43,7 @@ p3GxsTrans::~p3GxsTrans() } } -bool p3GxsTrans::getStatistics(GxsTransStatistics& stats) +bool p3GxsTrans::getDataStatistics(GxsTransStatistics& stats) { { RS_STACK_MUTEX(mDataMutex); @@ -656,6 +656,9 @@ void p3GxsTrans::notifyChanges(std::vector& changes) #ifdef DEBUG_GXSTRANS std::cout << "p3GxsTrans::notifyChanges(...)" << std::endl; #endif + std::list grps_to_request; + GxsMsgReq msgs_to_request; + for( auto it = changes.begin(); it != changes.end(); ++it ) { RsGxsGroupChange* grpChange = dynamic_cast(*it); @@ -666,18 +669,15 @@ void p3GxsTrans::notifyChanges(std::vector& changes) #ifdef DEBUG_GXSTRANS std::cout << "p3GxsTrans::notifyChanges(...) grpChange" << std::endl; #endif - requestGroupsData(&(grpChange->mGrpIdList)); + grps_to_request.push_back(grpChange->mGroupId); } else if(msgChange) { #ifdef DEBUG_GXSTRANS std::cout << "p3GxsTrans::notifyChanges(...) msgChange" << std::endl; #endif - uint32_t token; - RsTokReqOptions opts; opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; - RsGenExchange::getTokenService()->requestMsgInfo( token, 0xcaca, - opts, msgChange->msgChangeMap ); - GxsTokenQueue::queueRequest(token, MAILS_UPDATE); + + msgs_to_request[msgChange->mGroupId].insert(msgChange->mMsgId); #ifdef DEBUG_GXSTRANS for( GxsMsgReq::const_iterator it = msgChange->msgChangeMap.begin(); @@ -698,6 +698,20 @@ void p3GxsTrans::notifyChanges(std::vector& changes) } delete *it; } + + if(!msgs_to_request.empty()) + { + uint32_t token; + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; + RsGenExchange::getTokenService()->requestMsgInfo( token, 0xcaca, opts, msgs_to_request); + + GxsTokenQueue::queueRequest(token, MAILS_UPDATE); + } + + + if(!grps_to_request.empty()) + requestGroupsData(&grps_to_request); } uint32_t p3GxsTrans::AuthenPolicy() @@ -1335,4 +1349,53 @@ bool p3GxsTrans::acceptNewMessage(const RsGxsMsgMetaData *msgMeta,uint32_t msg_s } +bool p3GxsTrans::getGroupStatistics(std::map& stats) +{ + uint32_t token1; + + RsTokReqOptions opts1; + opts1.mReqType = GXS_REQUEST_TYPE_GROUP_META; + if( !requestGroupInfo(token1, opts1) || waitToken(token1) != RsTokenService::COMPLETE ) + return false; + + std::list group_metas; + getGroupSummary(token1,group_metas); + + for(auto& group_meta:group_metas) + { + RsGxsTransGroupStatistics& stat(stats[group_meta.mGroupId]); + + uint32_t token2; + if(!RsGxsIfaceHelper::requestGroupStatistic(token2,group_meta.mGroupId) || waitToken(token2) != RsTokenService::COMPLETE) + continue; + + RsGenExchange::getGroupStatistic(token2,stat); + + stat.popularity = group_meta.mPop ; + stat.subscribed = IS_GROUP_SUBSCRIBED(group_meta.mSubscribeFlags) ; + stat.mGrpId = group_meta.mGroupId ; + + std::vector metas; + + uint32_t token3; + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_MSG_META; + + std::list groupIds; + groupIds.push_back(group_meta.mGroupId); + + if( !requestMsgInfo(token3, opts, groupIds) || waitToken(token3, std::chrono::seconds(5)) != RsTokenService::COMPLETE ) + continue; + + GxsMsgMetaMap metaMap; + if(!RsGenExchange::getMsgMeta(token3, metaMap) || metaMap.size() != 1) + continue; + + for(auto& meta: metaMap.begin()->second) + stat.addMessageMeta(group_meta.mGroupId,meta) ; + } + + return true; +} + diff --git a/libretroshare/src/gxstrans/p3gxstrans.h b/libretroshare/src/gxstrans/p3gxstrans.h index fd2b92ee8..307492281 100644 --- a/libretroshare/src/gxstrans/p3gxstrans.h +++ b/libretroshare/src/gxstrans/p3gxstrans.h @@ -113,13 +113,21 @@ public: /*! * \brief getStatistics - * Gathers all sorts of statistics about the internals of p3GxsTrans, in order to display info about the running status, - * message transport, etc. + * Gathers all sorts of statistics about the data transported by p3GxsTrans, in order to display info about the running status, + * message transport, etc. This is a blocking call. Use it in a thread. * \param stats This structure contains all statistics information. * \return true is the call succeeds. */ - virtual bool getStatistics(GxsTransStatistics& stats); + virtual bool getDataStatistics(GxsTransStatistics& stats) override; + + /*! + * \brief getGroupStatistics + * Gathers statistics about GXS groups and messages used by GxsTrans to transport data. This is a blocking call. Use it in a thread. + * \param stats + * \return true if the data collection succeeds. + */ + virtual bool getGroupStatistics(std::map& stats) override; /** * Send an email to recipient, in the process author of the email is diff --git a/libretroshare/src/gxstrans/p3gxstransitems.h b/libretroshare/src/gxstrans/p3gxstransitems.h index cc7322844..f2cab3cfe 100644 --- a/libretroshare/src/gxstrans/p3gxstransitems.h +++ b/libretroshare/src/gxstrans/p3gxstransitems.h @@ -181,8 +181,6 @@ public: {} void clear() {} - std::ostream &print(std::ostream &out, uint16_t /*indent = 0*/) - { return out; } }; class RsGxsTransSerializer; diff --git a/libretroshare/src/gxstunnel/p3gxstunnel.cc b/libretroshare/src/gxstunnel/p3gxstunnel.cc index 08ca943a6..2909238ea 100644 --- a/libretroshare/src/gxstunnel/p3gxstunnel.cc +++ b/libretroshare/src/gxstunnel/p3gxstunnel.cc @@ -113,8 +113,6 @@ int p3GxsTunnelService::tick() flush() ; - rstime::rs_usleep(1000*500); - return 0 ; } @@ -978,7 +976,7 @@ void p3GxsTunnelService::handleRecvDHPublicKey(RsGxsTunnelDHPublicKeyItem *item) std::cerr << "(SS) Signature was verified and it doesn't check! This is a security issue!" << std::endl; return ; } - mGixs->timeStampKey(item->signature.keyId,RsIdentityUsage(RS_SERVICE_TYPE_GXS_TUNNEL,RsIdentityUsage::GXS_TUNNEL_DH_SIGNATURE_CHECK)); + mGixs->timeStampKey(item->signature.keyId,RsIdentityUsage(RsServiceType::GXS_TUNNEL,RsIdentityUsage::GXS_TUNNEL_DH_SIGNATURE_CHECK)); #ifdef DEBUG_GXS_TUNNEL std::cerr << " Signature checks! Sender's ID = " << senders_id << std::endl; diff --git a/libretroshare/src/jsonapi/jsonapi.cpp b/libretroshare/src/jsonapi/jsonapi.cpp index 0c180ce52..3dcde2a27 100644 --- a/libretroshare/src/jsonapi/jsonapi.cpp +++ b/libretroshare/src/jsonapi/jsonapi.cpp @@ -78,10 +78,10 @@ JsonApiServer::corsOptionsHeaders = #define INITIALIZE_API_CALL_JSON_CONTEXT \ RsGenericSerializer::SerializeContext cReq( \ nullptr, 0, \ - RsGenericSerializer::SERIALIZATION_FLAG_YIELDING ); \ + RsSerializationFlags::YIELDING ); \ RsJson& jReq(cReq.mJson); \ if(session->get_request()->get_method() == "GET") \ - { \ + { \ const std::string jrqp(session->get_request()->get_query_parameter("jsonData")); \ jReq.Parse(jrqp.c_str(), jrqp.size()); \ } \ @@ -165,6 +165,7 @@ JsonApiServer::JsonApiServer(): configMutex("JsonApiServer config"), RsThread::setStopTimeout(10); #endif +#if !RS_VERSION_AT_LEAST(0,6,6) registerHandler("/rsLoginHelper/createLocation", [this](const std::shared_ptr session) { @@ -180,6 +181,7 @@ JsonApiServer::JsonApiServer(): configMutex("JsonApiServer config"), std::string errorMessage; bool makeHidden = false; bool makeAutoTor = false; + std::string createToken; // deserialize input parameters from JSON { @@ -189,6 +191,7 @@ JsonApiServer::JsonApiServer(): configMutex("JsonApiServer config"), RS_SERIAL_PROCESS(password); RS_SERIAL_PROCESS(makeHidden); RS_SERIAL_PROCESS(makeAutoTor); + RS_SERIAL_PROCESS(createToken); } // call retroshare C++ API @@ -196,8 +199,9 @@ JsonApiServer::JsonApiServer(): configMutex("JsonApiServer config"), location, password, errorMessage, makeHidden, makeAutoTor ); - if(retval) - authorizeUser(location.mLocationId.toStdString(),password); + std::string tokenUser, tokenPw; + if(retval && parseToken(createToken, tokenUser, tokenPw)) + authorizeUser(tokenUser,tokenPw); // serialize out parameters and return value to JSON { @@ -212,8 +216,9 @@ JsonApiServer::JsonApiServer(): configMutex("JsonApiServer config"), DEFAULT_API_CALL_JSON_RETURN(rb::OK); } ); }, false); +#endif // !RS_VERSION_AT_LEAST(0,6,6) - registerHandler("/rsLoginHelper/attemptLogin", + registerHandler("/rsLoginHelper/createLocationV2", [this](const std::shared_ptr session) { auto reqSize = session->get_request()->get_header("Content-Length", 0); @@ -223,28 +228,51 @@ JsonApiServer::JsonApiServer(): configMutex("JsonApiServer config"), { INITIALIZE_API_CALL_JSON_CONTEXT; - RsPeerId account; + RsPeerId locationId; + RsPgpId pgpId; + std::string locationName; + std::string pgpName; std::string password; + // JSON API only + std::string apiUser; + std::string apiPass; + // deserialize input parameters from JSON { RsGenericSerializer::SerializeContext& ctx(cReq); RsGenericSerializer::SerializeJob j(RsGenericSerializer::FROM_JSON); - RS_SERIAL_PROCESS(account); + RS_SERIAL_PROCESS(locationId); + RS_SERIAL_PROCESS(pgpId); + RS_SERIAL_PROCESS(locationName); + RS_SERIAL_PROCESS(pgpName); RS_SERIAL_PROCESS(password); + + // JSON API only + RS_SERIAL_PROCESS(apiUser); + RS_SERIAL_PROCESS(apiPass); } - // call retroshare C++ API - RsInit::LoadCertificateStatus retval = - rsLoginHelper->attemptLogin(account, password); + std::error_condition retval; - if( retval == RsInit::OK ) - authorizeUser(account.toStdString(), password); + if(apiUser.empty()) + retval = RsJsonApiErrorNum::TOKEN_FORMAT_INVALID; + + if(!retval) + retval = badApiCredientalsFormat(apiUser, apiPass); + + if(!retval) // call retroshare C++ API + retval = rsLoginHelper->createLocationV2( + locationId, pgpId, locationName, pgpName, password ); + + if(!retval) retval = authorizeUser(apiUser, apiPass); // serialize out parameters and return value to JSON { RsGenericSerializer::SerializeContext& ctx(cAns); RsGenericSerializer::SerializeJob j(RsGenericSerializer::TO_JSON); + RS_SERIAL_PROCESS(locationId); + RS_SERIAL_PROCESS(pgpId); RS_SERIAL_PROCESS(retval); } @@ -352,7 +380,7 @@ JsonApiServer::JsonApiServer(): configMutex("JsonApiServer config"), rsEvents, "rsEvents", cAns, session ) ) return; - RsEventType eventType = RsEventType::NONE; + RsEventType eventType = RsEventType::__NONE; // deserialize input parameters from JSON { @@ -395,7 +423,8 @@ JsonApiServer::JsonApiServer(): configMutex("JsonApiServer config"), } ); }; - bool retval = rsEvents->registerEventsHandler(eventType,multiCallback, hId); + std::error_condition retval = rsEvents->registerEventsHandler( + multiCallback, hId, eventType ); { RsGenericSerializer::SerializeContext& ctx(cAns); @@ -465,16 +494,19 @@ void JsonApiServer::registerHandler( const std::shared_ptr session, const std::function)>& callback ) { + /* Declare outside the lambda to avoid returning a dangling + * reference */ + RsWarn tWarn; const auto authFail = - [&path, &session](int status) -> RsWarn::stream_type& + [&](int status) -> std::ostream& { /* Capture session by reference as it is cheaper then copying * shared_ptr by value which is not needed in this case */ session->close(status, corsOptionsHeaders); - return RsWarn() << "JsonApiServer authentication handler " - "blocked an attempt to call JSON API " - "authenticated method: " << path; + return tWarn << "JsonApiServer authentication handler " + "blocked an attempt to call JSON API " + "authenticated method: " << path; }; if(session->get_request()->get_method() == "OPTIONS") diff --git a/libretroshare/src/jsonapi/jsonapi.h b/libretroshare/src/jsonapi/jsonapi.h index 70cc5e309..317d4066f 100644 --- a/libretroshare/src/jsonapi/jsonapi.h +++ b/libretroshare/src/jsonapi/jsonapi.h @@ -151,8 +151,6 @@ protected: /// @see RsThread void onStopRequested() override; - static const RsJsonApiErrorCategory sErrorCategory; - static std::error_condition badApiCredientalsFormat( const std::string& user, const std::string& passwd ); diff --git a/libretroshare/src/libretroshare.pro b/libretroshare/src/libretroshare.pro index cc4e1dc11..2606e4083 100644 --- a/libretroshare/src/libretroshare.pro +++ b/libretroshare/src/libretroshare.pro @@ -12,14 +12,15 @@ DESTDIR = lib !include("use_libretroshare.pri"):error("Including") -# the dht stunner is used to obtain RS external ip addr. when it is natted -# this system is unreliable and rs supports a newer and better one (asking connected peers) -# CONFIG += useDhtStunner - # treat warnings as error for better removing #QMAKE_CFLAGS += -Werror #QMAKE_CXXFLAGS += -Werror +## Uncomment to enable Unfinished Services. +#CONFIG += wikipoos +#CONFIG += gxsthewire +#CONFIG += gxsphotoshare + debug { # DEFINES *= DEBUG # DEFINES *= OPENDHT_DEBUG DHT_DEBUG CONN_DEBUG DEBUG_UDP_SORTER P3DISC_DEBUG DEBUG_UDP_LAYER FT_DEBUG EXTADDRSEARCH_DEBUG @@ -44,7 +45,6 @@ file_lists { file_sharing/directory_updater.h \ file_sharing/rsfilelistitems.h \ file_sharing/dir_hierarchy.h \ - file_sharing/file_tree.h \ file_sharing/file_sharing_defaults.h SOURCES *= file_sharing/p3filelists.cc \ @@ -154,6 +154,7 @@ rs_webui { HEADERS += plugins/pluginmanager.h \ plugins/dlfcn_win32.h \ rsitems/rspluginitems.h \ + util/i2pcommon.h \ util/rsinitedptr.h HEADERS += $$PUBLIC_HEADERS @@ -237,7 +238,7 @@ win32-x-g++ { } ################################# Windows ########################################## -win32-g++ { +win32-g++|win32-clang-g++ { QMAKE_CC = $${QMAKE_CXX} OBJECTS_DIR = temp/obj MOC_DIR = temp/moc @@ -327,6 +328,8 @@ INCLUDEPATH *= $${OPENPGPSDK_DIR} PRE_TARGETDEPS *= $${OPENPGPSDK_DIR}/lib/libops.a LIBS *= $${OPENPGPSDK_DIR}/lib/libops.a -lbz2 +################################### HEADERS & SOURCES ############################# + HEADERS += ft/ftchunkmap.h \ ft/ftcontroller.h \ ft/ftdata.h \ @@ -468,7 +471,12 @@ HEADERS += turtle/p3turtle.h \ turtle/turtleclientservice.h HEADERS += util/folderiterator.h \ - util/rsdebug.h \ + util/rsdebug.h \ + util/rsdebuglevel0.h \ + util/rsdebuglevel1.h \ + util/rsdebuglevel2.h \ + util/rsdebuglevel3.h \ + util/rsdebuglevel4.h \ util/rskbdinput.h \ util/rsmemory.h \ util/smallobject.h \ @@ -480,6 +488,8 @@ HEADERS += util/folderiterator.h \ util/dnsresolver.h \ util/radix32.h \ util/radix64.h \ + util/rsbase64.h \ + util/rsendian.h \ util/rsinitedptr.h \ util/rsprint.h \ util/rsstring.h \ @@ -493,7 +503,8 @@ HEADERS += util/folderiterator.h \ util/rstime.h \ util/stacktrace.h \ util/rsdeprecate.h \ - util/cxx11retrocompat.h \ + util/cxx11retrocompat.h \ + util/cxx14retrocompat.h \ util/cxx17retrocompat.h \ util/rsurl.h \ util/rserrno.h @@ -507,7 +518,8 @@ SOURCES += ft/ftchunkmap.cc \ ft/ftfilesearch.cc \ ft/ftserver.cc \ ft/fttransfermodule.cc \ - ft/ftturtlefiletransferitem.cc + ft/ftturtlefiletransferitem.cc \ + util/i2pcommon.cpp SOURCES += crypto/chacha20.cpp \ crypto/hashstream.cc\ @@ -636,6 +648,7 @@ SOURCES += util/folderiterator.cc \ util/rsrecogn.cc \ util/rstime.cc \ util/rsurl.cc \ + util/rsbase64.cc \ util/rserrno.cc equals(RS_UPNP_LIB, miniupnpc) { @@ -668,6 +681,7 @@ HEADERS += rsitems/rsnxsitems.h \ util/rsdbbind.h \ util/contentvalue.h \ gxs/rsgxsutil.h \ + gxs/rsgxsnotify.h \ gxs/gxssecurity.h \ gxs/rsgds.h \ gxs/rsgxs.h \ @@ -763,6 +777,8 @@ SOURCES += services/p3gxschannels.cc \ rsitems/rsgxschannelitems.cc \ wikipoos { + DEFINES *= RS_USE_WIKI + # Wiki Service HEADERS += retroshare/rswiki.h \ services/p3wiki.h \ @@ -773,6 +789,8 @@ wikipoos { } gxsthewire { + DEFINES *= RS_USE_WIRE + # Wire Service HEADERS += retroshare/rswire.h \ services/p3wire.h \ @@ -793,6 +811,8 @@ SOURCES += services/p3postbase.cc \ rsitems/rsposteditems.cc gxsphotoshare { + DEFINES *= RS_USE_PHOTO + #Photo Service HEADERS += services/p3photoservice.h \ retroshare/rsphoto.h \ @@ -832,23 +852,41 @@ rs_jsonapi { no_rs_cross_compiling { DUMMYRESTBEDINPUT = FORCE CMAKE_GENERATOR_OVERRIDE="" - win32-g++:CMAKE_GENERATOR_OVERRIDE="-G \"MSYS Makefiles\"" + win32-g++|win32-clang-g++ { + isEmpty(QMAKE_SH) { + CMAKE_GENERATOR_OVERRIDE="-G \"MinGW Makefiles\"" + } else { + CMAKE_GENERATOR_OVERRIDE="-G \"MSYS Makefiles\"" + } + } genrestbedlib.name = Generating librestbed. genrestbedlib.input = DUMMYRESTBEDINPUT genrestbedlib.output = $$clean_path($${RESTBED_BUILD_PATH}/librestbed.a) genrestbedlib.CONFIG += target_predeps combine genrestbedlib.variable_out = PRE_TARGETDEPS - genrestbedlib.commands = \ - cd $${RS_SRC_PATH} && ( \ - git submodule update --init supportlibs/restbed ; \ - cd $${RESTBED_SRC_PATH} ; \ - git submodule update --init dependency/asio ; \ - git submodule update --init dependency/catch ; \ - git submodule update --init dependency/kashmir ; \ - true ) && \ - mkdir -p $${RESTBED_BUILD_PATH} && cd $${RESTBED_BUILD_PATH} && \ + win32-g++:isEmpty(QMAKE_SH) { + genrestbedlib.commands = \ + cd $$shell_path($${RS_SRC_PATH}) && git submodule update --init supportlibs/restbed || cd . $$escape_expand(\\n\\t) \ + cd $$shell_path($${RESTBED_SRC_PATH}) && git submodule update --init dependency/asio || cd . $$escape_expand(\\n\\t) \ + cd $$shell_path($${RESTBED_SRC_PATH}) && git submodule update --init dependency/catch || cd . $$escape_expand(\\n\\t )\ + cd $$shell_path($${RESTBED_SRC_PATH}) && git submodule update --init dependency/kashmir || cd . $$escape_expand(\\n\\t) \ + $(CHK_DIR_EXISTS) $$shell_path($$UDP_DISCOVERY_BUILD_PATH) $(MKDIR) $$shell_path($${UDP_DISCOVERY_BUILD_PATH}) $$escape_expand(\\n\\t) + } else { + genrestbedlib.commands = \ + cd $${RS_SRC_PATH} && ( \ + git submodule update --init supportlibs/restbed ; \ + cd $${RESTBED_SRC_PATH} ; \ + git submodule update --init dependency/asio ; \ + git submodule update --init dependency/catch ; \ + git submodule update --init dependency/kashmir ; \ + true ) && \ + mkdir -p $${RESTBED_BUILD_PATH} && + } + genrestbedlib.commands += \ + cd $$shell_path($${RESTBED_BUILD_PATH}) && \ cmake \ -DCMAKE_CXX_COMPILER=$$QMAKE_CXX \ + \"-DCMAKE_CXX_FLAGS=$${QMAKE_CXXFLAGS}\" \ $${CMAKE_GENERATOR_OVERRIDE} -DBUILD_SSL=OFF \ -DCMAKE_INSTALL_PREFIX=. -B. \ -H$$shell_path($${RESTBED_SRC_PATH}) && \ @@ -859,9 +897,9 @@ rs_jsonapi { genrestbedheader.name = Generating restbed header. genrestbedheader.input = genrestbedlib.output genrestbedheader.output = $${RESTBED_HEADER_FILE} - genrestbedheader.CONFIG += target_predeps combine no_link + genrestbedheader.CONFIG += target_predeps no_link genrestbedheader.variable_out = HEADERS - genrestbedheader.commands = cd $${RESTBED_BUILD_PATH} && $(MAKE) install + genrestbedheader.commands = cd $$shell_path($${RESTBED_BUILD_PATH}) && $(MAKE) install QMAKE_EXTRA_COMPILERS += genrestbedheader } @@ -877,13 +915,19 @@ rs_jsonapi { genjsonapi.clean = $${WRAPPERS_INCL_FILE} $${WRAPPERS_REG_FILE} genjsonapi.CONFIG += target_predeps combine no_link genjsonapi.variable_out = HEADERS - genjsonapi.commands = \ - mkdir -p $${JSONAPI_GENERATOR_OUT} && \ - cp $${DOXIGEN_CONFIG_SRC} $${DOXIGEN_CONFIG_OUT} && \ - echo OUTPUT_DIRECTORY=$${JSONAPI_GENERATOR_OUT} >> $${DOXIGEN_CONFIG_OUT} && \ - echo INPUT=$${DOXIGEN_INPUT_DIRECTORY} >> $${DOXIGEN_CONFIG_OUT} && \ - doxygen $${DOXIGEN_CONFIG_OUT} && \ - $${JSONAPI_GENERATOR_EXE} $${JSONAPI_GENERATOR_SRC} $${JSONAPI_GENERATOR_OUT}; + win32-g++:isEmpty(QMAKE_SH) { + genjsonapi.commands = \ + $(CHK_DIR_EXISTS) $$shell_path($$JSONAPI_GENERATOR_OUT) $(MKDIR) $$shell_path($${JSONAPI_GENERATOR_OUT}) $$escape_expand(\\n\\t) + } else { + genjsonapi.commands = \ + mkdir -p $${JSONAPI_GENERATOR_OUT} && \ + cp $${DOXIGEN_CONFIG_SRC} $${DOXIGEN_CONFIG_OUT} && \ + echo OUTPUT_DIRECTORY=$${JSONAPI_GENERATOR_OUT} >> $${DOXIGEN_CONFIG_OUT} && \ + echo INPUT=$${DOXIGEN_INPUT_DIRECTORY} >> $${DOXIGEN_CONFIG_OUT} && \ + doxygen $${DOXIGEN_CONFIG_OUT} && + } + genjsonapi.commands += \ + $${JSONAPI_GENERATOR_EXE} $${JSONAPI_GENERATOR_SRC} $${JSONAPI_GENERATOR_OUT} QMAKE_EXTRA_COMPILERS += genjsonapi # Force recalculation of libretroshare dependencies see https://stackoverflow.com/a/47884045 @@ -929,20 +973,34 @@ rs_broadcast_discovery { no_rs_cross_compiling { DUMMYQMAKECOMPILERINPUT = FORCE CMAKE_GENERATOR_OVERRIDE="" - win32-g++:CMAKE_GENERATOR_OVERRIDE="-G \"MSYS Makefiles\"" + win32-g++|win32-clang-g++ { + isEmpty(QMAKE_SH) { + CMAKE_GENERATOR_OVERRIDE="-G \"MinGW Makefiles\"" + } else { + CMAKE_GENERATOR_OVERRIDE="-G \"MSYS Makefiles\"" + } + } udpdiscoverycpplib.name = Generating libudp-discovery.a. udpdiscoverycpplib.input = DUMMYQMAKECOMPILERINPUT udpdiscoverycpplib.output = $$clean_path($${UDP_DISCOVERY_BUILD_PATH}/libudp-discovery.a) udpdiscoverycpplib.CONFIG += target_predeps combine udpdiscoverycpplib.variable_out = PRE_TARGETDEPS - udpdiscoverycpplib.commands = \ - cd $${RS_SRC_PATH} && ( \ - git submodule update --init supportlibs/udp-discovery-cpp || \ - true ) && \ - mkdir -p $${UDP_DISCOVERY_BUILD_PATH} && \ - cd $${UDP_DISCOVERY_BUILD_PATH} && \ + win32-g++:isEmpty(QMAKE_SH) { + udpdiscoverycpplib.commands = \ + cd $$shell_path($${RS_SRC_PATH}) && git submodule update --init supportlibs/udp-discovery-cpp || cd . $$escape_expand(\\n\\t) \ + $(CHK_DIR_EXISTS) $$shell_path($$UDP_DISCOVERY_BUILD_PATH) $(MKDIR) $$shell_path($${UDP_DISCOVERY_BUILD_PATH}) $$escape_expand(\\n\\t) + } else { + udpdiscoverycpplib.commands = \ + cd $${RS_SRC_PATH} && ( \ + git submodule update --init supportlibs/udp-discovery-cpp || \ + true ) && \ + mkdir -p $${UDP_DISCOVERY_BUILD_PATH} && + } + udpdiscoverycpplib.commands += \ + cd $$shell_path($${UDP_DISCOVERY_BUILD_PATH}) && \ cmake -DCMAKE_C_COMPILER=$$fixQmakeCC($$QMAKE_CC) \ -DCMAKE_CXX_COMPILER=$$QMAKE_CXX \ + \"-DCMAKE_CXX_FLAGS=$${QMAKE_CXXFLAGS}\" \ $${CMAKE_GENERATOR_OVERRIDE} \ -DBUILD_EXAMPLE=OFF -DBUILD_TOOL=OFF \ -DCMAKE_INSTALL_PREFIX=. -B. \ diff --git a/libretroshare/src/pgp/pgpauxutils.cc b/libretroshare/src/pgp/pgpauxutils.cc index a6a592c2c..d7229e848 100644 --- a/libretroshare/src/pgp/pgpauxutils.cc +++ b/libretroshare/src/pgp/pgpauxutils.cc @@ -67,14 +67,6 @@ bool PgpAuxUtilsImpl::parseSignature(unsigned char *sign, unsigned int signlen, return AuthGPG::getAuthGPG()->parseSignature(sign,signlen,issuer); } -bool PgpAuxUtilsImpl::askForDeferredSelfSignature(const void *data, - const uint32_t len, - unsigned char *sign, - unsigned int *signlen, - int& signature_result , std::string reason) -{ - return RsServer::notify()->askForDeferredSelfSignature(data, len, sign, signlen, signature_result, reason); -} diff --git a/libretroshare/src/pgp/pgpauxutils.h b/libretroshare/src/pgp/pgpauxutils.h index 604a5d6e0..4b188e3ae 100644 --- a/libretroshare/src/pgp/pgpauxutils.h +++ b/libretroshare/src/pgp/pgpauxutils.h @@ -41,10 +41,6 @@ class PgpAuxUtils virtual bool parseSignature(unsigned char *sign, unsigned int signlen, RsPgpId& issuer) const =0; virtual bool VerifySignBin(const void *data, uint32_t len, unsigned char *sign, unsigned int signlen, const PGPFingerprintType& withfingerprint) = 0; - - /** @deprecated this method depends on retroshare-gui to work */ - RS_DEPRECATED_FOR("AuthGPG::SignDataBin") - virtual bool askForDeferredSelfSignature(const void *data, const uint32_t len, unsigned char *sign, unsigned int *signlen,int& signature_result, std::string reason) = 0; }; class PgpAuxUtilsImpl: public PgpAuxUtils @@ -59,8 +55,6 @@ public: virtual bool getKeyFingerprint(const RsPgpId& id,PGPFingerprintType& fp) const; virtual bool VerifySignBin(const void *data, uint32_t len, unsigned char *sign, unsigned int signlen, const PGPFingerprintType& withfingerprint); virtual bool getGPGAllList(std::list &ids); - virtual bool askForDeferredSelfSignature(const void *data, const uint32_t len, unsigned char *sign, unsigned int *signlen,int& signature_result, std::string reason); - }; diff --git a/libretroshare/src/pgp/pgphandler.cc b/libretroshare/src/pgp/pgphandler.cc index 4ed342629..0638136b6 100644 --- a/libretroshare/src/pgp/pgphandler.cc +++ b/libretroshare/src/pgp/pgphandler.cc @@ -630,50 +630,61 @@ std::string PGPHandler::SaveCertificateToString(const RsPgpId& id,bool include_s return makeRadixEncodedPGPKey(key,include_signatures) ; } -bool PGPHandler::exportPublicKey(const RsPgpId& id,unsigned char *& mem_block,size_t& mem_size,bool armoured,bool include_signatures) const +bool PGPHandler::exportPublicKey( + const RsPgpId& id, + unsigned char*& mem_block, size_t& mem_size, + bool armoured, bool include_signatures ) const { - RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. - const ops_keydata_t *key = locked_getPublicKey(id,false) ; - mem_block = NULL ; + mem_block = nullptr; mem_size = 0; // clear just in case if(armoured) { - std::cerr << __PRETTY_FUNCTION__ << ": should not be used with armoured=true, because there's a bug in the armoured export of OPS" << std::endl; - return false ; + RsErr() << __PRETTY_FUNCTION__ << " should not be used with " + << "armoured=true, because there's a bug in the armoured export" + << " of OPS" << std::endl; + print_stacktrace(); + return false; } - if(key == NULL) + RS_STACK_MUTEX(pgphandlerMtx); + const ops_keydata_t* key = locked_getPublicKey(id,false); + + if(!key) { - std::cerr << "Cannot output key " << id.toStdString() << ": not found in keyring." << std::endl; - return false ; + RsErr() << __PRETTY_FUNCTION__ << " key id: " << id + << " not found in keyring." << std::endl; + return false; } - ops_create_info_t* cinfo; - ops_memory_t *buf = NULL ; - ops_setup_memory_write(&cinfo, &buf, 0); + ops_create_info_t* cinfo; + ops_memory_t *buf = nullptr; + ops_setup_memory_write(&cinfo, &buf, 0); - if(ops_write_transferable_public_key_from_packet_data(key,armoured,cinfo) != ops_true) + if(ops_write_transferable_public_key_from_packet_data( + key, armoured, cinfo ) != ops_true) { - std::cerr << "ERROR: This key cannot be processed by RetroShare because\nDSA certificates are not yet handled." << std::endl; - return false ; + RsErr() << __PRETTY_FUNCTION__ << " This key id " << id + << " cannot be processed by RetroShare because DSA certificates" + << " support is not implemented yet." << std::endl; + return false; } - ops_writer_close(cinfo) ; + ops_writer_close(cinfo); - mem_block = new unsigned char[ops_memory_get_length(buf)] ; - mem_size = ops_memory_get_length(buf) ; - memcpy(mem_block,ops_memory_get_data(buf),mem_size) ; + mem_size = ops_memory_get_length(buf); + mem_block = reinterpret_cast(malloc(mem_size)); + memcpy(mem_block,ops_memory_get_data(buf),mem_size); - ops_teardown_memory_write(cinfo,buf); + ops_teardown_memory_write(cinfo,buf); if(!include_signatures) { - size_t new_size ; - PGPKeyManagement::findLengthOfMinimalKey(mem_block,mem_size,new_size) ; - mem_size = new_size ; + size_t new_size; + PGPKeyManagement::findLengthOfMinimalKey(mem_block, mem_size, new_size); + mem_size = new_size; } - return true ; + return true; } bool PGPHandler::exportGPGKeyPair(const std::string& filename,const RsPgpId& exported_key_id) const diff --git a/libretroshare/src/pgp/pgphandler.h b/libretroshare/src/pgp/pgphandler.h index 0b785285a..5d7eb82f8 100644 --- a/libretroshare/src/pgp/pgphandler.h +++ b/libretroshare/src/pgp/pgphandler.h @@ -107,7 +107,11 @@ public: bool LoadCertificateFromBinaryData(const unsigned char *bin_data,uint32_t bin_data_len, RsPgpId& gpg_id, std::string& error_string); std::string SaveCertificateToString(const RsPgpId& id,bool include_signatures) const ; - bool exportPublicKey(const RsPgpId& id,unsigned char *& mem,size_t& mem_size,bool armoured,bool include_signatures) const ; + + /** The caller is in charge of freeing `mem` once finished */ + bool exportPublicKey( const RsPgpId& id, + unsigned char*& mem, size_t& mem_size, + bool armoured, bool include_signatures) const; bool parseSignature(unsigned char *sign, unsigned int signlen,RsPgpId& issuer_id) ; bool SignDataBin(const RsPgpId& id, const void *data, const uint32_t len, unsigned char *sign, unsigned int *signlen, bool make_raw_signature=false, std::string reason = "") ; diff --git a/libretroshare/src/plugins/pluginmanager.cc b/libretroshare/src/plugins/pluginmanager.cc index f2a3c8b29..dcd474f8f 100644 --- a/libretroshare/src/plugins/pluginmanager.cc +++ b/libretroshare/src/plugins/pluginmanager.cc @@ -362,6 +362,7 @@ bool RsPluginManager::loadPlugin(const std::string& plugin_name,bool first_time) dlclose(handle); return false ; } +#ifdef TO_REMOVE if(pinfo.svn_revision == 0) { std::cerr << " -> No svn revision number." << std::endl; @@ -370,6 +371,7 @@ bool RsPluginManager::loadPlugin(const std::string& plugin_name,bool first_time) dlclose(handle); return false ; } +#endif // Now look for the plugin class symbol. // diff --git a/libretroshare/src/pqi/authssl.cc b/libretroshare/src/pqi/authssl.cc index 352c853e0..bc098dc4e 100644 --- a/libretroshare/src/pqi/authssl.cc +++ b/libretroshare/src/pqi/authssl.cc @@ -30,6 +30,7 @@ #include "pqinetwork.h" #include "authgpg.h" #include "rsitems/rsconfigitems.h" +#include "util/rsdebug.h" #include "util/rsdir.h" #include "util/rsstring.h" #include "pgp/pgpkeyutil.h" @@ -404,10 +405,16 @@ int AuthSSLimpl::InitAuth( std::cout.flush() ; +#ifndef RS_DISABLE_DIFFIE_HELLMAN_INIT_CHECK if(DH_check(dh, &codes) && codes == 0) - SSL_CTX_set_tmp_dh(sslctx, dh); + SSL_CTX_set_tmp_dh(sslctx, dh); else - pfs_enabled = false ; + pfs_enabled = false; +#else // ndef RS_DISABLE_DIFFIE_HELLMAN_INIT_CHECK + /* DH_check(...) is not strictly necessary and on Android devices it + * takes at least one minute which is untolerable there */ + SSL_CTX_set_tmp_dh(sslctx, dh); +#endif // ndef RS_DISABLE_DIFFIE_HELLMAN_INIT_CHECK } else pfs_enabled = false ; @@ -798,10 +805,24 @@ X509 *AuthSSLimpl::SignX509ReqWithGPG(X509_REQ *req, long /*days*/) } X509_NAME_free(issuer_name); - // NEW code, set validity time between null and null - // (does not leak the key creation date to the outside anymore. for more privacy) - ASN1_TIME_set(X509_get_notBefore(x509), 0); - ASN1_TIME_set(X509_get_notAfter(x509), 0); +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) + // (does not leak the key creation date to the outside anymore. for more privacy) + ASN1_TIME_set(X509_get_notBefore(x509), 0); + ASN1_TIME_set(X509_get_notAfter(x509), 0); +#else + // NEW code, set validity time between 2010 and 2110 (remember to change it when, if OpenSSL check it by default. ;) ) + // (does not leak the key creation date to the outside anymore. for more privacy) + if (!ASN1_TIME_set_string(X509_getm_notBefore(x509), "20100101000000Z")) + { + RsErr() << __PRETTY_FUNCTION__ << " Set notBefore FAIL" << std::endl; + return NULL; + } + if (!ASN1_TIME_set_string(X509_getm_notAfter(x509), "21100101000000Z")) + { + RsErr() << __PRETTY_FUNCTION__ << " Set notAfter FAIL" << std::endl; + return NULL; + } +#endif if (!X509_set_subject_name(x509, X509_REQ_get_subject_name(req))) { diff --git a/libretroshare/src/pqi/p3netmgr.cc b/libretroshare/src/pqi/p3netmgr.cc index 8c0a736c4..a215ff312 100644 --- a/libretroshare/src/pqi/p3netmgr.cc +++ b/libretroshare/src/pqi/p3netmgr.cc @@ -711,47 +711,6 @@ void p3NetMgrIMPL::netExtCheck() } } -#ifdef ALLOW_DHT_STUNNER - // (cyril) I disabled this because it's pretty dangerous. The DHT can report a wrong address quite easily - // if the other DHT peers are not collaborating. - - /* Next ask the DhtStunner */ - { -#if defined(NETMGR_DEBUG_TICK) || defined(NETMGR_DEBUG_RESET) - std::cerr << "p3NetMgrIMPL::netExtCheck() Ext Not Ok, Checking DhtStunner" << std::endl; -#endif - uint8_t isstable = 0; - struct sockaddr_storage tmpaddr; - sockaddr_storage_clear(tmpaddr); - - if (mDhtStunner) - { - /* input network bits */ - if (mDhtStunner->getExternalAddr(tmpaddr, isstable)) - { - if((rsBanList == NULL) || rsBanList->isAddressAccepted(tmpaddr,RSBANLIST_CHECKING_FLAGS_BLACKLIST)) - { - // must be stable??? - isStable = (isstable == 1); - //mNetFlags.mExtAddr = tmpaddr; - mNetFlags.mExtAddrOk = true; - mNetFlags.mExtAddrStableOk = isStable; - - address_votes[tmpaddr].n++ ; -#ifdef NETMGR_DEBUG_STATEBOX - std::cerr << "p3NetMgrIMPL::netExtCheck() From DhtStunner: "; - std::cerr << sockaddr_storage_tostring(tmpaddr); - std::cerr << " Stable: " << (uint32_t) isstable; - std::cerr << std::endl; -#endif - } - else - std::cerr << "(SS) DHTStunner returned wrong own IP " << sockaddr_storage_iptostring(tmpaddr) << " (banned). Rejecting." << std::endl; - } - } - } -#endif - /* ask ExtAddrFinder */ { /* ExtAddrFinder */ @@ -829,6 +788,49 @@ void p3NetMgrIMPL::netExtCheck() #endif } +#ifdef ALLOW_DHT_STUNNER + // (cyril) I disabled this because it's pretty dangerous. The DHT can report a wrong address quite easily + // if the other DHT peers are not collaborating. + // (sehraf) For the record: The udp stunner uses multiple (as for now: two) peers to ensure that the IP recieved is the correct one, see UdpStunner::locked_checkExternalAddress() + // Nevertheless this stays a more risky method to determine the external ip address. + + /* lastly ask the DhtStunner as fallback */ + if (address_votes.empty()) { +#if defined(NETMGR_DEBUG_TICK) || defined(NETMGR_DEBUG_RESET) + std::cerr << "p3NetMgrIMPL::netExtCheck() Ext Not Ok, Checking DhtStunner" << std::endl; +#endif + uint8_t isstable = 0; + struct sockaddr_storage tmpaddr; + sockaddr_storage_clear(tmpaddr); + + if (mDhtStunner) + { + /* input network bits */ + if (mDhtStunner->getExternalAddr(tmpaddr, isstable)) + { + if((rsBanList == NULL) || rsBanList->isAddressAccepted(tmpaddr,RSBANLIST_CHECKING_FLAGS_BLACKLIST)) + { + // must be stable??? + isStable = (isstable == 1); + //mNetFlags.mExtAddr = tmpaddr; + mNetFlags.mExtAddrOk = true; + mNetFlags.mExtAddrStableOk = isStable; + + address_votes[tmpaddr].n++ ; +#ifdef NETMGR_DEBUG_STATEBOX + std::cerr << "p3NetMgrIMPL::netExtCheck() From DhtStunner: "; + std::cerr << sockaddr_storage_tostring(tmpaddr); + std::cerr << " Stable: " << (uint32_t) isstable; + std::cerr << std::endl; +#endif + } + else + std::cerr << "(SS) DHTStunner returned wrong own IP " << sockaddr_storage_iptostring(tmpaddr) << " (banned). Rejecting." << std::endl; + } + } + } +#endif + /* any other sources ??? */ /* finalise address */ @@ -1801,15 +1803,16 @@ void p3NetMgrIMPL::updateNatSetting() #endif #ifdef RS_USE_DHT_STUNNER - switch(natType) - { - case RsNatTypeMode::RESTRICTED_CONE: + if (mProxyStunner) { + switch(natType) + { + case RsNatTypeMode::RESTRICTED_CONE: { if ((natHole == RsNatHoleMode::NONE) || (natHole == RsNatHoleMode::UNKNOWN)) { mProxyStunner->setRefreshPeriod(NET_STUNNER_PERIOD_FAST); } - else + else { mProxyStunner->setRefreshPeriod(NET_STUNNER_PERIOD_SLOW); } @@ -1824,6 +1827,7 @@ void p3NetMgrIMPL::updateNatSetting() mProxyStunner->setRefreshPeriod(NET_STUNNER_PERIOD_SLOW); break; + } } #endif // RS_USE_DHT_STUNNER diff --git a/libretroshare/src/pqi/p3netmgr.h b/libretroshare/src/pqi/p3netmgr.h index bfcafca2a..a847c07b1 100644 --- a/libretroshare/src/pqi/p3netmgr.h +++ b/libretroshare/src/pqi/p3netmgr.h @@ -291,8 +291,8 @@ private: //p3BitDht *mBitDht; #ifdef RS_USE_DHT_STUNNER - pqiAddrAssist *mDhtStunner; - pqiAddrAssist *mProxyStunner; + pqiAddrAssist *mDhtStunner = nullptr; + pqiAddrAssist *mProxyStunner = nullptr; #endif // RS_USE_DHT_STUNNER RsMutex mNetMtx; /* protects below */ diff --git a/libretroshare/src/pqi/p3notify.cc b/libretroshare/src/pqi/p3notify.cc index 7a5dc8c45..772a7e9bb 100644 --- a/libretroshare/src/pqi/p3notify.cc +++ b/libretroshare/src/pqi/p3notify.cc @@ -293,14 +293,6 @@ bool p3Notify::askForPluginConfirmation (const std::string& plugin_filen return false ; } -bool p3Notify::askForDeferredSelfSignature (const void * data , const uint32_t len , unsigned char *sign, unsigned int *signlen,int& signature_result, std::string reason /*=""*/) -{ - FOR_ALL_NOTIFY_CLIENTS - if( (*it)->askForDeferredSelfSignature(data,len,sign,signlen,signature_result, reason)) - return true ; - - return false ; -} void p3Notify::registerNotifyClient(NotifyClient *cl) { diff --git a/libretroshare/src/pqi/p3notify.h b/libretroshare/src/pqi/p3notify.h index 46fb69c8e..3d78cdf8c 100644 --- a/libretroshare/src/pqi/p3notify.h +++ b/libretroshare/src/pqi/p3notify.h @@ -114,7 +114,6 @@ class p3Notify: public RsNotify void notifyPeerStatusChangedSummary () ; void notifyDiscInfoChanged () ; - bool askForDeferredSelfSignature (const void * /* data */, const uint32_t /* len */, unsigned char * /* sign */, unsigned int * /* signlen */, int& signature_result , std::string reason = "") ; void notifyDownloadComplete (const std::string& /* fileHash */) ; void notifyDownloadCompleteCount (uint32_t /* count */) ; void notifyHistoryChanged (uint32_t /* msgId */, int /* type */) ; diff --git a/libretroshare/src/pqi/pqihandler.cc b/libretroshare/src/pqi/pqihandler.cc index 1e86fa9d1..72d7dbf9d 100644 --- a/libretroshare/src/pqi/pqihandler.cc +++ b/libretroshare/src/pqi/pqihandler.cc @@ -42,39 +42,19 @@ using std::dec; #include #endif -//#define PQI_HDL_DEBUG_UR 1 - -#ifdef PQI_HDL_DEBUG_UR -static double getCurrentTS() -{ - -#ifndef WINDOWS_SYS - struct timeval cts_tmp; - gettimeofday(&cts_tmp, NULL); - double cts = (cts_tmp.tv_sec) + ((double) cts_tmp.tv_usec) / 1000000.0; -#else - struct _timeb timebuf; - _ftime( &timebuf); - double cts = (timebuf.time) + ((double) timebuf.millitm) / 1000.0; -#endif - return cts; -} -#endif - struct RsLog::logInfo pqihandlerzoneInfo = {RsLog::Default, "pqihandler"}; #define pqihandlerzone &pqihandlerzoneInfo //static const int PQI_HANDLER_NB_PRIORITY_LEVELS = 10 ; //static const float PQI_HANDLER_NB_PRIORITY_RATIO = 2 ; -/**** -#define DEBUG_TICK 1 -#define RSITEM_DEBUG 1 -****/ +//#define UPDATE_RATES_DEBUG 1 +// #define DEBUG_TICK 1 +// #define RSITEM_DEBUG 1 pqihandler::pqihandler() : coreMtx("pqihandler") { - RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + RS_STACK_MUTEX(coreMtx); /**************** LOCKED MUTEX ****************/ // setup minimal total+individual rates. rateIndiv_out = 0.01; @@ -97,7 +77,7 @@ int pqihandler::tick() int moreToTick = 0; { - RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + RS_STACK_MUTEX(coreMtx); /**************** LOCKED MUTEX ****************/ // tick all interfaces... std::map::iterator it; @@ -127,9 +107,13 @@ int pqihandler::tick() if(now > mLastRateCapUpdate + 5) { + std::map rateMap; + std::map::iterator it; + + // every 5 secs, update the max rates for all modules - RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + RS_STACK_MUTEX(coreMtx); /**************** LOCKED MUTEX ****************/ for(std::map::iterator it = mods.begin(); it != mods.end(); ++it) { // This is rather inelegant, but pqihandler has searchModules that are dynamically allocated, so the max rates @@ -149,7 +133,7 @@ int pqihandler::tick() bool pqihandler::queueOutRsItem(RsItem *item) { - RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + RS_STACK_MUTEX(coreMtx); /**************** LOCKED MUTEX ****************/ uint32_t size ; locked_HandleRsItem(item, size); @@ -166,7 +150,7 @@ bool pqihandler::queueOutRsItem(RsItem *item) int pqihandler::status() { std::map::iterator it; - RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + RS_STACK_MUTEX(coreMtx); /**************** LOCKED MUTEX ****************/ { // for output std::string out = "pqihandler::status() Active Modules:\n"; @@ -192,7 +176,7 @@ int pqihandler::status() bool pqihandler::AddSearchModule(SearchModule *mod) { - RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + RS_STACK_MUTEX(coreMtx); /**************** LOCKED MUTEX ****************/ // if peerid used -> error. //std::map::iterator it; if (mod->peerid != mod->pqi->PeerId()) @@ -223,7 +207,7 @@ bool pqihandler::AddSearchModule(SearchModule *mod) bool pqihandler::RemoveSearchModule(SearchModule *mod) { - RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + RS_STACK_MUTEX(coreMtx); /**************** LOCKED MUTEX ****************/ std::map::iterator it; for(it = mods.begin(); it != mods.end(); ++it) { @@ -313,7 +297,7 @@ int pqihandler::ExtractRates(std::map &ratemap, RsBwRat total.mQueueOut = 0; /* Lock once rates have been retrieved */ - RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + RS_STACK_MUTEX(coreMtx); /**************** LOCKED MUTEX ****************/ std::map::iterator it; for(it = mods.begin(); it != mods.end(); ++it) @@ -340,10 +324,6 @@ int pqihandler::ExtractRates(std::map &ratemap, RsBwRat // internal fn to send updates int pqihandler::UpdateRates() { -#ifdef PQI_HDL_DEBUG_UR - uint64_t t_now; -#endif - std::map::iterator it; float avail_in = getMaxRate(true); @@ -353,18 +333,15 @@ int pqihandler::UpdateRates() float used_bw_out = 0; /* Lock once rates have been retrieved */ - RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + RS_STACK_MUTEX(coreMtx); /**************** LOCKED MUTEX ****************/ int num_sm = mods.size(); float used_bw_in_table[num_sm]; /* table of in bandwidth currently used by each module */ float used_bw_out_table[num_sm]; /* table of out bandwidth currently used by each module */ - int effectiveUploadsSm = 0; - int effectiveDownloadsSm = 0; - - // loop through modules to get the used bandwith and the number of modules that are affectively transfering -#ifdef PQI_HDL_DEBUG_UR - std::cerr << "Looping through modules" << std::endl; + // loop through modules to get the used bandwidth +#ifdef UPDATE_RATES_DEBUG + RsDbg() << "UPDATE_RATES pqihandler::UpdateRates Looping through modules" << std::endl; #endif int index = 0; @@ -372,49 +349,33 @@ int pqihandler::UpdateRates() for(it = mods.begin(); it != mods.end(); ++it) { SearchModule *mod = (it -> second); - float crate_in = mod -> pqi -> getRate(true); traffInSum += mod -> pqi -> getTraffic(true); traffOutSum += mod -> pqi -> getTraffic(false); -#ifdef PQI_HDL_DEBUG_UR - if(crate_in > 0.0) - std::cerr << " got in rate for peer " << it->first << " : " << crate_in << std::endl; -#endif - - if ((crate_in > 0.01 * avail_in) || (crate_in > 0.1)) - { - ++effectiveDownloadsSm; - } - + float crate_in = mod -> pqi -> getRate(true); float crate_out = mod -> pqi -> getRate(false); - if ((crate_out > 0.01 * avail_out) || (crate_out > 0.1)) - { - ++effectiveUploadsSm; - } used_bw_in += crate_in; used_bw_out += crate_out; - /* fill the table of bandwidth */ + /* fill the table of used bandwidths */ used_bw_in_table[index] = crate_in; used_bw_out_table[index] = crate_out; + ++index; } -#ifdef PQI_HDL_DEBUG_UR - t_now = 1000 * getCurrentTS(); - std::cerr << dec << t_now << " pqihandler::UpdateRates(): Sorting used_bw_out_table: " << num_sm << " entries" << std::endl; +#ifdef UPDATE_RATES_DEBUG + RsDbg() << "UPDATE_RATES pqihandler::UpdateRates Sorting used_bw_out_table: " << num_sm << " entries" << std::endl; #endif /* Sort the used bw in/out table in ascending order */ std::sort(used_bw_in_table, used_bw_in_table + num_sm); std::sort(used_bw_out_table, used_bw_out_table + num_sm); -#ifdef PQI_HDL_DEBUG_UR - t_now = 1000 * getCurrentTS(); - std::cerr << dec << t_now << " pqihandler::UpdateRates(): Done." << std::endl; - std::cerr << dec << t_now << " pqihandler::UpdateRates(): used_bw_out " << used_bw_out << std::endl; +#ifdef UPDATE_RATES_DEBUG + RsDbg() << "UPDATE_RATES pqihandler::UpdateRates used_bw_out " << used_bw_out << std::endl; #endif /* Calculate the optimal out_max value, taking into account avail_out and the out bw requested by modules */ @@ -441,9 +402,8 @@ int pqihandler::UpdateRates() } } -#ifdef PQI_HDL_DEBUG_UR - t_now = 1000 * getCurrentTS(); - std::cerr << dec << t_now << " pqihandler::UpdateRates(): mod_index " << mod_index << " out_max_bw " << out_max_bw << " remaining out bw " << out_remaining_bw << std::endl; +#ifdef UPDATE_RATES_DEBUG + RsDbg() << "UPDATE_RATES pqihandler::UpdateRates mod_index " << mod_index << " out_max_bw " << out_max_bw << " remaining out bw " << out_remaining_bw << std::endl; #endif /* Allocate only half the remaining out bw, if any, to make it smoother */ @@ -473,67 +433,70 @@ int pqihandler::UpdateRates() } } -#ifdef PQI_HDL_DEBUG_UR - t_now = 1000 * getCurrentTS(); - std::cerr << dec << t_now << " pqihandler::UpdateRates(): mod_index " << mod_index << " in_max_bw " << in_max_bw << " remaining in bw " << in_remaining_bw << std::endl; +#ifdef UPDATE_RATES_DEBUG + RsDbg() << "UPDATE_RATES pqihandler::UpdateRates mod_index " << mod_index << " in_max_bw " << in_max_bw << " remaining in bw " << in_remaining_bw << std::endl; #endif /* Allocate only half the remaining in bw, if any, to make it smoother */ in_max_bw = in_max_bw + in_remaining_bw / 2; - -#ifdef DEBUG_QOS -// std::cerr << "Totals (In) Used B/W " << used_bw_in; -// std::cerr << " Available B/W " << avail_in; -// std::cerr << " Effective transfers " << effectiveDownloadsSm << std::endl; -// std::cerr << "Totals (Out) Used B/W " << used_bw_out; -// std::cerr << " Available B/W " << avail_out; -// std::cerr << " Effective transfers " << effectiveUploadsSm << std::endl; -#endif - + // store current total in and ou used bw locked_StoreCurrentRates(used_bw_in, used_bw_out); - //computing average rates for effective transfers - float max_in_effective = avail_in / num_sm; - if (effectiveDownloadsSm != 0) { - max_in_effective = avail_in / effectiveDownloadsSm; - } - float max_out_effective = avail_out / num_sm; - if (effectiveUploadsSm != 0) { - max_out_effective = avail_out / effectiveUploadsSm; - } - - //modify the in and out limit -#ifdef PQI_HDL_DEBUG_UR - t_now = 1000 * getCurrentTS(); - std::cerr << dec << t_now << " pqihandler::UpdateRates(): setting new out_max " << out_max_bw << " in_max " << in_max_bw << std::endl; +#ifdef UPDATE_RATES_DEBUG + RsDbg() << "UPDATE_RATES pqihandler::UpdateRates setting new out_max " << out_max_bw << " in_max " << in_max_bw << std::endl; #endif + // retrieve down (from peer point of view) bandwidth limits set by peers in their own settings + std::map rateMap; + rsConfig->getAllBandwidthRates(rateMap); + std::map::iterator rateMap_it; + +#ifdef UPDATE_RATES_DEBUG + // Dump RsConfigurationDataRates + RsDbg() << "UPDATE_RATES pqihandler::UpdateRates RsConfigDataRates dump" << std::endl; + for (rateMap_it = rateMap.begin(); rateMap_it != rateMap.end(); rateMap_it++) + RsDbg () << "UPDATE_RATES pqihandler::UpdateRates PeerId " << rateMap_it->first.toStdString() << " mAllowedOut " << rateMap_it->second.mAllowedOut << std::endl; +#endif + + // update max rates taking into account the limits set by peers in their own settings for(it = mods.begin(); it != mods.end(); ++it) { SearchModule *mod = (it -> second); - - mod -> pqi -> setMaxRate(true, in_max_bw); - mod -> pqi -> setMaxRate(false, out_max_bw); + + // for our down bandwidth we set the max to the calculated value without taking into account the max set by peers: they will control their up bw on their side + mod -> pqi -> setMaxRate(true, in_max_bw); + + // for our up bandwidth we limit to the maximum down bw provided by peers via BwCtrl because we don't want to clog our outqueues, the SSL buffers, and our friends inbound queues + if ((rateMap_it = rateMap.find(mod->pqi->PeerId())) != rateMap.end()) + { + if (rateMap_it->second.mAllowedOut > 0) + { + if (out_max_bw > rateMap_it->second.mAllowedOut) + mod -> pqi -> setMaxRate(false, rateMap_it->second.mAllowedOut); + else + mod -> pqi -> setMaxRate(false, out_max_bw); + } + else + mod -> pqi -> setMaxRate(false, out_max_bw); + } } - - //cap the rates +#ifdef UPDATE_RATES_DEBUG + // dump maxRates for(it = mods.begin(); it != mods.end(); ++it) { SearchModule *mod = (it -> second); - if (mod -> pqi -> getMaxRate(false) < max_out_effective) mod -> pqi -> setMaxRate(false, max_out_effective); - if (mod -> pqi -> getMaxRate(false) > avail_out) mod -> pqi -> setMaxRate(false, avail_out); - if (mod -> pqi -> getMaxRate(true) < max_in_effective) mod -> pqi -> setMaxRate(true, max_in_effective); - if (mod -> pqi -> getMaxRate(true) > avail_in) mod -> pqi -> setMaxRate(true, avail_in); + RsDbg() << "UPDATE_RATES pqihandler::UpdateRates PeerID " << (mod ->pqi -> PeerId()).toStdString() << " new bandwidth limits up " << mod -> pqi -> getMaxRate(false) << " down " << mod -> pqi -> getMaxRate(true) << std::endl; } +#endif return 1; } void pqihandler::getCurrentRates(float &in, float &out) { - RsStackMutex stack(coreMtx); /**************** LOCKED MUTEX ****************/ + RS_STACK_MUTEX(coreMtx); /**************** LOCKED MUTEX ****************/ in = rateTotal_in; out = rateTotal_out; diff --git a/libretroshare/src/pqi/pqinetwork.cc b/libretroshare/src/pqi/pqinetwork.cc index e2251ed6a..18fe71fc0 100644 --- a/libretroshare/src/pqi/pqinetwork.cc +++ b/libretroshare/src/pqi/pqinetwork.cc @@ -413,9 +413,10 @@ int unix_fcntl_nonblock(int fd) { int ret; -/******************* WINDOWS SPECIFIC PART ******************/ +/******************* OS SPECIFIC PART ******************/ #ifndef WINDOWS_SYS // ie UNIX - ret = fcntl(fd, F_SETFL, O_NONBLOCK); + int flags = fcntl(fd, F_GETFL); + ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK); #ifdef NET_DEBUG std::cerr << "unix_fcntl_nonblock():" << ret << " errno:" << errno << std::endl; diff --git a/libretroshare/src/pqi/pqiservice.cc b/libretroshare/src/pqi/pqiservice.cc index 3d28f9164..86336631a 100644 --- a/libretroshare/src/pqi/pqiservice.cc +++ b/libretroshare/src/pqi/pqiservice.cc @@ -1,5 +1,5 @@ /******************************************************************************* - * libretroshare/src/pqi: pqiservice.h * + * libretroshare/src/pqi: pqiservice.cc * * * * libretroshare: retroshare core library * * * @@ -23,6 +23,22 @@ #include "util/rsdebug.h" #include "util/rsstring.h" +#include +#include +static double getCurrentTS() +{ +#ifndef WINDOWS_SYS + struct timeval cts_tmp; + gettimeofday(&cts_tmp, NULL); + double cts = (cts_tmp.tv_sec) + ((double) cts_tmp.tv_usec) / 1000000.0; +#else + struct _timeb timebuf; + _ftime( &timebuf); + double cts = (timebuf.time) + ((double) timebuf.millitm) / 1000.0; +#endif + return cts; +} + #ifdef SERVICE_DEBUG const int pqiservicezone = 60478; #endif @@ -44,7 +60,7 @@ bool pqiService::send(RsRawItem *item) p3ServiceServer::p3ServiceServer(pqiPublisher *pub, p3ServiceControl *ctrl) : mPublisher(pub), mServiceControl(ctrl), srvMtx("p3ServiceServer") { - RsStackMutex stack(srvMtx); /********* LOCKED *********/ + RS_STACK_MUTEX(srvMtx); /********* LOCKED *********/ #ifdef SERVICE_DEBUG pqioutput(PQL_DEBUG_BASIC, pqiservicezone, @@ -56,7 +72,7 @@ p3ServiceServer::p3ServiceServer(pqiPublisher *pub, p3ServiceControl *ctrl) : mP int p3ServiceServer::addService(pqiService *ts, bool defaultOn) { - RsStackMutex stack(srvMtx); /********* LOCKED *********/ + RS_STACK_MUTEX(srvMtx); /********* LOCKED *********/ #ifdef SERVICE_DEBUG pqioutput(PQL_DEBUG_BASIC, pqiservicezone, @@ -84,7 +100,7 @@ int p3ServiceServer::addService(pqiService *ts, bool defaultOn) bool p3ServiceServer::getServiceItemNames(uint32_t service_type,std::map& names) { - RsStackMutex stack(srvMtx); /********* LOCKED *********/ + RS_STACK_MUTEX(srvMtx); /********* LOCKED *********/ std::map::iterator it=services.find(service_type) ; @@ -99,7 +115,7 @@ bool p3ServiceServer::getServiceItemNames(uint32_t service_type,std::map PacketId(), (item -> PacketId() & 0xffffff00)); - item -> print_string(out); - std::cerr << out; - std::cerr << std::endl; - } -#endif - // Packet Filtering. // This doesn't need to be in Mutex. if (!mServiceControl->checkFilter(item->PacketId() & 0xffffff00, item->PeerId())) { -#ifdef SERVICE_DEBUG - std::cerr << "p3ServiceServer::recvItem() Fails Filtering " << std::endl; -#endif delete item; return false; } + pqiService *s = NULL; - std::map::iterator it; - it = services.find(item -> PacketId() & 0xffffff00); - if (it == services.end()) + // access the service map under mutex lock { -#ifdef SERVICE_DEBUG - std::cerr << "p3ServiceServer::incoming() Service: No Service - deleting"; - std::cerr << std::endl; -#endif - delete item; - return false; + RS_STACK_MUTEX(srvMtx); + auto it = services.find(item -> PacketId() & 0xffffff00); + if (it == services.end()) + { + delete item; + return false; + } + s = it->second; } - { -#ifdef SERVICE_DEBUG - std::cerr << "p3ServiceServer::incoming() Sending to : " << (void *) it -> second; - std::cerr << std::endl; -#endif - - return (it->second) -> recv(item); - } - - delete item; - return false; + // then call recv off mutex + bool result = s->recv(item); + return result; } - - bool p3ServiceServer::sendItem(RsRawItem *item) { #ifdef SERVICE_DEBUG @@ -196,46 +185,35 @@ bool p3ServiceServer::sendItem(RsRawItem *item) // Packet Filtering. if (!mServiceControl->checkFilter(item->PacketId() & 0xffffff00, item->PeerId())) { +#ifdef SERVICE_DEBUG std::cerr << "p3ServiceServer::sendItem() Fails Filtering for packet id=" << std::hex << item->PacketId() << std::dec << ", and peer " << item->PeerId() << std::endl; +#endif delete item; return false; } mPublisher->sendItem(item); + return true; } - - int p3ServiceServer::tick() { - mServiceControl->tick(); - RsStackMutex stack(srvMtx); /********* LOCKED *********/ - -#ifdef SERVICE_DEBUG - pqioutput(PQL_DEBUG_ALL, pqiservicezone, - "p3ServiceServer::tick()"); -#endif - - std::map::iterator it; - - // from the beginning to where we started. - for(it = services.begin();it != services.end(); ++it) - { - -#ifdef SERVICE_DEBUG - std::string out; - rs_sprintf(out, "p3ServiceServer::service id: %u -> Service: %p", it -> first, it -> second); - pqioutput(PQL_DEBUG_ALL, pqiservicezone, out); -#endif - - // now we should actually tick the service. - (it -> second) -> tick(); + // make a copy of the service map + std::map local_map; + { + RS_STACK_MUTEX(srvMtx); + local_map=services; } + + // tick all services off mutex + for(auto it(local_map.begin());it!=local_map.end();++it) + { + (it->second)->tick(); + } + return 1; + } - - - diff --git a/libretroshare/src/pqi/pqissl.cc b/libretroshare/src/pqi/pqissl.cc index 0ef930fc2..734fc9aba 100644 --- a/libretroshare/src/pqi/pqissl.cc +++ b/libretroshare/src/pqi/pqissl.cc @@ -372,9 +372,11 @@ int pqissl::status() // tick...... int pqissl::tick() { - RsStackMutex stack(mSslMtx); /**** LOCKED MUTEX ****/ + // there is no reason to lock pqissl mutex now + // we will lock the mutex later if we actually need to call to ConnectAttempt + // RsStackMutex stack(mSslMtx); /**** LOCKED MUTEX ****/ - //pqistreamer::tick(); + // pqistreamer::tick(); // continue existing connection attempt. if (!active) @@ -385,7 +387,8 @@ int pqissl::tick() #ifdef PQISSL_LOG_DEBUG rslog(RSL_DEBUG_BASIC, pqisslzone, "pqissl::tick() Continuing Connection Attempt!"); #endif - + // now lock pqissl mutex, that will take up to 10 ms + RsStackMutex stack(mSslMtx); /**** LOCKED MUTEX ****/ ConnectAttempt(); return 1; } @@ -1110,10 +1113,16 @@ int pqissl::SSL_Connection_Complete() if(rsEvents) { X509 *x509 = SSL_get_peer_certificate(ssl_connection); - auto ev = std::make_shared(); - ev->mSslId = RsX509Cert::getCertSslId(*x509); - ev->mErrorCode = RsAuthSslError::PEER_REFUSED_CONNECTION; - rsEvents->postEvent(ev); + + if(x509) + { + auto ev = std::make_shared(); + ev->mSslId = RsX509Cert::getCertSslId(*x509); + ev->mErrorCode = RsAuthSslError::PEER_REFUSED_CONNECTION; + + if(!ev->mSslId.isNull()) + rsEvents->postEvent(ev); + } } std::string out; diff --git a/libretroshare/src/pqi/pqissl.h b/libretroshare/src/pqi/pqissl.h index 3f315e329..30905be5e 100644 --- a/libretroshare/src/pqi/pqissl.h +++ b/libretroshare/src/pqi/pqissl.h @@ -56,6 +56,69 @@ const int PQISSL_UDP_FLAG = 0x02; /* TCP buffer size for Windows systems */ const int WINDOWS_TCP_BUFFER_SIZE = 512 * 1024; // 512 KB + +// This is a (very) simple overview of the different state machnines. The tree includes high level funtions only. +// +// connect_parameter() is used to pass down settings, like address or timeout values +// +// tick() or connect() +// | +// +----- ConnectAttempt() +// | +// +--WAITING_NOT or WAITING_DELAY +// | | +// | +----- Delay_Connection() +// | | +// | +--WAITING_NOT +// | | - set 'waiting' to WAITING_DELAY and set delay for next connection attempt +// | | +// | +--WAITING_DELAY +// | | +// | +----- Initiate_Connection() +// | | +// | +----- setup socket +// | +----- connect +// | - on success: set "waiting" to WAITING_SOCK_CONNECT and "sockfd" to newly created socket +// | - on failure: set "waiting" to WAITING_FAIL_INTERFACE +// | +// +--WAITING_SOCK_CONNECT +// | | +// | +----- Initiate_SSL_Connection() +// | | +// | +----- Basic_Connection_Complete() +// | | | +// | | +----- CheckConnectionTimeout() +// | | | +// | | +----- ready up socket. +// | | - SOCKS, udp tou, i2p BOB intercept here +// | | - on failure: set "waiting" to WAITING_FAIL_INTERFACE and "sockfd" to -1 +// | | +// | +----- create SSL context and attach file descriptors +// | - on success:_set "waiting" to WAITING_SSL_CONNECTION +// | +// +--WAITING_SSL_CONNECTION or WAITING_SSL_AUTHORISE +// | | +// | +----- Authorise_SSL_Connection() +// | | +// | +----- SSL_Connection_Complete() +// | | | +// | | +----- performes TSL handshake +// | | - on success: set "waiting" to WAITING_SSL_AUTHORISE +// | | - on failure: set "waiting" to WAITING_FAIL_INTERFACE +// | | +// | +----- set "waiting" to WAITING_NOT +// | | +// | +----- accept_locked() +// | - add peer to the rest of RS and start pqi thread +// | +// | +// +--WAITING_FAIL_INTERFACE +// | +// +----- Failed_Connection() +// - set "waiting" to WAITING_NOT +// + + /***************************** pqi Net SSL Interface ********************************* * This provides the base SSL interface class, * and handles most of the required functionality. @@ -203,9 +266,9 @@ bool CheckConnectionTimeout(); uint32_t mConnectTimeout; rstime_t mTimeoutTS; + RS_SET_CONTEXT_DEBUG_LEVEL(1) + private: // ssl only fns. int connectInterface(const struct sockaddr_storage &addr); - - RS_SET_CONTEXT_DEBUG_LEVEL(1) }; diff --git a/libretroshare/src/pqi/pqissllistener.h b/libretroshare/src/pqi/pqissllistener.h index 42ad0d0c2..85af2d869 100644 --- a/libretroshare/src/pqi/pqissllistener.h +++ b/libretroshare/src/pqi/pqissllistener.h @@ -34,6 +34,73 @@ #define RS_PQISSL_AUTH_DOUBLE_CHECK 1 + +// This is a simple overview of how the listener is setup, ticked (calling accept) and peers added to it. +// On the highest level (RsServer) the listener lives inside the pqisslpersongrp class (variable: "pqih"). +// Inside pqisslpersongrp the listener is stored in "pqil". +// +// The listener has an internal list with incoming connections that are handled in a similar fashion to pqissl. +// (Mainly setting up the socket (non-blocking) and establisching the ssl handshake.) +// When everything went fine the connection is passed to pqissl in finaliseConnection() +// +// This is how the listener is initialized during start up: +// +// RsServer::StartupRetroShare() +// | +// +----- pqih = new pqisslpersongrp(serviceCtrl, flags, mPeerMgr); +// +----- pqih->init_listener(); +// | +// +----- pqil = locked_createListener(laddr); +// | +// +----- return new pqissllistener(laddr, mPeerMgr); +// +// +// This is how the listener is ticked to call accept: +// +// RsServer::StartupRetroShare() +// | +// +----- pqih->tick(); +// | +// +----- pqil->tick(); +// | +// +----- acceptconnection(); +// | | +// | +----- accecpt() +// | +// +----- continueaccepts(); +// +----- finaliseAccepts(); +// | +// +----- finaliseConnection() +// | +// +----- pqis->accept() +// +// +// This is how peers (their id) are registered to the listener: +// (This is only used to tell if a connection peer is known or a new one (which is then added)) +// +// pqipersongrp::addPeer() +// | +// +----- pqiperson *pqip = locked_createPerson(id, pqil); +// | | +// | +----- pqiperson *pqip = new pqiperson(id, this); +// | +----- pqissl *pqis = new pqissl((pqissllistener *) listener, pqip, mLinkMgr); +// | +----- pqiconnect *pqisc = new pqiconnect(pqip, rss, pqis); +// | +----- pqip->addChildInterface(PQI_CONNECT_TCP, pqisc); +// | | +// | +-- sets kids[type] = pqisc; +// | +// +----- pqip->reset(); +// +----- pqip->listen(); +// | +// +-- for all kids[] +// | +// +----- listen() ( of class pqiconnect ) +// | +// +----- listen() ( of class pqissl ) +// | +// +----- pqil->addlistenaddr(PeerId(), this); + + /***************************** pqi Net SSL Interface ********************************* */ diff --git a/libretroshare/src/pqi/pqistreamer.cc b/libretroshare/src/pqi/pqistreamer.cc index dc9ceaabc..62b4d7f84 100644 --- a/libretroshare/src/pqi/pqistreamer.cc +++ b/libretroshare/src/pqi/pqistreamer.cc @@ -1,5 +1,5 @@ /******************************************************************************* - * libretroshare/src/pqi: pqistreamer.h * + * libretroshare/src/pqi: pqistreamer.cc * * * * libretroshare: retroshare core library * * * @@ -102,38 +102,39 @@ pqistreamer::pqistreamer(RsSerialiser *rss, const RsPeerId& id, BinInterface *bi mAvgDtOut(0), mAvgDtIn(0) { - // 100 B/s (minimal) - setMaxRate(true, 0.1); - setMaxRate(false, 0.1); - setRate(true, 0); // needs to be off-mutex - setRate(false, 0); + // 100 B/s (minimal) + setMaxRate(true, 0.1); + setMaxRate(false, 0.1); + setRate(true, 0); // needs to be off-mutex + setRate(false, 0); - RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/ + RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/ - mAcceptsPacketSlicing = false ; // by default. Will be turned into true when everyone's ready. - mLastSentPacketSlicingProbe = 0 ; + mAcceptsPacketSlicing = false ; // by default. Will be turned into true when everyone's ready. + mLastSentPacketSlicingProbe = 0 ; - mAvgLastUpdate = mCurrSentTS = mCurrReadTS = getCurrentTS(); + mAvgLastUpdate = mCurrSentTS = mCurrReadTS = getCurrentTS(); - mIncomingSize = 0 ; + mIncomingSize = 0 ; + mIncomingSize_bytes = 0; - mStatisticsTimeStamp = 0 ; - /* allocated once */ - mPkt_rpend_size = 0; - mPkt_rpending = 0; - mReading_state = reading_state_initial ; + mStatisticsTimeStamp = 0 ; + /* allocated once */ + mPkt_rpend_size = 0; + mPkt_rpending = 0; + mReading_state = reading_state_initial ; - pqioutput(PQL_DEBUG_ALL, pqistreamerzone, "pqistreamer::pqistreamer() Initialisation!"); + pqioutput(PQL_DEBUG_ALL, pqistreamerzone, "pqistreamer::pqistreamer() Initialisation!"); - if (!bio_in) - { - pqioutput(PQL_ALERT, pqistreamerzone, "pqistreamer::pqistreamer() NULL bio, FATAL ERROR!"); - exit(1); - } + if (!bio_in) + { + pqioutput(PQL_ALERT, pqistreamerzone, "pqistreamer::pqistreamer() NULL bio, FATAL ERROR!"); + exit(1); + } - mFailed_read_attempts = 0; // reset failed read, as no packet is still read. + mFailed_read_attempts = 0; // reset failed read, as no packet is still read. - return; + return; } pqistreamer::~pqistreamer() @@ -159,7 +160,7 @@ pqistreamer::~pqistreamer() if (mRsSerialiser) delete mRsSerialiser; - free_pend_locked() ; + free_pend() ; // clean up incoming. while (!mIncoming.empty()) @@ -177,6 +178,7 @@ pqistreamer::~pqistreamer() // Get/Send Items. +// This is the entry poing for methods willing to send items through our out queue int pqistreamer::SendItem(RsItem *si,uint32_t& out_size) { #ifdef RSITEM_DEBUG @@ -199,18 +201,30 @@ RsItem *pqistreamer::GetItem() pqioutput(PQL_DEBUG_ALL, pqistreamerzone, "pqistreamer::GetItem()"); #endif - RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/ - if(mIncoming.empty()) return NULL; RsItem *osr = mIncoming.front() ; - mIncoming.pop_front() ; - --mIncomingSize; + mIncoming.pop_front() ; + --mIncomingSize; +// for future use +// mIncomingSize_bytes -= return osr; } + +float pqistreamer::getMaxRate(bool b) +{ + RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/ + return getMaxRate_locked(b); +} + +float pqistreamer::getMaxRate_locked(bool b) +{ + return RateInterface::getMaxRate(b) ; +} + float pqistreamer::getRate(bool b) { RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/ @@ -219,26 +233,28 @@ float pqistreamer::getRate(bool b) void pqistreamer::setMaxRate(bool b,float f) { - RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/ - RateInterface::setMaxRate(b,f) ; + RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/ + setMaxRate_locked(b,f); } + +void pqistreamer::setMaxRate_locked(bool b,float f) +{ + RateInterface::setMaxRate(b,f) ; +} + void pqistreamer::setRate(bool b,float f) { RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/ RateInterface::setRate(b,f) ; } + void pqistreamer::updateRates() { - // update rates both ways. + // update actual rates both ways. double t = getCurrentTS(); // get current timestamp. - double diff ; - - { - RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/ - diff = t - mAvgLastUpdate ; - } + double diff = t - mAvgLastUpdate; if (diff > PQISTREAM_AVG_PERIOD) { @@ -263,10 +279,11 @@ void pqistreamer::updateRates() setRate(false, 0); } + mAvgLastUpdate = t; + mAvgReadCount = 0; + { RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/ - mAvgLastUpdate = t; - mAvgReadCount = 0; mAvgSentCount = 0; } } @@ -277,7 +294,7 @@ int pqistreamer::tick_bio() RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/ mBio->tick(); - /* short circuit everything is bio isn't active */ + /* short circuit everything if bio isn't active */ if (!(mBio->isactive())) { return 0; @@ -285,36 +302,36 @@ int pqistreamer::tick_bio() return 1; } - int pqistreamer::tick_recv(uint32_t timeout) { - RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/ +// Apart from a few exceptions that are atomic (mLastIncomingTs, mIncomingSize), only this pqi thread reads/writes mIncoming queue and related counters. +// The lock of pqistreamer mutex is thus not needed here. +// The mutex lock is still needed before calling locked_addTrafficClue because this method is also used by the thread pushing packets in mOutPkts. +// Locks around rates are provided internally. if (mBio->moretoread(timeout)) { - handleincoming_locked(); + handleincoming(); + } + if(!(mBio->isactive())) + { + free_pend(); } - if(!(mBio->isactive())) - { - free_pend_locked(); - } return 1; } - int pqistreamer::tick_send(uint32_t timeout) { - RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/ - - /* short circuit everything is bio isn't active */ + /* short circuit everything if bio isn't active */ if (!(mBio->isactive())) { - free_pend_locked(); + free_pend(); return 0; } if (mBio->cansend(timeout)) { + RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/ handleoutgoing_locked(); } @@ -340,12 +357,11 @@ int pqistreamer::status() return 0; } +// this method is overloaded by pqiqosstreamer void pqistreamer::locked_storeInOutputQueue(void *ptr,int,int) { mOutPkts.push_back(ptr); } -// -/**************** HANDLE OUTGOING TRANSLATION + TRANSMISSION ******/ int pqistreamer::queue_outpqi_locked(RsItem *pqi,uint32_t& pktsize) { @@ -354,7 +370,6 @@ int pqistreamer::queue_outpqi_locked(RsItem *pqi,uint32_t& pktsize) std::cerr << "pqistreamer::queue_outpqi() called." << std::endl; #endif - /* decide which type of packet it is */ pktsize = mRsSerialiser->size(pqi); @@ -362,7 +377,6 @@ int pqistreamer::queue_outpqi_locked(RsItem *pqi,uint32_t& pktsize) if(ptr == NULL) return 0 ; - #ifdef DEBUG_PQISTREAMER std::cerr << "pqistreamer::queue_outpqi() serializing packet with packet size : " << pktsize << std::endl; @@ -403,27 +417,31 @@ int pqistreamer::queue_outpqi_locked(RsItem *pqi,uint32_t& pktsize) return 1; // keep error internal. } -int pqistreamer::handleincomingitem_locked(RsItem *pqi,int len) +int pqistreamer::handleincomingitem(RsItem *pqi,int len) { #ifdef DEBUG_PQISTREAMER - pqioutput(PQL_DEBUG_ALL, pqistreamerzone, "pqistreamer::handleincomingitem_locked()"); + pqioutput(PQL_DEBUG_ALL, pqistreamerzone, "pqistreamer::handleincomingitem()"); #endif // timestamp last received packet. mLastIncomingTs = time(NULL); // Use overloaded Contact function pqi -> PeerId(PeerId()); - mIncoming.push_back(pqi); - ++mIncomingSize ; - /*******************************************************************************************/ - // keep info for stats for a while. Only keep the items for the last two seconds. sec n is ongoing and second n-1 - // is a full statistics chunk that can be used in the GUI + mIncoming.push_back(pqi); + ++mIncomingSize; + // for future use + // mIncomingSize_bytes += len; - locked_addTrafficClue(pqi,len,mCurrentStatsChunk_In) ; - - /*******************************************************************************************/ + /*******************************************************************************************/ + // keep info for stats for a while. Only keep the items for the last two seconds. sec n is ongoing and second n-1 + // is a full statistics chunk that can be used in the GUI + { + RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/ + locked_addTrafficClue(pqi,len,mCurrentStatsChunk_In) ; + } + /*******************************************************************************************/ return 1; } @@ -456,8 +474,8 @@ void pqistreamer::locked_addTrafficClue(const RsItem *pqi,uint32_t pktsize,std:: rstime_t pqistreamer::getLastIncomingTS() { - RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/ - + // This is the only case where another thread (rs main for pqiperson) will access our data + // Still a mutex lock is not needed because the operation is atomic return mLastIncomingTs; } @@ -693,23 +711,23 @@ int pqistreamer::handleoutgoing_locked() /* Handles reading from input stream. */ -int pqistreamer::handleincoming_locked() +int pqistreamer::handleincoming() { int readbytes = 0; static const int max_failed_read_attempts = 2000 ; #ifdef DEBUG_PQISTREAMER - pqioutput(PQL_DEBUG_ALL, pqistreamerzone, "pqistreamer::handleincoming_locked()"); + pqioutput(PQL_DEBUG_ALL, pqistreamerzone, "pqistreamer::handleincoming()"); #endif if(!(mBio->isactive())) { mReading_state = reading_state_initial ; - free_pend_locked(); + free_pend(); return 0; } else - allocate_rpend_locked(); + allocate_rpend(); // enough space to read any packet. uint32_t maxlen = mPkt_rpend_size; @@ -718,7 +736,7 @@ int pqistreamer::handleincoming_locked() // initial read size: basic packet. int blen = getRsPktBaseSize(); // this is valid for both packet slices and normal un-sliced packets (same header size) - int maxin = inAllowedBytes_locked(); + int maxin = inAllowedBytes(); #ifdef DEBUG_PQISTREAMER std::cerr << "[" << (void*)pthread_self() << "] " << "reading state = " << mReading_state << std::endl ; @@ -967,19 +985,19 @@ continue_packet: std::cerr << "Inputing partial packet " << RsUtil::BinToHex((char*)block,8) << std::endl; #endif uint32_t packet_length = 0 ; - pkt = addPartialPacket_locked(block,pktlen,slice_packet_id,is_packet_starting,is_packet_ending,packet_length) ; + pkt = addPartialPacket(block,pktlen,slice_packet_id,is_packet_starting,is_packet_ending,packet_length) ; pktlen = packet_length ; } else pkt = mRsSerialiser->deserialise(block, &pktlen); - if ((pkt != NULL) && (0 < handleincomingitem_locked(pkt,pktlen))) + if ((pkt != NULL) && (0 < handleincomingitem(pkt,pktlen))) { #ifdef DEBUG_PQISTREAMER pqioutput(PQL_DEBUG_BASIC, pqistreamerzone, "Successfully Read a Packet!"); #endif - inReadBytes_locked(pktlen); // only count deserialised packets, because that's what is actually been transfered. + inReadBytes(pktlen); // only count deserialised packets, because that's what is actually been transfered. } else if (!is_partial_packet) { @@ -1012,7 +1030,7 @@ continue_packet: return 0; } -RsItem *pqistreamer::addPartialPacket_locked(const void *block, uint32_t len, uint32_t slice_packet_id, bool is_packet_starting, bool is_packet_ending, uint32_t &total_len) +RsItem *pqistreamer::addPartialPacket(const void *block, uint32_t len, uint32_t slice_packet_id, bool is_packet_starting, bool is_packet_ending, uint32_t &total_len) { #ifdef DEBUG_PACKET_SLICING std::cerr << "Receiving partial packet. size=" << len << ", ID=" << std::hex << slice_packet_id << std::dec << ", starting:" << is_packet_starting << ", ending:" << is_packet_ending ; @@ -1134,7 +1152,7 @@ int pqistreamer::outAllowedBytes_locked() // low pass filter on mAvgDtOut mAvgDtOut = PQISTREAM_AVG_DT_FRAC * mAvgDtOut + (1 - PQISTREAM_AVG_DT_FRAC) * dt; - double maxout = getMaxRate(false) * 1024.0; + double maxout = getMaxRate_locked(false) * 1024.0; // this is used to take into account a possible excess of data sent during the previous round mCurrSent -= int(dt * maxout); @@ -1156,7 +1174,7 @@ int pqistreamer::outAllowedBytes_locked() return quota; } -int pqistreamer::inAllowedBytes_locked() +int pqistreamer::inAllowedBytes() { double t = getCurrentTS(); // in sec, with high accuracy @@ -1194,7 +1212,7 @@ int pqistreamer::inAllowedBytes_locked() #ifdef DEBUG_PQISTREAMER uint64_t t_now = 1000 * getCurrentTS(); - std::cerr << std::dec << t_now << " DEBUG_PQISTREAMER pqistreamer::inAllowedBytes_locked PeerId " << this->PeerId().toStdString() << " dt " << (int)(1000 * dt) << "ms, mAvgDtIn " << (int)(1000 * mAvgDtIn) << "ms, maxin " << (int)(maxin) << " bytes/s, mCurrRead " << mCurrRead << " bytes, quota " << (int)(quota) << " bytes" << std::endl; + std::cerr << std::dec << t_now << " DEBUG_PQISTREAMER pqistreamer::inAllowedBytes PeerId " << this->PeerId().toStdString() << " dt " << (int)(1000 * dt) << "ms, mAvgDtIn " << (int)(1000 * mAvgDtIn) << "ms, maxin " << (int)(maxin) << " bytes/s, mCurrRead " << mCurrRead << " bytes, quota " << (int)(quota) << " bytes" << std::endl; #endif return quota; @@ -1231,7 +1249,7 @@ void pqistreamer::outSentBytes_locked(uint32_t outb) return; } -void pqistreamer::inReadBytes_locked(uint32_t inb) +void pqistreamer::inReadBytes(uint32_t inb) { #ifdef DEBUG_PQISTREAMER { @@ -1248,7 +1266,7 @@ void pqistreamer::inReadBytes_locked(uint32_t inb) return; } -void pqistreamer::allocate_rpend_locked() +void pqistreamer::allocate_rpend() { if(mPkt_rpending) return; @@ -1271,17 +1289,17 @@ int pqistreamer::reset() #ifdef DEBUG_PQISTREAMER std::cerr << "pqistreamer::reset()" << std::endl; #endif - free_pend_locked(); + free_pend(); return 1 ; } -void pqistreamer::free_pend_locked() +void pqistreamer::free_pend() { if(mPkt_rpending) { #ifdef DEBUG_PQISTREAMER - std::cerr << "pqistreamer::free_pend_locked(): pending input packet buffer" << std::endl; + std::cerr << "pqistreamer::free_pend(): pending input packet buffer" << std::endl; #endif free(mPkt_rpending); mPkt_rpending = 0; @@ -1291,7 +1309,7 @@ void pqistreamer::free_pend_locked() if (mPkt_wpending) { #ifdef DEBUG_PQISTREAMER - std::cerr << "pqistreamer::free_pend_locked(): pending output packet buffer" << std::endl; + std::cerr << "pqistreamer::free_pend(): pending output packet buffer" << std::endl; #endif free(mPkt_wpending); mPkt_wpending = NULL; @@ -1300,7 +1318,7 @@ void pqistreamer::free_pend_locked() #ifdef DEBUG_PQISTREAMER if(!mPartialPackets.empty()) - std::cerr << "pqistreamer::free_pend_locked(): " << mPartialPackets.size() << " pending input partial packets" << std::endl; + std::cerr << "pqistreamer::free_pend(): " << mPartialPackets.size() << " pending input partial packets" << std::endl; #endif // also delete any incoming partial packet for(std::map::iterator it(mPartialPackets.begin());it!=mPartialPackets.end();++it) @@ -1318,26 +1336,47 @@ int pqistreamer::gatherStatistics(std::list& outqueue_lst,std return locked_gatherStatistics(outqueue_lst,inqueue_lst); } + +// this method is overloaded by pqiqosstreamer int pqistreamer::getQueueSize(bool in) { - RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/ - if (in) - return mIncomingSize; - else - return locked_out_queue_size(); +// no mutex is needed here because this is atomic + return mIncomingSize; + else + { + RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/ + return locked_out_queue_size(); + } +} + +int pqistreamer::getQueueSize_bytes(bool in) +{ + if (in) +// no mutex is needed here because this is atomic +// for future use, mIncomingSize_bytes is not updated yet + return mIncomingSize_bytes; + else + { + RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/ + return locked_compute_out_pkt_size(); + } } void pqistreamer::getRates(RsBwRates &rates) { RateInterface::getRates(rates); - RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/ +// no mutex is needed here because this is atomic + rates.mQueueIn = mIncomingSize; - rates.mQueueIn = mIncomingSize; - rates.mQueueOut = locked_out_queue_size(); + { + RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/ + rates.mQueueOut = locked_out_queue_size(); + } } +// this method is overloaded by pqiqosstreamer int pqistreamer::locked_out_queue_size() const { // Warning: because out_pkt is a list, calling size @@ -1347,6 +1386,7 @@ int pqistreamer::locked_out_queue_size() const return mOutPkts.size() ; } +// this method is overloaded by pqiqosstreamer void pqistreamer::locked_clear_out_queue() { for(std::list::iterator it = mOutPkts.begin(); it != mOutPkts.end(); ) @@ -1361,6 +1401,7 @@ void pqistreamer::locked_clear_out_queue() } } +// this method is overloaded by pqiqosstreamer int pqistreamer::locked_compute_out_pkt_size() const { int total = 0 ; @@ -1379,6 +1420,7 @@ int pqistreamer::locked_gatherStatistics(std::list& out_lst,std:: return 1 ; } +// this method is overloaded by pqiqosstreamer void *pqistreamer::locked_pop_out_data(uint32_t /*max_slice_size*/, uint32_t &size, bool &starts, bool &ends, uint32_t &packet_id) { size = 0 ; @@ -1400,4 +1442,3 @@ void *pqistreamer::locked_pop_out_data(uint32_t /*max_slice_size*/, uint32_t &si return res ; } - diff --git a/libretroshare/src/pqi/pqistreamer.h b/libretroshare/src/pqi/pqistreamer.h index c5b2bc983..0e7f6b815 100644 --- a/libretroshare/src/pqi/pqistreamer.h +++ b/libretroshare/src/pqi/pqistreamer.h @@ -38,8 +38,8 @@ class RsSerialiser; struct PartialPacketRecord { - void *mem ; - uint32_t size ; + void *mem ; + uint32_t size ; }; /** @@ -65,18 +65,23 @@ class pqistreamer: public PQInterface virtual RsItem *GetItem(); virtual int status(); - rstime_t getLastIncomingTS(); // Time of last data packet, for checking a connection is alive. + rstime_t getLastIncomingTS(); // Time of last data packet, for checking a connection is alive. virtual void getRates(RsBwRates &rates); virtual int getQueueSize(bool in); // extracting data. + virtual int getQueueSize_bytes(bool in); // size of incoming queue in bytes virtual int gatherStatistics(std::list& outqueue_stats,std::list& inqueue_stats); // extracting data. // mutex protected versions of RateInterface calls. virtual void setRate(bool b,float f) ; virtual void setMaxRate(bool b,float f) ; - virtual float getRate(bool b) ; + virtual void setMaxRate_locked(bool b,float f) ; - protected: - virtual int reset() ; + virtual float getRate(bool b) ; + virtual float getMaxRate(bool b) ; + virtual float getMaxRate_locked(bool b); + + protected: + virtual int reset() ; int tick_bio(); int tick_send(uint32_t timeout); @@ -104,12 +109,12 @@ class pqistreamer: public PQInterface private: int queue_outpqi_locked(RsItem *i,uint32_t& serialized_size); - int handleincomingitem_locked(RsItem *i, int len); + int handleincomingitem(RsItem *i, int len); // ticked regularly (manages out queues and sending // via above interfaces. virtual int handleoutgoing_locked(); - virtual int handleincoming_locked(); + virtual int handleincoming(); // Bandwidth/Streaming Management. float outTimeSlice_locked(); @@ -117,11 +122,11 @@ class pqistreamer: public PQInterface int outAllowedBytes_locked(); void outSentBytes_locked(uint32_t ); - int inAllowedBytes_locked(); - void inReadBytes_locked(uint32_t ); + int inAllowedBytes(); + void inReadBytes(uint32_t ); // cleans up everything that's pending / half finished. - void free_pend_locked(); + void free_pend(); // RsSerialiser - determines which packets can be serialised. RsSerialiser *mRsSerialiser; @@ -129,13 +134,12 @@ class pqistreamer: public PQInterface void *mPkt_wpending; // storage for pending packet to write. uint32_t mPkt_wpending_size; // ... and its size. - void allocate_rpend_locked(); // use these two functions to allocate/free the buffer below + void allocate_rpend(); // use these two functions to allocate/free the buffer below int mPkt_rpend_size; // size of pkt_rpending. void *mPkt_rpending; // storage for read in pending packets. - enum {reading_state_packet_started=1, - reading_state_initial=0 } ; + enum {reading_state_packet_started=1, reading_state_initial=0 } ; int mReading_state ; int mFailed_read_attempts ; @@ -144,7 +148,8 @@ class pqistreamer: public PQInterface std::list mOutPkts; // Cntrl / Search / Results queue std::list mIncoming; - uint32_t mIncomingSize; // size of mIncoming. To avoid calling linear cost std::list::size() + uint32_t mIncomingSize; // size of mIncoming. To avoid calling linear cost std::list::size() + uint32_t mIncomingSize_bytes; // size of Incoming in btyes // data for network stats. int mTotalRead; @@ -154,8 +159,8 @@ class pqistreamer: public PQInterface int mCurrRead; int mCurrSent; - double mCurrReadTS; // TS from which these are measured. - double mCurrSentTS; + double mCurrReadTS; // TS from which these are measured. + double mCurrSentTS; double mAvgLastUpdate; // TS from which these are measured. uint32_t mAvgReadCount; @@ -174,12 +179,12 @@ class pqistreamer: public PQInterface std::list mCurrentStatsChunk_Out ; rstime_t mStatisticsTimeStamp ; - bool mAcceptsPacketSlicing ; - rstime_t mLastSentPacketSlicingProbe ; - void locked_addTrafficClue(const RsItem *pqi, uint32_t pktsize, std::list &lst); - RsItem *addPartialPacket_locked(const void *block, uint32_t len, uint32_t slice_packet_id,bool packet_starting,bool packet_ending,uint32_t& total_len); + bool mAcceptsPacketSlicing ; + rstime_t mLastSentPacketSlicingProbe ; + void locked_addTrafficClue(const RsItem *pqi, uint32_t pktsize, std::list &lst); + RsItem *addPartialPacket(const void *block, uint32_t len, uint32_t slice_packet_id,bool packet_starting,bool packet_ending,uint32_t& total_len); - std::map mPartialPackets ; + std::map mPartialPackets ; }; #endif //MRK_PQI_STREAMER_HEADER diff --git a/libretroshare/src/pqi/pqithreadstreamer.cc b/libretroshare/src/pqi/pqithreadstreamer.cc index 857075ef3..6745dc864 100644 --- a/libretroshare/src/pqi/pqithreadstreamer.cc +++ b/libretroshare/src/pqi/pqithreadstreamer.cc @@ -23,17 +23,17 @@ #include "pqi/pqithreadstreamer.h" #include -#define DEFAULT_STREAMER_TIMEOUT 10000 // 10 ms. -#define DEFAULT_STREAMER_SLEEP 1000 // 1 ms. +#define DEFAULT_STREAMER_TIMEOUT 10000 // 10 ms +#define DEFAULT_STREAMER_SLEEP 30000 // 30 ms #define DEFAULT_STREAMER_IDLE_SLEEP 1000000 // 1 sec -//#define PQISTREAMER_DEBUG +// #define PQISTREAMER_DEBUG pqithreadstreamer::pqithreadstreamer(PQInterface *parent, RsSerialiser *rss, const RsPeerId& id, BinInterface *bio_in, int bio_flags_in) :pqistreamer(rss, id, bio_in, bio_flags_in), mParent(parent), mTimeout(0), mThreadMutex("pqithreadstreamer") { - mTimeout = DEFAULT_STREAMER_TIMEOUT; - mSleepPeriod = DEFAULT_STREAMER_SLEEP; + mTimeout = DEFAULT_STREAMER_TIMEOUT; + mSleepPeriod = DEFAULT_STREAMER_SLEEP; } bool pqithreadstreamer::RecvItem(RsItem *item) @@ -43,55 +43,59 @@ bool pqithreadstreamer::RecvItem(RsItem *item) int pqithreadstreamer::tick() { - RsStackMutex stack(mThreadMutex); - tick_bio(); + // pqithreadstreamer mutex lock is not needed here + // we will only check if the connection is active, and if not we will try to establish it + tick_bio(); return 0; } void pqithreadstreamer::threadTick() { - uint32_t recv_timeout = 0; - uint32_t sleep_period = 0; - bool isactive = false; - { - RsStackMutex stack(mStreamerMtx); - recv_timeout = mTimeout; - sleep_period = mSleepPeriod; - isactive = mBio->isactive(); - } + uint32_t recv_timeout = 0; + uint32_t sleep_period = 0; + bool isactive = false; + + { + RsStackMutex stack(mStreamerMtx); + recv_timeout = mTimeout; + sleep_period = mSleepPeriod; + isactive = mBio->isactive(); + } - updateRates() ; + // update the connection rates + updateRates() ; - if (!isactive) - { - rstime::rs_usleep(DEFAULT_STREAMER_IDLE_SLEEP); - return ; - } + // if the connection est not active, long sleep then return + if (!isactive) + { + rstime::rs_usleep(DEFAULT_STREAMER_IDLE_SLEEP); + return ; + } - { - RsStackMutex stack(mThreadMutex); - tick_recv(recv_timeout); - } + // fill incoming queue with items from SSL + { + RsStackMutex stack(mThreadMutex); + tick_recv(recv_timeout); + } - // Push Items, Outside of Mutex. - RsItem *incoming = NULL; - while((incoming = GetItem())) - { - RecvItem(incoming); - } + // move items to appropriate service queue or shortcut to fast service + RsItem *incoming = NULL; + while((incoming = GetItem())) + { + RecvItem(incoming); + } - { - RsStackMutex stack(mThreadMutex); - tick_send(0); - } + // parse the outgoing queue and send items to SSL + { + RsStackMutex stack(mThreadMutex); + tick_send(0); + } - if (sleep_period) - { - rstime::rs_usleep(sleep_period); - } + // sleep + if (sleep_period) + { + rstime::rs_usleep(sleep_period); + } } - - - diff --git a/libretroshare/src/retroshare/rsevents.h b/libretroshare/src/retroshare/rsevents.h index 090368910..2498a7712 100644 --- a/libretroshare/src/retroshare/rsevents.h +++ b/libretroshare/src/retroshare/rsevents.h @@ -3,7 +3,9 @@ * * * libretroshare: retroshare core library * * * - * Copyright (C) 2019 Gioacchino Mazzurco * + * Copyright (C) 2019-2020 Retroshare Team * + * Copyright (C) 2019-2020 Gioacchino Mazzurco * + * Copyright (C) 2020 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -31,6 +33,7 @@ #include "serialiser/rsserializable.h" #include "serialiser/rstypeserializer.h" #include "util/rstime.h" +#include "util/rsdebug.h" class RsEvents; @@ -50,7 +53,7 @@ extern RsEvents* rsEvents; */ enum class RsEventType : uint32_t { - NONE = 0, /// Used to detect uninitialized event + __NONE = 0, /// Used internally to detect invalid event type passed /// @see RsBroadcastDiscovery BROADCAST_DISCOVERY = 1, @@ -64,7 +67,7 @@ enum class RsEventType : uint32_t /// @see pqissl PEER_CONNECTION = 4, - /// @see RsGxsChanges // this one is used in RsGxsBroadcast + /// @see RsGxsChanges, used also in @see RsGxsBroadcast GXS_CHANGES = 5, /// Emitted when a peer state changes, @see RsPeers @@ -94,9 +97,62 @@ enum class RsEventType : uint32_t /// @see RsFiles FILE_TRANSFER = 14, - MAX /// Used to detect invalid event type passed + /// @see RsMsgs + CHAT_MESSAGE = 15, + + __MAX /// Used internally, keep last }; +enum class RsEventsErrorNum : int32_t +{ + EVENT_TYPE_UNDEFINED = 3004, + EVENT_TYPE_OUT_OF_RANGE = 3005, + INVALID_HANDLER_ID = 3006, + NULL_EVENT_POINTER = 3007 +}; + +struct RsEventsErrorCategory: std::error_category +{ + const char* name() const noexcept override + { return "RetroShare Events"; } + + std::string message(int ev) const override + { + switch (static_cast(ev)) + { + case RsEventsErrorNum::EVENT_TYPE_UNDEFINED: + return "Undefined event type"; + case RsEventsErrorNum::EVENT_TYPE_OUT_OF_RANGE: + return "Event type out of range"; + case RsEventsErrorNum::INVALID_HANDLER_ID: + return "Invalid handler id"; + default: + return rsErrorNotInCategory(ev, name()); + } + } + + std::error_condition default_error_condition(int ev) const noexcept override; + + const static RsEventsErrorCategory instance; +}; + + +namespace std +{ +/** Register RsJsonApiErrorNum as an error condition enum, must be in std + * namespace */ +template<> struct is_error_condition_enum : true_type {}; +} + +/** Provide RsEventsErrorNum conversion to std::error_condition, must be in + * same namespace of RsJsonApiErrorNum */ +inline std::error_condition make_error_condition(RsEventsErrorNum e) noexcept +{ + return std::error_condition( + static_cast(e), RsEventsErrorCategory::instance ); +}; + + /** * This struct is not meant to be used directly, you should create events type * deriving from it. @@ -104,7 +160,7 @@ enum class RsEventType : uint32_t struct RsEvent : RsSerializable { protected: - RsEvent(RsEventType type) : + explicit RsEvent(RsEventType type) : mType(type), mTimePoint(std::chrono::system_clock::now()) {} RsEvent() = delete; @@ -139,27 +195,19 @@ public: /** * @brief Post event to the event queue. * @param[in] event - * @param[out] errorMessage Optional storage for error messsage, meaningful - * only on failure. - * @return False on error, true otherwise. + * @return Success or error details. */ - virtual bool postEvent( - std::shared_ptr event, - std::string& errorMessage = RS_DEFAULT_STORAGE_PARAM(std::string) - ) = 0; + virtual std::error_condition postEvent( + std::shared_ptr event ) = 0; /** * @brief Send event directly to handlers. Blocking API * The handlers get exectuded on the caller thread. * @param[in] event - * @param[out] errorMessage Optional storage for error messsage, meaningful - * only on failure. - * @return False on error, true otherwise. + * @return Success or error details. */ - virtual bool sendEvent( - std::shared_ptr event, - std::string& errorMessage = RS_DEFAULT_STORAGE_PARAM(std::string) - ) = 0; + virtual std::error_condition sendEvent( + std::shared_ptr event ) = 0; /** * @brief Generate unique handler identifier @@ -171,8 +219,9 @@ public: * @brief Register events handler * Every time an event is dispatced the registered events handlers will get * their method handleEvent called with the event passed as paramether. + * @attention Callbacks must not fiddle internally with methods of this + * class otherwise a deadlock will happen. * @jsonapi{development,manualwrapper} - * @param eventType Type of event for which the callback is called * @param multiCallback Function that will be called each time an event * is dispatched. * @param[inout] hId Optional storage for handler id, useful to @@ -180,21 +229,23 @@ public: * value may be provided to the function call but * must habe been generated with * @see generateUniqueHandlerId() - * @return False on error, true otherwise. + * @param[in] eventType Optional type of event for which the callback is + * called, if __NONE is passed multiCallback is + * called for every events without filtering. + * @return Success or error details. */ - virtual bool registerEventsHandler( - RsEventType eventType, + virtual std::error_condition registerEventsHandler( std::function)> multiCallback, - RsEventsHandlerId_t& hId = RS_DEFAULT_STORAGE_PARAM(RsEventsHandlerId_t, 0) - ) = 0; + RsEventsHandlerId_t& hId = RS_DEFAULT_STORAGE_PARAM(RsEventsHandlerId_t, 0), + RsEventType eventType = RsEventType::__NONE ) = 0; /** * @brief Unregister event handler * @param[in] hId Id of the event handler to unregister - * @return True if the handler id has been found, false otherwise. + * @return Success or error details. */ - virtual bool unregisterEventsHandler(RsEventsHandlerId_t hId) = 0; + virtual std::error_condition unregisterEventsHandler( + RsEventsHandlerId_t hId ) = 0; virtual ~RsEvents(); }; - diff --git a/libretroshare/src/retroshare/rsfiles.h b/libretroshare/src/retroshare/rsfiles.h index 3818a8f45..fa6b37e29 100644 --- a/libretroshare/src/retroshare/rsfiles.h +++ b/libretroshare/src/retroshare/rsfiles.h @@ -4,7 +4,8 @@ * libretroshare: retroshare core library * * * * Copyright (C) 2008 Robert Fernie * - * Copyright (C) 2018-2019 Gioacchino Mazzurco * + * Copyright (C) 2018-2020 Gioacchino Mazzurco * + * Copyright (C) 2019-2020 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -28,12 +29,15 @@ #include #include #include +#include #include "rstypes.h" #include "serialiser/rsserializable.h" #include "rsturtle.h" #include "util/rstime.h" #include "retroshare/rsevents.h" +#include "util/rsmemory.h" +#include "util/rsdebug.h" class RsFiles; @@ -43,6 +47,49 @@ class RsFiles; */ extern RsFiles* rsFiles; +enum class RsFilesErrorNum : int32_t +{ + FILES_HANDLE_NOT_FOUND = 2004, +}; + +struct RsFilesErrorCategory: std::error_category +{ + const char* name() const noexcept override + { return "RetroShare Files"; } + + std::string message(int ev) const override + { + switch (static_cast(ev)) + { + case RsFilesErrorNum::FILES_HANDLE_NOT_FOUND: + return "Files handle not found"; + default: + return rsErrorNotInCategory(ev, name()); + } + } + + std::error_condition default_error_condition(int ev) const noexcept override; + + const static RsFilesErrorCategory instance; +}; + + +namespace std +{ +/** Register RsFilesErrorNum as an error condition enum, must be in std + * namespace */ +template<> struct is_error_condition_enum : true_type {}; +} + +/** Provide RsJsonApiErrorNum conversion to std::error_condition, must be in + * same namespace of RsJsonApiErrorNum */ +inline std::error_condition make_error_condition(RsFilesErrorNum e) noexcept +{ + return std::error_condition( + static_cast(e), RsFilesErrorCategory::instance ); +}; + + namespace RsRegularExpression { class Expression; } /* These are used mainly by ftController at the moment */ @@ -96,8 +143,28 @@ const FileSearchFlags RS_FILE_HINTS_BROWSABLE ( 0x00000100 );// bro const FileSearchFlags RS_FILE_HINTS_SEARCHABLE ( 0x00000200 );// can be searched anonymously const FileSearchFlags RS_FILE_HINTS_PERMISSION_MASK ( 0x00000380 );// OR of the last tree flags. Used to filter out. -// Flags used when requesting a transfer -// +/** Flags used when requesting a new file transfer */ +enum class FileRequestFlags: uint32_t +{ + /// Enable requesting file via turtle routing. + ANONYMOUS_ROUTING = 0x00000040, + + /// Asks (TODO: or enforce?) for end-to-end encryption of file trasfer + ENCRYPTED = 0x00000080, + + /// Asks (TODO: or enforce?) no end-to-end encryption of file trasfer + UNENCRYPTED = 0x00000100, + + /// Start trasfer very slow + SLOW = 0x00002000, + + /// Disable searching for potential direct sources + NO_DIRECT_SEARCH = 0x02000000 +}; +RS_REGISTER_ENUM_FLAGS_TYPE(FileRequestFlags) + +/// @deprecated Flags used when requesting a transfer +/// @see FileRequestFlags instead const TransferRequestFlags RS_FILE_REQ_ANONYMOUS_ROUTING ( 0x00000040 ); // Use to ask turtle router to download the file. const TransferRequestFlags RS_FILE_REQ_ENCRYPTED ( 0x00000080 ); // Asks for end-to-end encryption of file at the level of ftServer const TransferRequestFlags RS_FILE_REQ_UNENCRYPTED ( 0x00000100 ); // Asks for no end-to-end encryption of file at the level of ftServer @@ -111,10 +178,6 @@ const TransferRequestFlags RS_FILE_REQ_NO_SEARCH ( 0x02000000 ); // di // const uint32_t RS_FILE_HINTS_SHARE_FLAGS_MASK = RS_FILE_HINTS_NETWORK_WIDE_OTHERS | RS_FILE_HINTS_BROWSABLE_OTHERS // | RS_FILE_HINTS_NETWORK_WIDE_GROUPS | RS_FILE_HINTS_BROWSABLE_GROUPS ; -/* Callback Codes */ - -const uint32_t RS_FILE_EXTRA_DELETE = 0x0010; - enum class RsSharedDirectoriesEventCode: uint8_t { UNKNOWN = 0x00, STARTING_DIRECTORY_SWEEP = 0x01, // (void) @@ -202,36 +265,117 @@ struct SharedDirStats uint64_t total_shared_size ; }; -// This class represents a tree of directories and files, only with their names size and hash. It is used to create collection links in the GUI -// and to transmit directory information between services. This class is independent from the existing FileHierarchy classes used in storage because -// we need a very copact serialization and storage size since we create links with it. Besides, we cannot afford to risk the leak of other local information -// by using the orignal classes. - -class FileTree +/** This class represents a tree of directories and files, only with their names + * size and hash. It is used to create collection links in the GUI and to + * transmit directory information between services. This class is independent + * from the existing FileHierarchy classes used in storage because we need a + * very copact serialization and storage size since we create links with it. + * Besides, we cannot afford to risk the leak of other local information + * by using the orignal classes. + */ +struct RsFileTree : RsSerializable { public: - virtual ~FileTree() {} + RsFileTree() : mTotalFiles(0), mTotalSize(0) {} - static FileTree *create(const DirDetails& dd, bool remote, bool remove_top_dirs = true) ; - static FileTree *create(const std::string& radix64_string) ; + /** + * @brief Create a RsFileTree from directory details + * @param dd directory or file details + * @param remote + * @param remove_top_dirs + * @return pointer to the created file-tree + */ + static std::unique_ptr fromDirDetails( + const DirDetails& dd, bool remote, bool remove_top_dirs = true ); - virtual std::string toRadix64() const =0 ; + /** + * @brief Create a RsFileTree from Base64 representation + * @param base64 base64 or base64url string representation of the file-tree + * @return pointer to the parsed file-tree on success, nullptr plus error + * details on failure + */ + static std::tuple, std::error_condition> + fromBase64(const std::string& base64); - // These methods allow the user to browse the hierarchy + /** @brief Convert to base64 representetion */ + std::string toBase64() const; - struct FileData { - std::string name ; - uint64_t size ; - RsFileHash hash ; + /// @see RsSerializable + virtual void serial_process( + RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx ) + { + RS_SERIAL_PROCESS(mFiles); + RS_SERIAL_PROCESS(mDirs); + RS_SERIAL_PROCESS(mTotalFiles); + RS_SERIAL_PROCESS(mTotalSize); + } + + struct FileData: RsSerializable + { + std::string name; + uint64_t size; + RsFileHash hash; + + void serial_process( + RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx ) override; }; - virtual uint32_t root() const { return 0;} - virtual bool getDirectoryContent(uint32_t index,std::string& name,std::vector& subdirs,std::vector& subfiles) const = 0; + /// Allow browsing the hierarchy + bool getDirectoryContent( + std::string& name, std::vector& subdirs, + std::vector& subfiles, uint64_t handle = 0 ) const; - virtual void print() const=0; + struct DirData: RsSerializable + { + std::string name; + std::vector subdirs; + std::vector subfiles; - uint32_t mTotalFiles ; - uint64_t mTotalSize ; + void serial_process( + RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx ) override; + }; + + static void recurs_buildFileTree( + RsFileTree& ft, uint32_t index, const DirDetails& dd, bool remote, + bool remove_top_dirs ); + + std::vector mFiles; + std::vector mDirs; + + uint32_t mTotalFiles; + uint64_t mTotalSize; + + virtual ~RsFileTree(); + + /** + * @brief Create a RsFileTree from Radix64 representation + * @deprecated kept for retrocompatibility with RetroShare-gui + * The format is not compatible with the new methods + * @param radix64_string + * @return nullptr if on failure, pointer to the created FileTree on success + */ + RS_DEPRECATED_FOR(fromBase64) + static std::unique_ptr fromRadix64( + const std::string& radix64_string ); + + /** @brief Convert to radix64 representetion + * @deprecated kept for retrocompatibility with RetroShare-gui + * The format is not compatible with the new methods + */ + RS_DEPRECATED_FOR(toBase64) + std::string toRadix64() const; + +private: + /** @deprecated kept for retrocompatibility with RetroShare-gui */ + RS_DEPRECATED_FOR(serial_process) + bool serialise(unsigned char *& data,uint32_t& data_size) const; + + /** @deprecated kept for retrocompatibility with RetroShare-gui */ + RS_DEPRECATED_FOR(serial_process) + bool deserialise(unsigned char* data, uint32_t data_size); }; struct BannedFileEntry : RsSerializable @@ -258,12 +402,12 @@ struct TurtleFileInfoV2 : RsSerializable { TurtleFileInfoV2() : fSize(0), fWeight(0) {} - TurtleFileInfoV2(const TurtleFileInfo& oldInfo) : + explicit TurtleFileInfoV2(const TurtleFileInfo& oldInfo) : fSize(oldInfo.size), fHash(oldInfo.hash), fName(oldInfo.name), fWeight(0) {} #ifdef RS_DEEP_FILES_INDEX - TurtleFileInfoV2(const DeepFilesSearchResult& dRes); + explicit TurtleFileInfoV2(const DeepFilesSearchResult& dRes); #endif // def RS_DEEP_FILES_INDEX uint64_t fSize; /// File size @@ -301,9 +445,6 @@ struct TurtleFileInfoV2 : RsSerializable class RsFiles { public: - RsFiles() {} - virtual ~RsFiles() {} - /** * @brief Provides file data for the GUI, media streaming or API clients. * It may return unverified chunks. This allows streaming without having to @@ -344,10 +485,10 @@ public: /** * @brief Initiate downloading of a file * @jsonapi{development} - * @param[in] fileName - * @param[in] hash - * @param[in] size - * @param[in] destPath in not empty specify a destination path + * @param[in] fileName file name + * @param[in] hash file hash + * @param[in] size file size + * @param[in] destPath optional specify the destination directory * @param[in] flags you usually want RS_FILE_REQ_ANONYMOUS_ROUTING * @param[in] srcIds eventually specify known sources * @return false if we already have the file, true otherwhise @@ -357,6 +498,25 @@ public: const std::string& destPath, TransferRequestFlags flags, const std::list& srcIds ) = 0; + /** + * @brief Initiate download of a files collection + * @jsonapi{development} + * An usually useful companion method of this is @see parseFilesLink() + * @param[in] collection collection of files to download + * @param[in] destPath optional base path on which to download the + * collection, if left empty the default download directory will be used + * @param[in] srcIds optional peers id known as direct source of the + * collection + * @param[in] flags optional flags to fine tune search and download + * algorithm + * @return success or error details. + */ + virtual std::error_condition requestFiles( + const RsFileTree& collection, + const std::string& destPath = "", + const std::vector& srcIds = std::vector(), + FileRequestFlags flags = FileRequestFlags::ANONYMOUS_ROUTING ) = 0; + /** * @brief Cancel file downloading * @jsonapi{development} @@ -498,7 +658,8 @@ public: * @brief Get file details * @jsonapi{development} * @param[in] hash file identifier - * @param[in] hintflags filtering hint (RS_FILE_HINTS_EXTRA|...|RS_FILE_HINTS_LOCAL) + * @param[in] hintflags filtering hint ( RS_FILE_HINTS_UPLOAD|...| + * RS_FILE_HINTS_EXTRA|RS_FILE_HINTS_LOCAL ) * @param[out] info storage for file information * @return true if file found, false otherwise */ @@ -575,7 +736,7 @@ public: * @return false if error occurred, true otherwise */ virtual bool requestDirDetails( - DirDetails &details, std::uintptr_t handle = 0, + DirDetails &details, uint64_t handle = 0, FileSearchFlags flags = RS_FILE_HINTS_LOCAL ) = 0; /*** @@ -584,8 +745,9 @@ public: /** * Kept for retrocompatibility, it was originally written for easier * interaction with Qt. As soon as you can, you should prefer to use the - * version of this methodn which take `std::uintptr_t handle` as paramether. + * version of this method which take `uint64_t handle` as paramether. */ + RS_DEPRECATED_FOR(requestDirDetails) virtual int RequestDirDetails( void* handle, DirDetails& details, FileSearchFlags flags ) = 0; @@ -726,6 +888,76 @@ public: */ virtual bool removeSharedDirectory(std::string dir) = 0; + /// Default base URL used for files links @see exportFilesLink + static const std::string DEFAULT_FILES_BASE_URL; + + /** Link query field used to store collection files count + * @see exportFilesLink */ + static const std::string FILES_URL_COUNT_FIELD; + + /// Link query field used to store collection data @see exportFilesLink + static const std::string FILES_URL_DATA_FIELD; + + /** Link query FILES_URL_DATA_FIELD field value used to instruct the parser + * to look for the data into the link fragment + * @see exportFilesLink and parseFilesLink */ + static const std::string FILES_URL_FAGMENT_FORWARD; + + /// Link query field used to store collection name @see exportFilesLink + static const std::string FILES_URL_NAME_FIELD; + + /// Link query field used to store collection size @see exportFilesLink + static const std::string FILES_URL_SIZE_FIELD; + + /** + * @brief Export link to a collection of files + * @jsonapi{development} + * @param[out] link storage for the generated link + * @param[in] handle directory RetroShare handle + * @param[in] fragSneak when true the file data is sneaked into fragment + * instead of FILES_URL_DATA_FIELD query field, this way if using an + * http[s] link to pass around a disguised file link a misconfigured host + * attempting to visit that link with a web browser will not send the file + * data to the server thus protecting at least some of the privacy of the + * user even in a misconfiguration scenario. + * @param[in] baseUrl URL into which to sneak in the RetroShare file link + * base64, this is primarly useful to induce applications into making the + * link clickable, or to disguise the RetroShare link into a "normal" + * looking web link. If empty the collection data link will be outputted in + * plain base64 format. + * @return error information if some error occurred, 0/SUCCESS otherwise + */ + virtual std::error_condition exportCollectionLink( + std::string& link, uint64_t handle, bool fragSneak = false, + const std::string& baseUrl = RsFiles::DEFAULT_FILES_BASE_URL ) = 0; + + /** + * @brief Export link to a file + * @jsonapi{development} + * @param[out] link @see exportCollectionLink + * @param[in] fileHash hash of the file + * @param[in] fileSize size of the file + * @param[in] fileName name of the file + * @param[in] fragSneak @see exportCollectionLink + * @param[in] baseUrl @see exportCollectionLink + * @return error @see exportCollectionLink + */ + virtual std::error_condition exportFileLink( + std::string& link, const RsFileHash& fileHash, uint64_t fileSize, + const std::string& fileName, bool fragSneak = false, + const std::string& baseUrl = RsFiles::DEFAULT_FILES_BASE_URL ) = 0; + + /** + * @brief Parse RetroShare files link + * @jsonapi{development} + * Support also old RetroShare-gui file and collections links format. + * @param[in] link files link either in base64 or URL format + * @param[out] collection storage for parsed files link + * @return error information if some error occurred, 0/SUCCESS otherwise + */ + virtual std::error_condition parseFilesLink( + const std::string& link, RsFileTree& collection ) = 0; + /** * @brief Get list of ignored file name prefixes and suffixes * @param[out] ignoredPrefixes storage for ingored prefixes @@ -757,4 +989,6 @@ public: virtual bool ignoreDuplicates() = 0; virtual void setIgnoreDuplicates(bool ignore) = 0; + + virtual ~RsFiles() = default; }; diff --git a/libretroshare/src/retroshare/rsflags.h b/libretroshare/src/retroshare/rsflags.h index 8adcc8e4f..8775f1dfb 100644 --- a/libretroshare/src/retroshare/rsflags.h +++ b/libretroshare/src/retroshare/rsflags.h @@ -24,6 +24,7 @@ #include #include +#include /** Check if given type is a scoped enum */ template @@ -135,13 +136,7 @@ typename std::enable_if::enabled, std::ostream>::type& operator <<(std::ostream& stream, EFT flags) { using u_t = typename std::underlying_type::type; - - for(int i = sizeof(u_t) << 3; i>=0; --i) - { - stream << ((static_cast(flags) & ( 1 << i )) ? "1" : "0"); - if( i % 8 == 0 ) stream << " "; - } - return stream; + return stream << std::bitset(static_cast(flags)); } #include @@ -177,6 +172,7 @@ template class RS_DEPRECATED_FOR(RS_REGISTER_ENUM_FLAGS_TYPE) t_RsFlags32 inline t_RsFlags32() : _bits(0) {} inline explicit t_RsFlags32(uint32_t N) : _bits(N) {} // allows initialization from a set of uint32_t + inline t_RsFlags32 operator| (const t_RsFlags32& f) const { return t_RsFlags32(_bits | f._bits) ; } inline t_RsFlags32 operator^ (const t_RsFlags32& f) const { return t_RsFlags32(_bits ^ f._bits) ; } inline t_RsFlags32 operator* (const t_RsFlags32& f) const { return t_RsFlags32(_bits & f._bits) ; } @@ -194,6 +190,19 @@ template class RS_DEPRECATED_FOR(RS_REGISTER_ENUM_FLAGS_TYPE) t_RsFlags32 //inline explicit operator bool() const { return _bits>0; } inline uint32_t toUInt32() const { return _bits ; } + /// Easier porting to new flag system + template inline + typename std::enable_if<(Rs__BitFlagsOps::enabled && + sizeof(EFT) >= sizeof(uint32_t) ), EFT>::type + toEFT() { return static_cast(_bits); } + + /// Easier porting to new flag system + template + static inline typename std::enable_if< + Rs__BitFlagsOps::enabled && + sizeof(EFT) <= sizeof(uint32_t), t_RsFlags32>::type + fromEFT(EFT e) { return t_RsFlags32(static_cast(e)); } + void clear() { _bits = 0 ; } friend std::ostream& operator<<(std::ostream& o,const t_RsFlags32& f) // friendly print with 0 and I @@ -206,8 +215,10 @@ template class RS_DEPRECATED_FOR(RS_REGISTER_ENUM_FLAGS_TYPE) t_RsFlags32 } return o ; } - private: - uint32_t _bits ; + +private: + friend struct RsTypeSerializer; + uint32_t _bits; }; #define FLAGS_TAG_TRANSFER_REQS 0x4228af @@ -217,9 +228,9 @@ template class RS_DEPRECATED_FOR(RS_REGISTER_ENUM_FLAGS_TYPE) t_RsFlags32 #define FLAGS_TAG_SERVICE_CHAT 0x839042 #define FLAGS_TAG_SERIALIZER 0xa0338d -// Flags for requesting transfers, ask for turtle, cache, speed, etc. -// -typedef t_RsFlags32 TransferRequestFlags ; +/// @deprecated Flags for requesting transfers, ask for turtle, cache, speed, etc. +RS_DEPRECATED_FOR(FileRequestFlags) +typedef t_RsFlags32 TransferRequestFlags; // Flags for file storage. Mainly permissions like BROWSABLE/NETWORK_WIDE for groups and peers. // @@ -237,7 +248,3 @@ typedef t_RsFlags32 ServicePermissionFlags ; // typedef t_RsFlags32 ChatLobbyFlags ; -// Flags for serializer -// -typedef t_RsFlags32 SerializationFlags ; - diff --git a/libretroshare/src/retroshare/rsgxschannels.h b/libretroshare/src/retroshare/rsgxschannels.h index 615ff30f7..106147dc6 100644 --- a/libretroshare/src/retroshare/rsgxschannels.h +++ b/libretroshare/src/retroshare/rsgxschannels.h @@ -4,7 +4,8 @@ * libretroshare: retroshare core library * * * * Copyright (C) 2012 Robert Fernie * - * Copyright (C) 2018-2019 Gioacchino Mazzurco * + * Copyright (C) 2018-2020 Gioacchino Mazzurco * + * Copyright (C) 2019-2020 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -45,11 +46,10 @@ class RsGxsChannels; extern RsGxsChannels* rsGxsChannels; -struct RsGxsChannelGroup : RsSerializable +struct RsGxsChannelGroup : RsSerializable, RsGxsGenericGroupData { RsGxsChannelGroup() : mAutoDownload(false) {} - RsGroupMetaData mMeta; std::string mDescription; RsGxsImage mImage; @@ -69,12 +69,10 @@ struct RsGxsChannelGroup : RsSerializable ~RsGxsChannelGroup() override; }; -struct RsGxsChannelPost : RsSerializable +struct RsGxsChannelPost : RsSerializable, RsGxsGenericMsgData { RsGxsChannelPost() : mCount(0), mSize(0) {} - RsMsgMetaData mMeta; - std::set mOlderVersions; std::string mMsg; // UTF8 encoded. @@ -114,18 +112,16 @@ enum class RsChannelEventCode: uint8_t SUBSCRIBE_STATUS_CHANGED = 0x06, // subscription for channel mChannelGroupId changed. READ_STATUS_CHANGED = 0x07, // existing message has been read or set to unread RECEIVED_DISTANT_SEARCH_RESULT = 0x08, // result for the given group id available for the given turtle request id + STATISTICS_CHANGED = 0x09, // stats (nb of supplier friends, how many msgs they have etc) has changed }; struct RsGxsChannelEvent: RsEvent { - RsGxsChannelEvent(): - RsEvent(RsEventType::GXS_CHANNELS), - mChannelEventCode(RsChannelEventCode::UNKNOWN) {} + RsGxsChannelEvent(): RsEvent(RsEventType::GXS_CHANNELS), mChannelEventCode(RsChannelEventCode::UNKNOWN) {} RsChannelEventCode mChannelEventCode; RsGxsGroupId mChannelGroupId; RsGxsMessageId mChannelMsgId; - TurtleRequestId mDistantSearchRequestId; ///* @see RsEvent @see RsSerializable void serial_process( RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) override @@ -135,8 +131,28 @@ struct RsGxsChannelEvent: RsEvent RS_SERIAL_PROCESS(mChannelEventCode); RS_SERIAL_PROCESS(mChannelGroupId); RS_SERIAL_PROCESS(mChannelMsgId); - RS_SERIAL_PROCESS(mDistantSearchRequestId); - } + } +}; + +// This event is used to factor multiple search results notifications in a single event. + +struct RsGxsChannelSearchResultEvent: RsEvent +{ + RsGxsChannelSearchResultEvent(): + RsEvent(RsEventType::GXS_CHANNELS), + mChannelEventCode(RsChannelEventCode::RECEIVED_DISTANT_SEARCH_RESULT) {} + + RsChannelEventCode mChannelEventCode; + std::map > mSearchResultsMap; + + ///* @see RsEvent @see RsSerializable + void serial_process( RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) override + { + RsEvent::serial_process(j, ctx); + + RS_SERIAL_PROCESS(mChannelEventCode); + RS_SERIAL_PROCESS(mSearchResultsMap); + } }; class RsGxsChannels: public RsGxsIfaceHelper, public RsGxsCommentService @@ -285,26 +301,6 @@ public: */ virtual bool ExtraFileRemove(const RsFileHash& hash) = 0; - /** - * @brief Get auto-download option value for given channel - * @jsonapi{development} - * @param[in] channelId channel id - * @param[out] enabled storage for the auto-download option value - * @return false if something failed, true otherwhise - */ - virtual bool getChannelAutoDownload( - const RsGxsGroupId& channelId, bool& enabled ) = 0; - - /** - * @brief Get download directory for the given channel - * @jsonapi{development} - * @param[in] channelId id of the channel - * @param[out] directory reference to string where to store the path - * @return false on error, true otherwise - */ - virtual bool getChannelDownloadDirectory( const RsGxsGroupId& channelId, - std::string& directory ) = 0; - /** * @brief Get channels summaries list. Blocking API. * @jsonapi{development} @@ -326,18 +322,50 @@ public: std::vector& channelsInfo ) = 0; /** - * @brief Get channel contents + * @brief Get all channel messages and comments in a given channel + * @jsonapi{development} + * @param[in] channelId id of the channel of which the content is requested + * @param[out] posts storage for posts + * @param[out] comments storage for the comments + * @param[out] votes storage for votes + * @return false if something failed, true otherwhise + */ + virtual bool getChannelAllContent( const RsGxsGroupId& channelId, + std::vector& posts, + std::vector& comments, + std::vector& votes ) = 0; + + /** + * @brief Get channel messages and comments corresponding to the given IDs. + * If the set is empty, nothing is returned. + * @note Since comments are internally themselves messages, it is possible + * to get comments only by supplying their IDs. * @jsonapi{development} * @param[in] channelId id of the channel of which the content is requested * @param[in] contentsIds ids of requested contents * @param[out] posts storage for posts * @param[out] comments storage for the comments + * @param[out] votes storage for the votes * @return false if something failed, true otherwhise */ virtual bool getChannelContent( const RsGxsGroupId& channelId, - const std::set& contentsIds, - std::vector& posts, - std::vector& comments ) = 0; + const std::set& contentsIds, + std::vector& posts, + std::vector& comments, + std::vector& votes ) = 0; + + /** + * @brief Get channel comments corresponding to the given IDs. + * If the set is empty, nothing is returned. + * @jsonapi{development} + * @param[in] channelId id of the channel of which the content is requested + * @param[in] contentIds ids of requested contents + * @param[out] comments storage for the comments + * @return false if something failed, true otherwhise + */ + virtual bool getChannelComments(const RsGxsGroupId &channelId, + const std::set &contentIds, + std::vector &comments) = 0; /** * @brief Get channel content summaries @@ -358,16 +386,6 @@ public: */ virtual bool markRead(const RsGxsGrpMsgIdPair& postId, bool read) = 0; - /** - * @brief Enable or disable auto-download for given channel. Blocking API - * @jsonapi{development} - * @param[in] channelId channel id - * @param[in] enable true to enable, false to disable - * @return false if something failed, true otherwhise - */ - virtual bool setChannelAutoDownload( - const RsGxsGroupId& channelId, bool enable ) = 0; - /** * @brief Share channel publishing key * This can be used to authorize other peers to post on the channel @@ -379,16 +397,6 @@ public: virtual bool shareChannelKeys( const RsGxsGroupId& channelId, const std::set& peers ) = 0; - /** - * @brief Set download directory for the given channel. Blocking API. - * @jsonapi{development} - * @param[in] channelId id of the channel - * @param[in] directory path - * @return false on error, true otherwise - */ - virtual bool setChannelDownloadDirectory( - const RsGxsGroupId& channelId, const std::string& directory) = 0; - /** * @brief Subscrbe to a channel. Blocking API * @jsonapi{development} @@ -399,46 +407,22 @@ public: virtual bool subscribeToChannel( const RsGxsGroupId& channelId, bool subscribe ) = 0; - /** - * @brief Request remote channels search + /** + * \brief Retrieve statistics about the channel service * @jsonapi{development} - * @param[in] matchString string to look for in the search - * @param multiCallback function that will be called each time a search - * result is received - * @param[in] maxWait maximum wait time in seconds for search results - * @return false on error, true otherwise - */ - virtual bool turtleSearchRequest( - const std::string& matchString, - const std::function& multiCallback, - rstime_t maxWait = 300 ) = 0; + * \param[out] stat Statistics structure + * \return + */ + virtual bool getChannelServiceStatistics(GxsServiceStatistic& stat) =0; - /** - * @brief Request remote channel + /** + * \brief Retrieve statistics about the given channel * @jsonapi{development} - * @param[in] channelId id of the channel to request to distants peers - * @param multiCallback function that will be called each time a result is - * received - * @param[in] maxWait maximum wait time in seconds for search results - * @return false on error, true otherwise - */ - virtual bool turtleChannelRequest( - const RsGxsGroupId& channelId, - const std::function& multiCallback, - rstime_t maxWait = 300 ) = 0; - - /** - * @brief Search local channels - * @jsonapi{development} - * @param[in] matchString string to look for in the search - * @param multiCallback function that will be called for each result - * @param[in] maxWait maximum wait time in seconds for search results - * @return false on error, true otherwise - */ - virtual bool localSearchRequest( - const std::string& matchString, - const std::function& multiCallback, - rstime_t maxWait = 30 ) = 0; + * \param[in] channelId Id of the channel group + * \param[out] stat Statistics structure + * \return + */ + virtual bool getChannelStatistics(const RsGxsGroupId& channelId,GxsGroupStatistic& stat) =0; /// default base URL used for channels links @see exportChannelLink static const std::string DEFAULT_CHANNEL_BASE_URL; @@ -484,6 +468,7 @@ public: /** * @brief Import channel from full link + * @jsonapi{development} * @param[in] link channel link either in radix or link format * @param[out] chanId optional storage for parsed channel id * @param[out] errMsg optional storage for error message, meaningful only in @@ -495,9 +480,114 @@ public: RsGxsGroupId& chanId = RS_DEFAULT_STORAGE_PARAM(RsGxsGroupId), std::string& errMsg = RS_DEFAULT_STORAGE_PARAM(std::string) ) = 0; + /** + * @brief Search the turtle reachable network for matching channels + * @jsonapi{development} + * An @see RsGxsChannelSearchResultEvent is emitted when matching channels + * arrives from the network + * @param[in] matchString string to search into the channels + * @return search id + */ + virtual TurtleRequestId turtleSearchRequest(const std::string& matchString)=0; - /* Following functions are deprecated as they expose internal functioning - * semantic, instead of a safe to use API */ + /** + * @brief Retrieve available search results + * @jsonapi{development} + * @param[in] searchId search id + * @param[out] results storage for search results + * @return false on error, true otherwise + */ + virtual bool retrieveDistantSearchResults( + TurtleRequestId searchId, + std::map& results ) = 0; + + /** + * @brief Request distant channel details + * @jsonapi{development} + * An @see RsGxsChannelSearchResultEvent is emitted once details are + * retrieved from the network + * @param[in] groupId if of the group to request to the network + * @return search id + */ + virtual TurtleRequestId turtleGroupRequest(const RsGxsGroupId& groupId) = 0; + + /** + * @brief Retrieve previously requested distant group + * @jsonapi{development} + * @param[in] groupId if of teh group + * @param[out] distantGroup storage for group data + * @return false on error, true otherwise + */ + virtual bool getDistantSearchResultGroupData( + const RsGxsGroupId& groupId, RsGxsChannelGroup& distantGroup ) = 0; + + /** + * @brief Clear accumulated search results + * @jsonapi{development} + * @param[in] reqId search id + * @return false on error, true otherwise + */ + virtual bool clearDistantSearchResults(TurtleRequestId reqId) = 0; + + ~RsGxsChannels() override; + + //////////////////////////////////////////////////////////////////////////// + /* Following functions are deprecated and should not be considered a safe to + * use API */ + + /** + * @brief Get auto-download option value for given channel + * @jsonapi{development} + * @deprecated This feature rely on very buggy code, the returned value is + * not reliable @see setChannelAutoDownload(). + * @param[in] channelId channel id + * @param[out] enabled storage for the auto-download option value + * @return false if something failed, true otherwhise + */ + RS_DEPRECATED + virtual bool getChannelAutoDownload( + const RsGxsGroupId& channelId, bool& enabled ) = 0; + + /** + * @brief Enable or disable auto-download for given channel. Blocking API + * @jsonapi{development} + * @deprecated This feature rely on very buggy code, when enabled the + * channel service start flooding erratically log with error messages, + * apparently without more dangerous consequences. Still those messages + * hints that something out of control is happening under the hood, use at + * your own risk. A safe alternative to this method can easly implemented + * at API client level instead. + * @param[in] channelId channel id + * @param[in] enable true to enable, false to disable + * @return false if something failed, true otherwhise + */ + RS_DEPRECATED + virtual bool setChannelAutoDownload( + const RsGxsGroupId& channelId, bool enable ) = 0; + + /** + * @brief Get download directory for the given channel + * @jsonapi{development} + * @deprecated @see setChannelAutoDownload() + * @param[in] channelId id of the channel + * @param[out] directory reference to string where to store the path + * @return false on error, true otherwise + */ + RS_DEPRECATED + virtual bool getChannelDownloadDirectory( const RsGxsGroupId& channelId, + std::string& directory ) = 0; + + /** + * @brief Set download directory for the given channel. Blocking API. + * @jsonapi{development} + * @deprecated @see setChannelAutoDownload() + * @param[in] channelId id of the channel + * @param[in] directory path + * @return false on error, true otherwise + */ + RS_DEPRECATED + virtual bool setChannelDownloadDirectory( + const RsGxsGroupId& channelId, const std::string& directory) = 0; /** * @brief Create channel. Blocking API. @@ -512,10 +602,13 @@ public: RS_DEPRECATED_FOR(getChannelsInfo) virtual bool getGroupData(const uint32_t &token, std::vector &groups) = 0; - RS_DEPRECATED_FOR(getChannelsContent) + RS_DEPRECATED_FOR(getChannelContent) + virtual bool getPostData(const uint32_t &token, std::vector &posts, std::vector &cmts, std::vector &votes) = 0; + + RS_DEPRECATED_FOR(getChannelContent) virtual bool getPostData(const uint32_t &token, std::vector &posts, std::vector &cmts) = 0; - RS_DEPRECATED_FOR(getChannelsContent) + RS_DEPRECATED_FOR(getChannelContent) virtual bool getPostData(const uint32_t &token, std::vector &posts) = 0; /** @@ -624,22 +717,4 @@ public: */ RS_DEPRECATED_FOR(editChannel) virtual bool updateGroup(uint32_t& token, RsGxsChannelGroup& group) = 0; - - ////////////////////////////////////////////////////////////////////////////// - /// Distant synchronisation methods /// - ////////////////////////////////////////////////////////////////////////////// - /// - RS_DEPRECATED_FOR(turtleChannelRequest) - virtual TurtleRequestId turtleGroupRequest(const RsGxsGroupId& group_id)=0; - RS_DEPRECATED - virtual TurtleRequestId turtleSearchRequest(const std::string& match_string)=0; - RS_DEPRECATED_FOR(turtleSearchRequest) - virtual bool retrieveDistantSearchResults(TurtleRequestId req, std::map &results) =0; - RS_DEPRECATED - virtual bool clearDistantSearchResults(TurtleRequestId req)=0; - RS_DEPRECATED_FOR(turtleChannelRequest) - virtual bool retrieveDistantGroup(const RsGxsGroupId& group_id,RsGxsChannelGroup& distant_group)=0; - ////////////////////////////////////////////////////////////////////////////// - - ~RsGxsChannels() override; }; diff --git a/libretroshare/src/retroshare/rsgxscircles.h b/libretroshare/src/retroshare/rsgxscircles.h index ed38f5c2d..ebe5a0131 100644 --- a/libretroshare/src/retroshare/rsgxscircles.h +++ b/libretroshare/src/retroshare/rsgxscircles.h @@ -45,25 +45,23 @@ extern RsGxsCircles* rsGxsCircles; enum class RsGxsCircleType : uint32_t // 32 bit overkill, just for retrocompat { - UNKNOWN = 0, /// Used to detect uninizialized values. - PUBLIC = 1, /// Public distribution - EXTERNAL = 2, /// Restricted to an external circle + UNKNOWN = 0, /// Used to detect uninizialized values. + PUBLIC = 1, /// Public distribution, based on GxsIds + EXTERNAL = 2, /// Restricted to an external circle, based on GxsIds - /** Restricted to a group of friend nodes, the administrator of the circle - * behave as a hub for them */ - NODES_GROUP = 3, + NODES_GROUP = 3, /// Restricted to a group of friend nodes, the administrator of the circle behave as a hub for them + /// Based on PGP nodes ids. LOCAL = 4, /// not distributed at all /** Self-restricted. Used only at creation time of self-restricted circles * when the circle id isn't known yet. Once the circle id is known the type * is set to EXTERNAL, and the external circle id is set to the id of the - * circle itself. + * circle itself. Based on GxsIds. */ EXT_SELF = 5, - /// distributed to nodes signed by your own PGP key only. - YOUR_EYES_ONLY = 6 + YOUR_EYES_ONLY = 6 /// distributed to nodes signed by your own PGP key only. }; // TODO: convert to enum class @@ -98,22 +96,32 @@ struct RsGxsCircleGroup : RsSerializable ~RsGxsCircleGroup() override; }; +enum class RsGxsCircleSubscriptionType:uint8_t { + UNKNOWN = 0x00, + SUBSCRIBE = 0x01, + UNSUBSCRIBE = 0x02 +}; + struct RsGxsCircleMsg : RsSerializable { RsMsgMetaData mMeta; +#ifdef TO_REMOVE + // This item is actually totally unused, so we can change it no problem #ifndef V07_NON_BACKWARD_COMPATIBLE_CHANGE_UNNAMED /* This is horrible and should be changed into yet to be defined something * reasonable in next non-retrocompatible version */ std::string stuff; #endif +#endif + RsGxsCircleSubscriptionType mSubscriptionType; /// @see RsSerializable void serial_process( RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx) override { RS_SERIAL_PROCESS(mMeta); - RS_SERIAL_PROCESS(stuff); + RS_SERIAL_PROCESS(mSubscriptionType); } ~RsGxsCircleMsg() override; @@ -121,15 +129,29 @@ struct RsGxsCircleMsg : RsSerializable struct RsGxsCircleDetails : RsSerializable { - RsGxsCircleDetails() : - mCircleType(static_cast(RsGxsCircleType::EXTERNAL)), - mAmIAllowed(false),mAmIAdmin(false) {} + RsGxsCircleDetails() : mCircleType(RsGxsCircleType::EXTERNAL), mAmIAllowed(false),mAmIAdmin(false) {} ~RsGxsCircleDetails() override; + // helper functions. + bool isIdInCircle(const RsGxsId& id) const { return mAllowedGxsIds.find(id) != mAllowedGxsIds.end(); } + bool isIdInInviteeList(const RsGxsId& id) const + { + auto it = mSubscriptionFlags.find(id); + return (it != mSubscriptionFlags.end()) && (it->second & GXS_EXTERNAL_CIRCLE_FLAGS_IN_ADMIN_LIST ); + } + bool isIdRequestingMembership(const RsGxsId& id) const + { + auto it = mSubscriptionFlags.find(id); + return it != mSubscriptionFlags.end() && (it->second & GXS_EXTERNAL_CIRCLE_FLAGS_SUBSCRIBED ); + } + bool isGxsIdBased() const { return mCircleType==RsGxsCircleType::PUBLIC || mCircleType==RsGxsCircleType::EXTERNAL || mCircleType==RsGxsCircleType::EXT_SELF; } + + // Members + RsGxsCircleId mCircleId; std::string mCircleName; - uint32_t mCircleType; + RsGxsCircleType mCircleType; RsGxsCircleId mRestrictedCircleId; /** true when one of load GXS ids belong to the circle allowed list (admin @@ -165,28 +187,54 @@ struct RsGxsCircleDetails : RsSerializable enum class RsGxsCircleEventCode: uint8_t { + // Notifications be only have 4 different possibilities: + // + // invitee list join/leave and + // membership request / leave request + // + // From there, depending on what the client displays, it is possible to interpret these + // as "some user joined the circle", or "membership pending for that Id", etc, depending + // on whether the current node owns the circle, or the admin is or is not yours. + // + // These should be decided in the UI based on what the circle cache is displaying. + // UNKNOWN = 0x00, - /** mCircleId contains the circle id and mGxsId is the id requesting - * membership */ - CIRCLE_MEMBERSHIP_REQUEST = 0x01, + /** + * Sent when we receive a membership request msg for a particular circle. + * + * mCircleId contains the circle id and mGxsId is the id requesting membership */ + CIRCLE_MEMBERSHIP_REQUEST = 0x01, - /** mCircleId is the circle that invites me, and mGxsId is my own Id that is - * invited */ - CIRCLE_MEMBERSHIP_INVITE = 0x02, + /** + * Sent when the ID has been added to the circle invitee list. + * + * mCircleId is the circle that invites me, and mGxsId is my own Id that is invited */ + CIRCLE_MEMBERSHIP_ID_ADDED_TO_INVITEE_LIST = 0x02, - /** mCircleId contains the circle id and mGxsId is the id dropping - * membership */ - CIRCLE_MEMBERSHIP_LEAVE = 0x03, + /** + * Sent when a GxsId annouces its will to not be in the circle. + * + * mCircleId contains the circle id and mGxsId is the id dropping membership */ + CIRCLE_MEMBERSHIP_LEAVE = 0x03, - /// mCircleId contains the circle id and mGxsId is the id of the new member - CIRCLE_MEMBERSHIP_JOIN = 0x04, + /** + * Sent when the Id has been removed from the invitee list. + * + * mCircleId contains the circle id and mGxsId is the id that was revoqued * by admin */ + CIRCLE_MEMBERSHIP_ID_REMOVED_FROM_INVITEE_LIST = 0x04, - /** mCircleId contains the circle id and mGxsId is the id that was revoqued * by admin */ - CIRCLE_MEMBERSHIP_REVOQUED= 0x05, + /** + * Means a new circle has been received. + * + * mCircleId contains the circle id */ + NEW_CIRCLE = 0x05, - /** mCircleId contains the circle id */ - NEW_CIRCLE = 0x06, + /** + * Means that the circle cache has updated, and membership status that is displayed should probably be updated to. + * + * no additional information. Simply means that the info previously from the cache has changed. */ + CACHE_DATA_UPDATED = 0x06, }; struct RsGxsCircleEvent: RsEvent @@ -312,7 +360,7 @@ public: RsGxsCircleMsg& msg) =0; /** - * @brief Invite identities to circle + * @brief Invite identities to circle (admin key is required) * @jsonapi{development} * @param[in] identities ids of the identities to invite * @param[in] circleId Id of the circle you own and want to invite ids in @@ -321,6 +369,16 @@ public: virtual bool inviteIdsToCircle( const std::set& identities, const RsGxsCircleId& circleId ) = 0; + /** + * @brief Remove identities from circle (admin key is required) + * @jsonapi{development} + * @param[in] identities ids of the identities to remove from the invite list + * @param[in] circleId Id of the circle you own and want to invite ids in + * @return false if something failed, true otherwhise + */ + virtual bool revokeIdsFromCircle( const std::set& identities, + const RsGxsCircleId& circleId ) = 0; + /** * @brief Request circle membership, or accept circle invitation * @jsonapi{development} diff --git a/libretroshare/src/retroshare/rsgxscommon.h b/libretroshare/src/retroshare/rsgxscommon.h index ad9d89ee2..cf71e0b95 100644 --- a/libretroshare/src/retroshare/rsgxscommon.h +++ b/libretroshare/src/retroshare/rsgxscommon.h @@ -43,6 +43,13 @@ struct RsGxsFile : RsSerializable RS_SERIAL_PROCESS(mHash); RS_SERIAL_PROCESS(mSize); } + + void clear() + { + mName.clear(); + mHash.clear(); + mSize = 0; + } }; struct RsGxsImage : RsSerializable @@ -64,6 +71,7 @@ struct RsGxsImage : RsSerializable void copy(uint8_t *data, uint32_t size); // Allocates and Copies. void clear(); // Frees. void shallowClear(); // Clears Pointer. + bool empty() const; uint32_t mSize; uint8_t* mData; @@ -156,7 +164,9 @@ struct RsGxsCommentService virtual bool getRelatedComments( uint32_t token, std::vector &comments ) = 0; - virtual bool createNewComment(uint32_t &token, RsGxsComment &comment) = 0; + virtual bool createNewComment(uint32_t &token, const RsGxsComment &comment) = 0; // async API + virtual bool createComment(RsGxsComment& comment) = 0; // blocking API. Updates comment with new metadata. + virtual bool createNewVote(uint32_t &token, RsGxsVote &vote) = 0; virtual bool acknowledgeComment( diff --git a/libretroshare/src/retroshare/rsgxsflags.h b/libretroshare/src/retroshare/rsgxsflags.h index 261173af4..10357118a 100644 --- a/libretroshare/src/retroshare/rsgxsflags.h +++ b/libretroshare/src/retroshare/rsgxsflags.h @@ -107,7 +107,8 @@ namespace GXS_SERV { static const uint32_t GXS_MSG_STATUS_UNPROCESSED = 0x00000001; // Flags to store the read/process status of group messages. static const uint32_t GXS_MSG_STATUS_GUI_UNREAD = 0x00000002; // The actual meaning may depend on the type of service. static const uint32_t GXS_MSG_STATUS_GUI_NEW = 0x00000004; // - static const uint32_t GXS_MSG_STATUS_KEEP = 0x00000008; // + /** Do not delete message even if older then group maximum storage time */ + static const uint32_t GXS_MSG_STATUS_KEEP_FOREVER = 0x00000008; static const uint32_t GXS_MSG_STATUS_DELETE = 0x00000020; // /** END GXS Msg status flags **/ diff --git a/libretroshare/src/retroshare/rsgxsforums.h b/libretroshare/src/retroshare/rsgxsforums.h index becddb329..da79a03a5 100644 --- a/libretroshare/src/retroshare/rsgxsforums.h +++ b/libretroshare/src/retroshare/rsgxsforums.h @@ -4,7 +4,8 @@ * libretroshare: retroshare core library * * * * Copyright (C) 2012-2014 Robert Fernie * - * Copyright (C) 2018-2019 Gioacchino Mazzurco * + * Copyright (C) 2018-2020 Gioacchino Mazzurco * + * Copyright (C) 2019-2020 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -25,6 +26,7 @@ #include #include #include +#include #include "retroshare/rstokenservice.h" #include "retroshare/rsgxsifacehelper.h" @@ -54,11 +56,8 @@ static const uint32_t RS_GXS_FORUM_MSG_FLAGS_MODERATED = 0x00000001; #define IS_FORUM_MSG_MODERATION(flags) (flags & RS_GXS_FORUM_MSG_FLAGS_MODERATED) -struct RsGxsForumGroup : RsSerializable +struct RsGxsForumGroup : RsSerializable, RsGxsGenericGroupData { - /** Forum GXS metadata */ - RsGroupMetaData mMeta; - /** @brief Forum desciption */ std::string mDescription; @@ -114,6 +113,8 @@ enum class RsForumEventCode: uint8_t UPDATED_MESSAGE = 0x04, /// existing message has been updated in a particular forum SUBSCRIBE_STATUS_CHANGED = 0x05, /// forum was subscribed or unsubscribed READ_STATUS_CHANGED = 0x06, /// msg was read or marked unread + STATISTICS_CHANGED = 0x07, /// suppliers and how many messages they have changed + MODERATOR_LIST_CHANGED = 0x08, /// forum moderation list has changed. }; struct RsGxsForumEvent: RsEvent @@ -125,6 +126,8 @@ struct RsGxsForumEvent: RsEvent RsForumEventCode mForumEventCode; RsGxsGroupId mForumGroupId; RsGxsMessageId mForumMsgId; + std::list mModeratorsAdded; + std::list mModeratorsRemoved; ///* @see RsEvent @see RsSerializable void serial_process( @@ -135,6 +138,9 @@ struct RsGxsForumEvent: RsEvent RS_SERIAL_PROCESS(mForumEventCode); RS_SERIAL_PROCESS(mForumGroupId); RS_SERIAL_PROCESS(mForumMsgId); + RS_SERIAL_PROCESS(mForumMsgId); + RS_SERIAL_PROCESS(mModeratorsAdded); + RS_SERIAL_PROCESS(mModeratorsRemoved); } ~RsGxsForumEvent() override; @@ -222,6 +228,24 @@ public: */ virtual bool getForumsSummaries(std::list& forums) = 0; + /** + * @brief returns statistics for the forum service + * @jsonapi{development} + * @param[out] stat statistics struct + * @return false if the call fails + */ + virtual bool getForumServiceStatistics(GxsServiceStatistic& stat) =0; + + /** + * @brief returns statistics about a particular forum + * @jsonapi{development} + * @param[in] forumId Id of the forum + * @param[out] stat statistics struct + * @return false when the object doesn't exist or when the timeout is reached requesting the data + */ + virtual bool getForumStatistics(const RsGxsGroupId& forumId,GxsGroupStatistic& stat)=0; + + /** * @brief Get forums information (description, thumbnail...). * Blocking API. @@ -245,7 +269,7 @@ public: std::vector& msgMetas) = 0; /** - * @brief Get specific list of messages from a single forums. Blocking API + * @brief Get specific list of messages from a single forum. Blocking API * @jsonapi{development} * @param[in] forumId id of the forum of which the content is requested * @param[in] msgsIds list of message ids to request @@ -289,10 +313,10 @@ public: static const std::string FORUM_URL_DATA_FIELD; /** Link query field used to store forum message title - * @see exportChannelLink */ + * @see exportForumLink */ static const std::string FORUM_URL_MSG_TITLE_FIELD; - /// Link query field used to store forum message id @see exportChannelLink + /// Link query field used to store forum message id @see exportForumLink static const std::string FORUM_URL_MSG_ID_FIELD; /** @@ -331,6 +355,32 @@ public: RsGxsGroupId& forumId = RS_DEFAULT_STORAGE_PARAM(RsGxsGroupId), std::string& errMsg = RS_DEFAULT_STORAGE_PARAM(std::string) ) = 0; + /** + * @brief Get posts related to the given post. + * If the set is empty, nothing is returned. + * @jsonapi{development} + * @param[in] forumId id of the forum of which the content is requested + * @param[in] parentId id of the post of which child posts (aka replies) + * are requested. + * @param[out] childPosts storage for the child posts + * @return Success or error details + */ + virtual std::error_condition getChildPosts( + const RsGxsGroupId& forumId, const RsGxsMessageId& parentId, + std::vector& childPosts ) = 0; + + /** + * @brief Set keep forever flag on a post so it is not deleted even if older + * then group maximum storage time + * @jsonapi{development} + * @param[in] forumId id of the forum of which the post pertain + * @param[in] postId id of the post on which to set the flag + * @param[in] keepForever true to set the flag, false to unset it + * @return Success or error details + */ + virtual std::error_condition setPostKeepForever( + const RsGxsGroupId& forumId, const RsGxsMessageId& postId, + bool keepForever ) = 0; /** * @brief Create forum. Blocking API. @@ -364,5 +414,5 @@ public: RS_DEPRECATED_FOR(createMessage) virtual bool createMsg(uint32_t &token, RsGxsForumMsg &msg) = 0; RS_DEPRECATED_FOR(editForum) - virtual bool updateGroup(uint32_t &token, RsGxsForumGroup &group) = 0; + virtual bool updateGroup(uint32_t &token, const RsGxsForumGroup &group) = 0; }; diff --git a/libretroshare/src/retroshare/rsgxsiface.h b/libretroshare/src/retroshare/rsgxsiface.h index 54a27656d..74e67008f 100644 --- a/libretroshare/src/retroshare/rsgxsiface.h +++ b/libretroshare/src/retroshare/rsgxsiface.h @@ -72,6 +72,46 @@ struct RsGxsGroupSummary : RsSerializable ~RsGxsGroupSummary(); }; +/*! + * This structure is used to locally store group search results for a given service. + * It contains the group information as well as a context + * strings to tell where the information was found. It is more compact than a + * GroupMeta object, so as to make search responses as light as possible. + */ +struct RsGxsGroupSearchResults : RsSerializable +{ + RsGxsGroupSearchResults() + : mPublishTs(0), mNumberOfMessages(0),mLastMessageTs(0), mSignFlags(0),mPopularity(0) + {} + + RsGxsGroupId mGroupId; + std::string mGroupName; + RsGxsId mAuthorId; + rstime_t mPublishTs; + uint32_t mNumberOfMessages; + rstime_t mLastMessageTs; + uint32_t mSignFlags; + uint32_t mPopularity; + + std::set mSearchContexts; + + /// @see RsSerializable::serial_process + void serial_process( RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx ) + { + RS_SERIAL_PROCESS(mGroupId); + RS_SERIAL_PROCESS(mGroupName); + RS_SERIAL_PROCESS(mAuthorId); + RS_SERIAL_PROCESS(mPublishTs); + RS_SERIAL_PROCESS(mNumberOfMessages); + RS_SERIAL_PROCESS(mLastMessageTs); + RS_SERIAL_PROCESS(mSignFlags); + RS_SERIAL_PROCESS(mPopularity); + RS_SERIAL_PROCESS(mSearchContexts); + } + + virtual ~RsGxsGroupSearchResults() = default; +}; /*! * Stores ids of changed gxs groups and messages. @@ -110,12 +150,20 @@ struct RsGxsChanges : RsEvent */ struct RsGxsIface { + /*! + * \brief serviceType + * \return The 16-bits service type. See @serialiser/rsserviceids.h + */ + virtual uint16_t serviceType() const =0; + +#ifdef TO_REMOVE /*! * Gxs services should call this for automatic handling of * changes, send * @param changes */ virtual void receiveChanges(std::vector& changes) = 0; +#endif /*! * @return handle to token service for this GXS service diff --git a/libretroshare/src/retroshare/rsgxsifacehelper.h b/libretroshare/src/retroshare/rsgxsifacehelper.h index de9925135..4339f95f0 100644 --- a/libretroshare/src/retroshare/rsgxsifacehelper.h +++ b/libretroshare/src/retroshare/rsgxsifacehelper.h @@ -3,8 +3,8 @@ * * * libretroshare: retroshare core library * * * - * Copyright 2011 by Christopher Evi-Parker * - * Copyright (C) 2018-2019 Gioacchino Mazzurco * + * Copyright (C) 2011 Christopher Evi-Parker * + * Copyright (C) 2018-2020 Gioacchino Mazzurco * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -26,9 +26,11 @@ #include #include "retroshare/rsgxsiface.h" +#include "retroshare/rsservicecontrol.h" #include "retroshare/rsreputations.h" #include "rsgxsflags.h" #include "util/rsdeprecate.h" +#include "util/rsdebug.h" /*! * This class only make method of internal members visible tu upper level to @@ -38,17 +40,40 @@ * To properly fix the API design many changes with the implied chain reactions * are necessary, so at this point this workaround seems acceptable. */ -struct RsGxsIfaceHelper + +//================================== +// #define DEBUG_GXSIFACEHELPER 1 +//================================== + +enum class TokenRequestType: uint8_t { + __NONE = 0x00, /// Used to detect uninitialized + GROUP_DATA = 0x01, + GROUP_META = 0x02, + GROUP_IDS = 0x03, + POSTS = 0x04, + ALL_POSTS = 0x05, + MSG_RELATED_INFO = 0x06, + GROUP_STATISTICS = 0x07, + SERVICE_STATISTICS = 0x08, + NO_KILL_TYPE = 0x09, + __MAX /// Used to detect out of range +}; + +class RsGxsIfaceHelper +{ +public: /*! * @param gxs handle to RsGenExchange instance of service (Usually the * service class itself) */ - RsGxsIfaceHelper(RsGxsIface& gxs) : - mGxs(gxs), mTokenService(*gxs.getTokenService()) {} + explicit RsGxsIfaceHelper(RsGxsIface& gxs) : + mGxs(gxs), mTokenService(*gxs.getTokenService()), mMtx("GxsIfaceHelper") + {} - ~RsGxsIfaceHelper(){} + ~RsGxsIfaceHelper() = default; +#ifdef TO_REMOVE /*! * Gxs services should call this for automatic handling of * changes, send @@ -58,6 +83,7 @@ struct RsGxsIfaceHelper { mGxs.receiveChanges(changes); } +#endif /* Generic Lists */ @@ -67,8 +93,7 @@ struct RsGxsIfaceHelper * @param groupIds the ids return for given request token * @return false if request token is invalid, check token status for error report */ - bool getGroupList(const uint32_t &token, - std::list &groupIds) + bool getGroupList(const uint32_t &token, std::list &groupIds) { return mGxs.getGroupList(token, groupIds); } @@ -101,8 +126,7 @@ struct RsGxsIfaceHelper * @param groupInfo the ids returned for given request token * @return false if request token is invalid, check token status for error report */ - bool getGroupSummary(const uint32_t &token, - std::list &groupInfo) + bool getGroupSummary(const uint32_t &token, std::list &groupInfo) { return mGxs.getGroupMeta(token, groupInfo); } @@ -112,8 +136,7 @@ struct RsGxsIfaceHelper * @param msgInfo the message metadata returned for given request token * @return false if request token is invalid, check token status for error report */ - bool getMsgSummary(const uint32_t &token, - GxsMsgMetaMap &msgInfo) + bool getMsgSummary(const uint32_t &token, GxsMsgMetaMap &msgInfo) { return mGxs.getMsgMeta(token, msgInfo); } @@ -233,30 +256,116 @@ struct RsGxsIfaceHelper } /// @see RsTokenService::requestGroupInfo - bool requestGroupInfo( uint32_t& token, const RsTokReqOptions& opts, - const std::list &groupIds ) - { return mTokenService.requestGroupInfo(token, 0, opts, groupIds); } + bool requestGroupInfo( uint32_t& token, const RsTokReqOptions& opts, const std::list &groupIds, bool high_priority_request = false ) + { + TokenRequestType token_request_type; + + switch(opts.mReqType) + { + case GXS_REQUEST_TYPE_GROUP_DATA: token_request_type = TokenRequestType::GROUP_DATA; break; + case GXS_REQUEST_TYPE_GROUP_META: token_request_type = TokenRequestType::GROUP_META; break; + case GXS_REQUEST_TYPE_GROUP_IDS: token_request_type = TokenRequestType::GROUP_IDS; break; + default: + RsErr() << __PRETTY_FUNCTION__ << "(EE) Unexpected request type " << opts.mReqType << "!!" << std::endl; + return false; + } + + cancelActiveRequestTokens(token_request_type); + + if( mTokenService.requestGroupInfo(token, 0, opts, groupIds)) + { + RS_STACK_MUTEX(mMtx); + mActiveTokens[token]=high_priority_request? (TokenRequestType::NO_KILL_TYPE) : token_request_type; +#ifdef DEBUG_GXSIFACEHELPER + locked_dumpTokens(); +#endif + return true; + } + else + return false; + } /// @see RsTokenService::requestGroupInfo - bool requestGroupInfo(uint32_t& token, const RsTokReqOptions& opts) - { return mTokenService.requestGroupInfo(token, 0, opts); } + bool requestGroupInfo(uint32_t& token, const RsTokReqOptions& opts, bool high_priority_request = false) + { + TokenRequestType token_request_type; + + switch(opts.mReqType) + { + case GXS_REQUEST_TYPE_GROUP_DATA: token_request_type = TokenRequestType::GROUP_DATA; break; + case GXS_REQUEST_TYPE_GROUP_META: token_request_type = TokenRequestType::GROUP_META; break; + case GXS_REQUEST_TYPE_GROUP_IDS: token_request_type = TokenRequestType::GROUP_IDS; break; + default: + RsErr() << __PRETTY_FUNCTION__ << "(EE) Unexpected request type " << opts.mReqType << "!!" << std::endl; + return false; + } + + cancelActiveRequestTokens(token_request_type); + + + if( mTokenService.requestGroupInfo(token, 0, opts)) + { + RS_STACK_MUTEX(mMtx); + mActiveTokens[token]=high_priority_request? (TokenRequestType::NO_KILL_TYPE) : token_request_type; +#ifdef DEBUG_GXSIFACEHELPER + locked_dumpTokens(); +#endif + return true; + } + else + return false; + } /// @see RsTokenService::requestMsgInfo - bool requestMsgInfo( uint32_t& token, - const RsTokReqOptions& opts, const GxsMsgReq& msgIds ) - { return mTokenService.requestMsgInfo(token, 0, opts, msgIds); } + bool requestMsgInfo( uint32_t& token, const RsTokReqOptions& opts, const GxsMsgReq& msgIds ) + { + if(mTokenService.requestMsgInfo(token, 0, opts, msgIds)) + { + RS_STACK_MUTEX(mMtx); + + mActiveTokens[token]= (msgIds.size()==1 && msgIds.begin()->second.size()==0) ?(TokenRequestType::ALL_POSTS):(TokenRequestType::POSTS); +#ifdef DEBUG_GXSIFACEHELPER + locked_dumpTokens(); +#endif + return true; + } + else + return false; + } /// @see RsTokenService::requestMsgInfo - bool requestMsgInfo( - uint32_t& token, const RsTokReqOptions& opts, - const std::list& grpIds ) - { return mTokenService.requestMsgInfo(token, 0, opts, grpIds); } + bool requestMsgInfo( uint32_t& token, const RsTokReqOptions& opts, const std::list& grpIds ) + { + if(mTokenService.requestMsgInfo(token, 0, opts, grpIds)) + { + RS_STACK_MUTEX(mMtx); + mActiveTokens[token]=TokenRequestType::ALL_POSTS; +#ifdef DEBUG_GXSIFACEHELPER + locked_dumpTokens(); +#endif + return true; + } + else + return false; + } /// @see RsTokenService::requestMsgRelatedInfo bool requestMsgRelatedInfo( uint32_t& token, const RsTokReqOptions& opts, const std::vector& msgIds ) - { return mTokenService.requestMsgRelatedInfo(token, 0, opts, msgIds); } + { + if( mTokenService.requestMsgRelatedInfo(token, 0, opts, msgIds)) + { + RS_STACK_MUTEX(mMtx); + mActiveTokens[token]=TokenRequestType::MSG_RELATED_INFO; +#ifdef DEBUG_GXSIFACEHELPER + locked_dumpTokens(); +#endif + return true; + } + else + return false; + } /** * @jsonapi{development} @@ -266,16 +375,62 @@ struct RsGxsIfaceHelper { return mTokenService.requestStatus(token); } /// @see RsTokenService::requestServiceStatistic - void requestServiceStatistic(uint32_t& token) - { mTokenService.requestServiceStatistic(token); } + bool requestServiceStatistic(uint32_t& token) + { + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_SERVICE_STATS; + + mTokenService.requestServiceStatistic(token,opts); + + RS_STACK_MUTEX(mMtx); + mActiveTokens[token]=TokenRequestType::SERVICE_STATISTICS; + +#ifdef DEBUG_GXSIFACEHELPER + locked_dumpTokens(); +#endif + return true; + } /// @see RsTokenService::requestGroupStatistic - void requestGroupStatistic(uint32_t& token, const RsGxsGroupId& grpId) - { mTokenService.requestGroupStatistic(token, grpId); } + bool requestGroupStatistic(uint32_t& token, const RsGxsGroupId& grpId) + { + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_GROUP_STATS; + + mTokenService.requestGroupStatistic(token, grpId,opts); + + RS_STACK_MUTEX(mMtx); + mActiveTokens[token]=TokenRequestType::GROUP_STATISTICS; +#ifdef DEBUG_GXSIFACEHELPER + locked_dumpTokens(); +#endif + return true; + } + + bool cancelActiveRequestTokens(TokenRequestType type) + { + RS_STACK_MUTEX(mMtx); + for(auto it = mActiveTokens.begin();it!=mActiveTokens.end();) + if(it->second == type) + { + mTokenService.cancelRequest(it->first); + it = mActiveTokens.erase(it); + } + else + ++it; + + return true; + } /// @see RsTokenService::cancelRequest bool cancelRequest(uint32_t token) - { return mTokenService.cancelRequest(token); } + { + { + RS_STACK_MUTEX(mMtx); + mActiveTokens.erase(token); + } + return mTokenService.cancelRequest(token); + } /** * @deprecated @@ -291,14 +446,91 @@ protected: * @param[in] token token associated to the request caller is waiting for * @param[in] maxWait maximum waiting time in milliseconds * @param[in] checkEvery time in millisecond between status checks + * @param[in] auto_delete_if_unsuccessful delete the request when it fails. This avoid leaving useless pending requests in the queue that would slow down additional calls. */ RsTokenService::GxsRequestStatus waitToken( uint32_t token, - std::chrono::milliseconds maxWait = std::chrono::milliseconds(500), - std::chrono::milliseconds checkEvery = std::chrono::milliseconds(2)) - { return mTokenService.waitToken(token, maxWait, checkEvery); } + std::chrono::milliseconds maxWait = std::chrono::milliseconds(20000), + std::chrono::milliseconds checkEvery = std::chrono::milliseconds(100), + bool auto_delete_if_unsuccessful=true) + { +#if defined(__ANDROID__) && (__ANDROID_API__ < 24) + auto wkStartime = std::chrono::steady_clock::now(); + int maxWorkAroundCnt = 10; +LLwaitTokenBeginLabel: +#endif + auto timeout = std::chrono::steady_clock::now() + maxWait; + auto st = requestStatus(token); + + while( !(st == RsTokenService::FAILED || st >= RsTokenService::COMPLETE) + && std::chrono::steady_clock::now() < timeout ) + { + std::this_thread::sleep_for(checkEvery); + st = requestStatus(token); + } + if(st != RsTokenService::COMPLETE && auto_delete_if_unsuccessful) + cancelRequest(token); + +#if defined(__ANDROID__) && (__ANDROID_API__ < 24) + /* Work around for very slow/old android devices, we don't expect this + * to be necessary on newer devices. If it take unreasonably long + * something worser is already happening elsewere and we return anyway. + */ + if( st > RsTokenService::FAILED && st < RsTokenService::COMPLETE + && maxWorkAroundCnt-- > 0 ) + { + maxWait *= 10; + checkEvery *= 3; + Dbg3() << __PRETTY_FUNCTION__ << " Slow Android device " + << " workaround st: " << st + << " maxWorkAroundCnt: " << maxWorkAroundCnt + << " maxWait: " << maxWait.count() + << " checkEvery: " << checkEvery.count() << std::endl; + goto LLwaitTokenBeginLabel; + } + Dbg3() << __PRETTY_FUNCTION__ << " lasted: " + << std::chrono::duration_cast( + std::chrono::steady_clock::now() - wkStartime ).count() + << "ms" << std::endl; + +#endif + + { + RS_STACK_MUTEX(mMtx); + mActiveTokens.erase(token); + } + + return st; + } private: RsGxsIface& mGxs; RsTokenService& mTokenService; + RsMutex mMtx; + + std::map mActiveTokens; + +#ifdef DEBUG_GXSIFACEHELPER + void locked_dumpTokens() + { + const uint16_t service_id = mGxs.serviceType(); + const auto countSize = static_cast(TokenRequestType::__MAX); + uint32_t count[countSize] = {0}; + + RsDbg rsdbg; + rsdbg << __PRETTY_FUNCTION__ << " Service 0x" << std::hex << service_id + << " (" << rsServiceControl->getServiceName( + RsServiceInfo::RsServiceInfoUIn16ToFullServiceId(service_id) ) + << ") this=0x" << static_cast(this) + << ") Active tokens (per type): "; + + // let's count how many token of each type we've got. + for(auto& it: mActiveTokens) ++count[static_cast(it.second)]; + + for(uint32_t i=0; i < countSize; ++i) + rsdbg /* << i << ":" */ << count[i] << " "; + } +#endif // def DEBUG_GXSIFACEHELPER + + RS_SET_CONTEXT_DEBUG_LEVEL(1) }; diff --git a/libretroshare/src/retroshare/rsgxsifacetypes.h b/libretroshare/src/retroshare/rsgxsifacetypes.h index d0e6e0372..2550f3b82 100644 --- a/libretroshare/src/retroshare/rsgxsifacetypes.h +++ b/libretroshare/src/retroshare/rsgxsifacetypes.h @@ -34,8 +34,6 @@ #include "serialiser/rstypeserializer.h" #include "util/rstime.h" -typedef Sha1CheckSum RsGxsMessageId; - typedef std::map > GxsMsgIdResult; typedef std::pair RsGxsGrpMsgIdPair; typedef std::map > MsgRelatedIdResult; @@ -45,6 +43,13 @@ struct RsMsgMetaData; typedef std::map > MsgMetaResult; +enum class GxsRequestPriority { + VERY_HIGH = 0x00, + HIGH = 0x01, + NORMAL = 0x02, + LOW = 0x03, + VERY_LOW = 0x04, +}; class RsGxsGrpMetaData; class RsGxsMsgMetaData; @@ -118,8 +123,15 @@ struct RsGroupMetaData : RsSerializable } }; +// This is the parent class of all interface-level GXS group data. Derived classes +// will include service-specific information, such as icon, description, etc +struct RsGxsGenericGroupData +{ + virtual ~RsGxsGenericGroupData() = default; // making the type polymorphic + RsGroupMetaData mMeta; +}; struct RsMsgMetaData : RsSerializable { @@ -177,49 +189,52 @@ struct RsMsgMetaData : RsSerializable } }; -class GxsGroupStatistic +struct RsGxsGenericMsgData { -public: - GxsGroupStatistic() + virtual ~RsGxsGenericMsgData() = default; // making the type polymorphic + + RsMsgMetaData mMeta; +}; + + +struct GxsGroupStatistic : RsSerializable +{ + GxsGroupStatistic() : + mNumMsgs(0), mTotalSizeOfMsgs(0), mNumThreadMsgsNew(0), + mNumThreadMsgsUnread(0), mNumChildMsgsNew(0), mNumChildMsgsUnread(0) {} + + /// @see RsSerializable + void serial_process( RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx ) override { - mNumMsgs = 0; - mTotalSizeOfMsgs = 0; - mNumThreadMsgsNew = 0; - mNumThreadMsgsUnread = 0; - mNumChildMsgsNew = 0; - mNumChildMsgsUnread = 0; + RS_SERIAL_PROCESS(mGrpId); + RS_SERIAL_PROCESS(mNumMsgs); + RS_SERIAL_PROCESS(mTotalSizeOfMsgs); + RS_SERIAL_PROCESS(mNumThreadMsgsNew); + RS_SERIAL_PROCESS(mNumThreadMsgsUnread); + RS_SERIAL_PROCESS(mNumChildMsgsNew); + RS_SERIAL_PROCESS(mNumChildMsgsUnread); + } -public: - /// number of message - RsGxsGroupId mGrpId; - - uint32_t mNumMsgs; // from the database + RsGxsGroupId mGrpId; + uint32_t mNumMsgs; /// number of message, from the database uint32_t mTotalSizeOfMsgs; uint32_t mNumThreadMsgsNew; uint32_t mNumThreadMsgsUnread; uint32_t mNumChildMsgsNew; - uint32_t mNumChildMsgsUnread; + uint32_t mNumChildMsgsUnread; + + ~GxsGroupStatistic() override; }; -class GxsServiceStatistic +struct GxsServiceStatistic : RsSerializable { -public: - GxsServiceStatistic() - { - mNumMsgs = 0; - mNumGrps = 0; - mSizeOfMsgs = 0; - mSizeOfGrps = 0; - mNumGrpsSubscribed = 0; - mNumThreadMsgsNew = 0; - mNumThreadMsgsUnread = 0; - mNumChildMsgsNew = 0; - mNumChildMsgsUnread = 0; - mSizeStore = 0; - } + GxsServiceStatistic() : + mNumMsgs(0), mNumGrps(0), mSizeOfMsgs(0), mSizeOfGrps(0), + mNumGrpsSubscribed(0), mNumThreadMsgsNew(0), mNumThreadMsgsUnread(0), + mNumChildMsgsNew(0), mNumChildMsgsUnread(0), mSizeStore(0) {} -public: uint32_t mNumMsgs; uint32_t mNumGrps; uint32_t mSizeOfMsgs; @@ -230,32 +245,35 @@ public: uint32_t mNumChildMsgsNew; uint32_t mNumChildMsgsUnread; uint32_t mSizeStore; + + /// @see RsSerializable + void serial_process( RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx ) override + { + RS_SERIAL_PROCESS(mNumMsgs); + RS_SERIAL_PROCESS(mNumGrps); + RS_SERIAL_PROCESS(mSizeOfMsgs); + RS_SERIAL_PROCESS(mSizeOfGrps); + RS_SERIAL_PROCESS(mNumGrpsSubscribed); + RS_SERIAL_PROCESS(mNumThreadMsgsNew); + RS_SERIAL_PROCESS(mNumThreadMsgsUnread); + RS_SERIAL_PROCESS(mNumChildMsgsNew); + RS_SERIAL_PROCESS(mNumChildMsgsUnread); + RS_SERIAL_PROCESS(mSizeStore); + } + + ~GxsServiceStatistic() override; }; -class UpdateItem -{ -public: - virtual ~UpdateItem() { } -}; - -class StringUpdateItem : public UpdateItem -{ -public: - StringUpdateItem(const std::string update) : mUpdate(update) {} - const std::string& getUpdate() const { return mUpdate; } - -private: - std::string mUpdate; -}; - -class RsGxsGroupUpdateMeta +class RS_DEPRECATED RsGxsGroupUpdateMeta { public: // expand as support is added for other utypes enum UpdateType { DESCRIPTION, NAME }; - RsGxsGroupUpdateMeta(const RsGxsGroupId& groupId) : mGroupId(groupId) {} + explicit RsGxsGroupUpdateMeta(const RsGxsGroupId& groupId): + mGroupId(groupId) {} typedef std::map GxsMetaUpdate; diff --git a/libretroshare/src/retroshare/rsgxsservice.h b/libretroshare/src/retroshare/rsgxsservice.h index cb4fcf7eb..152b018c9 100644 --- a/libretroshare/src/retroshare/rsgxsservice.h +++ b/libretroshare/src/retroshare/rsgxsservice.h @@ -32,68 +32,6 @@ typedef uint32_t TurtleRequestId; typedef std::map > GxsMsgMetaMap; typedef std::map > GxsMsgRelatedMetaMap; -/*! - * The aim of this class is to abstract how changes are represented so they can - * be determined outside the client API without explcitly enumerating all - * possible changes at the interface - */ -struct RsGxsNotify -{ - enum NotifyType - { - TYPE_UNKNOWN = 0x00, - TYPE_PUBLISHED = 0x01, - TYPE_RECEIVED_NEW = 0x02, - TYPE_PROCESSED = 0x03, - TYPE_RECEIVED_PUBLISHKEY = 0x04, - TYPE_RECEIVED_DISTANT_SEARCH_RESULTS = 0x05 - }; - - virtual ~RsGxsNotify() {} - virtual NotifyType getType() = 0; -}; - -/*! - * Relevant to group changes - */ -class RsGxsGroupChange : public RsGxsNotify -{ -public: - RsGxsGroupChange(NotifyType type, bool metaChange) : NOTIFY_TYPE(type), mMetaChange(metaChange) {} - std::list mGrpIdList; - NotifyType getType(){ return NOTIFY_TYPE;} - bool metaChange() { return mMetaChange; } -private: - const NotifyType NOTIFY_TYPE; - bool mMetaChange; -}; - -class RsGxsDistantSearchResultChange: public RsGxsNotify -{ -public: - RsGxsDistantSearchResultChange(TurtleRequestId id,const RsGxsGroupId& group_id) : mRequestId(id),mGroupId(group_id){} - - NotifyType getType() { return TYPE_RECEIVED_DISTANT_SEARCH_RESULTS ; } - - TurtleRequestId mRequestId ; - RsGxsGroupId mGroupId; -}; - -/*! - * Relevant to message changes - */ -class RsGxsMsgChange : public RsGxsNotify -{ -public: - RsGxsMsgChange(NotifyType type, bool metaChange) : NOTIFY_TYPE(type), mMetaChange(metaChange) {} - std::map > msgChangeMap; - NotifyType getType(){ return NOTIFY_TYPE;} - bool metaChange() { return mMetaChange; } -private: - const NotifyType NOTIFY_TYPE; - bool mMetaChange; -}; - #endif // RSGXSSERVICE_H diff --git a/libretroshare/src/retroshare/rsgxstrans.h b/libretroshare/src/retroshare/rsgxstrans.h index e1b8d4d8f..8cbb514a6 100644 --- a/libretroshare/src/retroshare/rsgxstrans.h +++ b/libretroshare/src/retroshare/rsgxstrans.h @@ -70,10 +70,8 @@ enum class GxsTransSendStatus : uint8_t typedef uint64_t RsGxsTransId; -class RsGxsTransGroup +class RsGxsTransGroup: public RsGxsGenericGroupData { - public: - RsGroupMetaData mMeta; }; class RsGxsTransMsg @@ -103,7 +101,34 @@ struct RsGxsTransOutgoingRecord RsGxsGroupId group_id ; }; +class RsGxsTransGroupStatistics: public GxsGroupStatistic +{ +public: + RsGxsTransGroupStatistics() + { + last_publish_TS = 0; + popularity = 0; + subscribed = false; + } + + void addMessageMeta(const RsGxsGroupId& grp,const RsMsgMetaData& meta) + { + messages_metas[meta.mMsgId] = meta ; + last_publish_TS = std::max(last_publish_TS,meta.mPublishTs) ; + mGrpId = grp ; + } + + bool subscribed ; + int popularity ; + + rstime_t last_publish_TS; + + std::map messages_metas ; +}; + + /// RetroShare GxsTrans asyncronous redundant small mail trasport on top of GXS +/// class RsGxsTrans: public RsGxsIfaceHelper { public: @@ -120,10 +145,8 @@ public: virtual ~RsGxsTrans() {} - virtual bool getStatistics(GxsTransStatistics& stats)=0; - -// virtual bool getGroupData(const uint32_t &token, std::vector &groups) = 0; -// virtual bool getPostData(const uint32_t &token, std::vector &posts) = 0; + virtual bool getDataStatistics(GxsTransStatistics& stats)=0; + virtual bool getGroupStatistics(std::map& stats) =0; }; extern RsGxsTrans *rsGxsTrans ; diff --git a/libretroshare/src/retroshare/rsidentity.h b/libretroshare/src/retroshare/rsidentity.h index 2874ccc91..7fa23697a 100644 --- a/libretroshare/src/retroshare/rsidentity.h +++ b/libretroshare/src/retroshare/rsidentity.h @@ -37,7 +37,7 @@ #include "serialiser/rstypeserializer.h" #include "util/rsdeprecate.h" -struct RsIdentity; +class RsIdentity; /** * Pointer to global instance of RsIdentity service implementation @@ -234,31 +234,30 @@ struct RsIdentityUsage : RsSerializable GXS_TUNNEL_DH_SIGNATURE_CHECK = 0x0c, GXS_TUNNEL_DH_SIGNATURE_CREATION = 0x0d, + /// Identity received through GXS sync + IDENTITY_NEW_FROM_GXS_SYNC = 0x0e, /// Group update on that identity data. Can be avatar, name, etc. - IDENTITY_DATA_UPDATE = 0x0e, + IDENTITY_NEW_FROM_DISCOVERY = 0x0f, + /// Explicit request to friend + IDENTITY_NEW_FROM_EXPLICIT_REQUEST = 0x10, /// Any signature verified for that identity - IDENTITY_GENERIC_SIGNATURE_CHECK = 0x0f, + IDENTITY_GENERIC_SIGNATURE_CHECK = 0x11, /// Any signature made by that identity - IDENTITY_GENERIC_SIGNATURE_CREATION = 0x10, + IDENTITY_GENERIC_SIGNATURE_CREATION = 0x12, - IDENTITY_GENERIC_ENCRYPTION = 0x11, - IDENTITY_GENERIC_DECRYPTION = 0x12, - CIRCLE_MEMBERSHIP_CHECK = 0x13 + IDENTITY_GENERIC_ENCRYPTION = 0x13, + IDENTITY_GENERIC_DECRYPTION = 0x14, + CIRCLE_MEMBERSHIP_CHECK = 0x15 } ; - RS_DEPRECATED - RsIdentityUsage( uint16_t service, const RsIdentityUsage::UsageCode& code, - const RsGxsGroupId& gid = RsGxsGroupId(), - const RsGxsMessageId& mid = RsGxsMessageId(), - uint64_t additional_id=0, - const std::string& comment = std::string() ); - RsIdentityUsage( RsServiceType service, RsIdentityUsage::UsageCode code, const RsGxsGroupId& gid = RsGxsGroupId(), - const RsGxsMessageId& mid = RsGxsMessageId(), + const RsGxsMessageId& message_id = RsGxsMessageId(), + const RsGxsMessageId& parent_id = RsGxsMessageId(), + const RsGxsMessageId& thread_id = RsGxsMessageId(), uint64_t additional_id=0, const std::string& comment = std::string() ); @@ -275,6 +274,12 @@ struct RsIdentityUsage : RsSerializable /// Message ID using the identity RsGxsMessageId mMsgId; + /// Reference message ID. Useful for votes/comments + RsGxsMessageId mParentId; + + /// Reference message ID. Useful for votes/comments + RsGxsMessageId mThreadId; + /// Some additional ID. Can be used for e.g. chat lobbies. uint64_t mAdditionalId; @@ -309,6 +314,7 @@ enum class RsGxsIdentityEventCode: uint8_t UNKNOWN = 0x00, NEW_IDENTITY = 0x01, DELETED_IDENTITY = 0x02, + UPDATED_IDENTITY = 0x03, }; struct RsGxsIdentityEvent: public RsEvent @@ -382,8 +388,9 @@ struct RsIdentityDetails : RsSerializable /** The Main Interface Class for GXS people identities */ -struct RsIdentity : RsGxsIfaceHelper +class RsIdentity: public RsGxsIfaceHelper { +public: explicit RsIdentity(RsGxsIface& gxs) : RsGxsIfaceHelper(gxs) {} /** diff --git a/libretroshare/src/retroshare/rsids.h b/libretroshare/src/retroshare/rsids.h index 8c2cdc51e..d38ee4518 100644 --- a/libretroshare/src/retroshare/rsids.h +++ b/libretroshare/src/retroshare/rsids.h @@ -328,6 +328,7 @@ using Sha256CheckSum = t_RsGenericIdType<_RsIdSize::SHA256 , false, R using RsPgpFingerprint = t_RsGenericIdType<_RsIdSize::PGP_FINGERPRINT, true, RsGenericIdType::PGP_FINGERPRINT>; using Bias20Bytes = t_RsGenericIdType<_RsIdSize::SHA1 , true, RsGenericIdType::BIAS_20_BYTES >; using RsGxsGroupId = t_RsGenericIdType<_RsIdSize::CERT_SIGN , false, RsGenericIdType::GXS_GROUP >; +using RsGxsMessageId = t_RsGenericIdType<_RsIdSize::SHA1 , false, RsGenericIdType::GXS_MSG >; using RsGxsId = t_RsGenericIdType<_RsIdSize::CERT_SIGN , false, RsGenericIdType::GXS_ID >; using RsGxsCircleId = t_RsGenericIdType<_RsIdSize::CERT_SIGN , false, RsGenericIdType::GXS_CIRCLE >; using RsGxsTunnelId = t_RsGenericIdType<_RsIdSize::SSL_ID , false, RsGenericIdType::GXS_TUNNEL >; diff --git a/libretroshare/src/retroshare/rsinit.h b/libretroshare/src/retroshare/rsinit.h index 5c2064030..19c0349bb 100644 --- a/libretroshare/src/retroshare/rsinit.h +++ b/libretroshare/src/retroshare/rsinit.h @@ -20,8 +20,7 @@ *******************************************************************************/ #pragma once -/// RetroShare initialization and login API - +/// @file RetroShare initialization and login API // Initialize ok, result >= 0 #define RS_INIT_OK 0 // Initialize ok @@ -32,11 +31,15 @@ #define RS_INIT_NO_KEYRING -3 // Keyring is empty. Need to import it. #define RS_INIT_NO_EXECUTABLE -4 // executable path hasn't been set in config options -#include #include #include #include -#include +#include +#include + +#include "retroshare/rstypes.h" +#include "retroshare/rsversion.h" + class RsLoginHelper; @@ -46,6 +49,71 @@ class RsLoginHelper; */ extern RsLoginHelper* rsLoginHelper; + +enum class RsInitErrorNum : int32_t +{ + ALREADY_LOGGED_IN = 6000, + CANT_ACQUIRE_LOCK = 6001, + INVALID_LOCATION_NAME = 6002, + PGP_NAME_OR_ID_NEEDED = 6003, + PGP_KEY_CREATION_FAILED = 6004, + SSL_KEY_CREATION_FAILED = 6005, + INVALID_SSL_ID = 6006, + LOGIN_FAILED = 6007 +}; + +struct RsInitErrorCategory: std::error_category +{ + const char* name() const noexcept override + { return "RetroShare init"; } + + std::string message(int ev) const override + { + switch (static_cast(ev)) + { + case RsInitErrorNum::ALREADY_LOGGED_IN: + return "Already logged in"; + case RsInitErrorNum::CANT_ACQUIRE_LOCK: + return "Cannot aquire lock on location data. Another instance is " + "already running with this profile?"; + case RsInitErrorNum::INVALID_LOCATION_NAME: + return "Invalid location name"; + case RsInitErrorNum::PGP_NAME_OR_ID_NEEDED: + return "Either PGP name or PGP id is needed"; + case RsInitErrorNum::PGP_KEY_CREATION_FAILED: + return "Failure creating PGP key"; + case RsInitErrorNum::SSL_KEY_CREATION_FAILED: + return "Failure creating SSL key"; + case RsInitErrorNum::INVALID_SSL_ID: + return "Invalid SSL id"; + case RsInitErrorNum::LOGIN_FAILED: + return "Generic login failure"; + default: + return rsErrorNotInCategory(ev, name()); + } + } + + const static RsInitErrorCategory instance; +}; + + +namespace std +{ +/** Register RsJsonApiErrorNum as an error condition enum, must be in std + * namespace */ +template<> struct is_error_condition_enum : true_type {}; +} + +/** Provide RsInitErrorNum conversion to std::error_condition, must be in + * same namespace of RsInitErrorNum */ +inline std::error_condition make_error_condition(RsInitErrorNum e) noexcept +{ + return std::error_condition( + static_cast(e), RsInitErrorCategory::instance ); +}; + + + /** * @brief The RsInitConfig struct * This class contains common configuration options, that executables using libretroshare may want to @@ -85,7 +153,7 @@ struct RsConfigOptions class RsInit { public: - enum LoadCertificateStatus : uint8_t + enum RS_DEPRECATED_FOR(RsInitErrorNum) LoadCertificateStatus : uint8_t { OK, /// Everything go as expected, no error occurred ERR_ALREADY_RUNNING, /// Another istance is running already @@ -313,10 +381,11 @@ extern RsAccounts* rsAccounts; class RsLoginHelper { public: - RsLoginHelper() {} + RsLoginHelper() = default; + /** * @brief Normal way to attempt login - * @jsonapi{development,manualwrapper} + * @jsonapi{development,unauthenticated} * @param[in] account Id of the account to which attempt login * @param[in] password Password for the given account * @return RsInit::OK if login attempt success, error code otherwhise @@ -352,6 +421,44 @@ public: void getLocations(std::vector& locations); /** + * @brief Creates a new RetroShare location, and log in once is created + * @jsonapi{development,manualwrapper} + * @param[out] locationId storage for generated location SSL id + * @param[inout] pgpId specify PGP id to use to sign the location, if a null + * id is passed the PGP key is created too and this param is used as + * storage for its id. + * @param[in] password to protect and unlock the associated PGP key + * param[in] apiUser (JSON API only) string containing username for JSON API + * so it can be later used to authenticate JSON API calls. It is passed + * down to @see RsJsonApi::authorizeUser under the hood. + * param[in] apiPass (JSON API only) string containing password for JSON API + * so it can be later used to authenticate JSON API calls. It is passed + * down to @see RsJsonApi::authorizeUser under the hood. + * To improve security we strongly advise to not use the same as the + * password used for the PGP key. + * @return Success or error information + */ + std::error_condition createLocationV2( + RsPeerId& locationId, + RsPgpId& pgpId, + const std::string& locationName, + const std::string& pgpName, + const std::string& password + /* JSON API only + * const std::string& apiUser + * const std::string& apiPass */ ); + + /** + * @brief Check if RetroShare is already logged in, this usually return true + * after a successfull attemptLogin() and before closeSession() + * @jsonapi{development,unauthenticated} + * @return true if already logged in, false otherwise + */ + bool isLoggedIn(); + +#if !RS_VERSION_AT_LEAST(0,6,6) + /** + * @deprecated Use @see createLocationV2 instead * @brief Creates a new RetroShare location, and log in once is created * @jsonapi{development,manualwrapper} * @param[inout] location provide input information to generate the location @@ -364,15 +471,9 @@ public: * Tor hidden location. UNTESTED! * @return true if success, false otherwise */ + RS_DEPRECATED_FOR(createLocationV2) bool createLocation( RsLoginHelper::Location& location, const std::string& password, std::string& errorMessage, bool makeHidden = false, bool makeAutoTor = false ); - - /** - * @brief Check if RetroShare is already logged in, this usually return true - * after a successfull attemptLogin() and before closeSession() - * @jsonapi{development,unauthenticated} - * @return true if already logged in, false otherwise - */ - bool isLoggedIn(); +#endif // !RS_VERSION_AT_LEAST(0,6,6) }; diff --git a/libretroshare/src/retroshare/rsjsonapi.h b/libretroshare/src/retroshare/rsjsonapi.h index 98b052d58..38e33490e 100644 --- a/libretroshare/src/retroshare/rsjsonapi.h +++ b/libretroshare/src/retroshare/rsjsonapi.h @@ -1,7 +1,7 @@ /* * RetroShare JSON API public header * - * Copyright (C) 2018-2020 Gioacchino Mazzurco + * Copyright (C) 2018-2020 Gioacchino Mazzurco * Copyright (C) 2019 Cyril Soler * Copyright (C) 2020 Asociación Civil Altermundi * @@ -29,6 +29,7 @@ #include #include +#include "util/rsdebug.h" #include "util/rsmemory.h" class RsJsonApi; @@ -74,8 +75,7 @@ struct RsJsonApiErrorCategory: std::error_category case RsJsonApiErrorNum::NOT_A_MACHINE_GUN: return "Method must not be called in burst"; default: - return "Error message for error: " + std::to_string(ev) + - " not available in category: " + name(); + return rsErrorNotInCategory(ev, name()); } } diff --git a/libretroshare/src/retroshare/rsmsgs.h b/libretroshare/src/retroshare/rsmsgs.h index 3bdc76505..614a99707 100644 --- a/libretroshare/src/retroshare/rsmsgs.h +++ b/libretroshare/src/retroshare/rsmsgs.h @@ -62,6 +62,7 @@ #define RS_MSG_SIGNED 0x004000 /* message was signed and signature didn't check */ #define RS_MSG_LOAD_EMBEDDED_IMAGES 0x008000 /* load embedded images */ #define RS_MSG_PUBLISH_KEY 0x020000 /* publish key */ +#define RS_MSG_SPAM 0x040000 /* Message is marked as spam */ #define RS_MSG_SYSTEM (RS_MSG_USER_REQUEST | RS_MSG_FRIEND_RECOMMENDATION | RS_MSG_PUBLISH_KEY) @@ -356,7 +357,7 @@ struct RsMailStatusEvent : RsEvent #define RS_DISTANT_CHAT_CONTACT_PERMISSION_FLAG_FILTER_NON_CONTACTS 0x0001 #define RS_DISTANT_CHAT_CONTACT_PERMISSION_FLAG_FILTER_EVERYBODY 0x0002 -struct DistantChatPeerInfo +struct DistantChatPeerInfo : RsSerializable { DistantChatPeerInfo() : status(0),pending_items(0) {} @@ -365,6 +366,16 @@ struct DistantChatPeerInfo DistantChatPeerId peer_id ; // this is the tunnel id actually uint32_t status ; // see the values in rsmsgs.h uint32_t pending_items; // items not sent, waiting for a tunnel + + ///* @see RsEvent @see RsSerializable + void serial_process( RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx ) override + { + RS_SERIAL_PROCESS(to_id); + RS_SERIAL_PROCESS(own_id); + RS_SERIAL_PROCESS(peer_id); + RS_SERIAL_PROCESS(status); + RS_SERIAL_PROCESS(pending_items); + } }; // Identifier for an chat endpoint like @@ -426,9 +437,8 @@ public: } }; -class ChatMessage +struct ChatMessage : RsSerializable { -public: ChatId chat_id; // id of chat endpoint RsPeerId broadcast_peer_id; // only used for broadcast chat: source peer id RsGxsId lobby_peer_gxs_id; // only used for lobbys: nickname of message author @@ -441,6 +451,22 @@ public: bool incoming; bool online; // for outgoing messages: was this message send? //bool system_message; + + ///* @see RsEvent @see RsSerializable + void serial_process( RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx ) override + { + RS_SERIAL_PROCESS(chat_id); + RS_SERIAL_PROCESS(broadcast_peer_id); + RS_SERIAL_PROCESS(lobby_peer_gxs_id); + RS_SERIAL_PROCESS(peer_alternate_nickname); + + RS_SERIAL_PROCESS(chatflags); + RS_SERIAL_PROCESS(sendTime); + RS_SERIAL_PROCESS(recvTime); + RS_SERIAL_PROCESS(msg); + RS_SERIAL_PROCESS(incoming); + RS_SERIAL_PROCESS(online); + } }; class ChatLobbyInvite : RsSerializable @@ -678,6 +704,15 @@ public: */ virtual bool MessageStar(const std::string &msgId, bool mark) = 0; + /** + * @brief MessageJunk + * @jsonapi{development} + * @param[in] msgId + * @param[in] mark + * @return true on success + */ + virtual bool MessageJunk(const std::string &msgId, bool mark) = 0; + /** * @brief MessageLoadEmbeddedImages * @jsonapi{development} @@ -962,13 +997,38 @@ virtual void getOwnAvatarData(unsigned char *& data,int& size) = 0 ; virtual uint32_t getDistantChatPermissionFlags()=0 ; virtual bool setDistantChatPermissionFlags(uint32_t flags)=0 ; - -virtual bool initiateDistantChatConnexion( + + /** + * @brief initiateDistantChatConnexion initiate a connexion for a distant chat + * @jsonapi{development} + * @param[in] to_pid RsGxsId to start the connection + * @param[in] from_pid owned RsGxsId who start the connection + * @param[out] pid distant chat id + * @param[out] error_code if the connection can't be stablished + * @param[in] notify notify remote that the connection is stablished + * @return true on success. If you try to initate a connection already started it will return the pid of it. + */ + virtual bool initiateDistantChatConnexion( const RsGxsId& to_pid, const RsGxsId& from_pid, DistantChatPeerId& pid, uint32_t& error_code, bool notify = true ) = 0; -virtual bool getDistantChatStatus(const DistantChatPeerId& pid,DistantChatPeerInfo& info)=0; -virtual bool closeDistantChatConnexion(const DistantChatPeerId& pid)=0; + + /** + * @brief getDistantChatStatus receives distant chat info to a given distant chat id + * @jsonapi{development} + * @param[in] pid distant chat id + * @param[out] info distant chat info + * @return true on success + */ + virtual bool getDistantChatStatus(const DistantChatPeerId& pid, DistantChatPeerInfo& info)=0; + + /** + * @brief closeDistantChatConnexion + * @jsonapi{development} + * @param[in] pid distant chat id to close the connection + * @return true on success + */ + virtual bool closeDistantChatConnexion(const DistantChatPeerId& pid)=0; /** * @brief MessageSend diff --git a/libretroshare/src/retroshare/rsnotify.h b/libretroshare/src/retroshare/rsnotify.h index d69357cdb..e8c9de614 100644 --- a/libretroshare/src/retroshare/rsnotify.h +++ b/libretroshare/src/retroshare/rsnotify.h @@ -115,11 +115,13 @@ const uint32_t RS_FEED_ITEM_CHAT_NEW = RS_FEED_TYPE_CHAT | 0x0001; const uint32_t RS_FEED_ITEM_MESSAGE = RS_FEED_TYPE_MSG | 0x0001; const uint32_t RS_FEED_ITEM_FILES_NEW = RS_FEED_TYPE_FILES | 0x0001; -const uint32_t RS_FEED_ITEM_CIRCLE_MEMB_REQ = RS_FEED_TYPE_CIRCLE | 0x0001; -const uint32_t RS_FEED_ITEM_CIRCLE_INVIT_REC = RS_FEED_TYPE_CIRCLE | 0x0002; -const uint32_t RS_FEED_ITEM_CIRCLE_MEMB_LEAVE = RS_FEED_TYPE_CIRCLE | 0x0003; -const uint32_t RS_FEED_ITEM_CIRCLE_MEMB_JOIN = RS_FEED_TYPE_CIRCLE | 0x0004; -const uint32_t RS_FEED_ITEM_CIRCLE_MEMB_REVOQUED = RS_FEED_TYPE_CIRCLE | 0x0005; +const uint32_t RS_FEED_ITEM_CIRCLE_MEMB_REQ = RS_FEED_TYPE_CIRCLE | 0x0001; +const uint32_t RS_FEED_ITEM_CIRCLE_INVITE_REC = RS_FEED_TYPE_CIRCLE | 0x0002; +const uint32_t RS_FEED_ITEM_CIRCLE_MEMB_LEAVE = RS_FEED_TYPE_CIRCLE | 0x0003; +const uint32_t RS_FEED_ITEM_CIRCLE_MEMB_JOIN = RS_FEED_TYPE_CIRCLE | 0x0004; +const uint32_t RS_FEED_ITEM_CIRCLE_MEMB_ACCEPTED = RS_FEED_TYPE_CIRCLE | 0x0005; +const uint32_t RS_FEED_ITEM_CIRCLE_MEMB_REVOKED = RS_FEED_TYPE_CIRCLE | 0x0006; +const uint32_t RS_FEED_ITEM_CIRCLE_INVITE_CANCELLED= RS_FEED_TYPE_CIRCLE | 0x0007; const uint32_t RS_MESSAGE_CONNECT_ATTEMPT = 0x0001; @@ -237,7 +239,6 @@ public: virtual void notifyPeerStatusChangedSummary () {} virtual void notifyDiscInfoChanged () {} - virtual bool askForDeferredSelfSignature (const void * /* data */, const uint32_t /* len */, unsigned char * /* sign */, unsigned int * /* signlen */,int& signature_result , std::string /*reason = ""*/) { signature_result = false ;return true; } virtual void notifyDownloadComplete (const std::string& /* fileHash */) {} virtual void notifyDownloadCompleteCount (uint32_t /* count */) {} virtual void notifyHistoryChanged (uint32_t /* msgId */, int /* type */) {} diff --git a/libretroshare/src/retroshare/rspeers.h b/libretroshare/src/retroshare/rspeers.h index 143060bc1..124dba357 100644 --- a/libretroshare/src/retroshare/rspeers.h +++ b/libretroshare/src/retroshare/rspeers.h @@ -3,7 +3,9 @@ * * * libretroshare: retroshare core library * * * - * Copyright 2004-2008 by Robert Fernie * + * Copyright (C) 2004-2008 by Robert Fernie * + * Copyright (C) 2018-2020 Gioacchino Mazzurco * + * Copyright (C) 2020 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -468,7 +470,7 @@ struct RsGroupInfo : RsSerializable struct RsPeerStateChangedEvent : RsEvent { /// @param[in] sslId is of the peer which changed state - RsPeerStateChangedEvent(RsPeerId sslId); + explicit RsPeerStateChangedEvent(RsPeerId sslId); /// Storage fot the id of the peer that changed state RsPeerId mSslId; @@ -605,7 +607,15 @@ public: virtual bool getAssociatedSSLIds(const RsPgpId& gpg_id, std::list& ids) = 0; virtual bool gpgSignData(const void *data, const uint32_t len, unsigned char *sign, unsigned int *signlen, std::string reason = "") = 0; - virtual RsPgpId pgpIdFromFingerprint(const RsPgpFingerprint& fpr) = 0; + /** + * @brief Convert PGP fingerprint to PGP id + * @jsonapi{development} + * Helper method useful while we port the whole RetroShare codebase from + * RsPgpId to RsPgpFingerprint + * @param[in] fpr PGP fingerprint to convert + * @return PGP id corresponding to the fingerprint + */ + virtual RsPgpId pgpIdFromFingerprint(const RsPgpFingerprint& fpr) = 0; // Note: the two methods below could be unified. The fact that one of them can take an optional RsPeerDetails struct as parameter // seems quite inconsistent. @@ -803,7 +813,7 @@ public: ServicePermissionFlags flags = RS_NODE_PERM_DEFAULT ) = 0; - /* Auth Stuff */ + RS_DEPRECATED /// This function doesn't provide meaningful error reporting virtual std::string getPGPKey(const RsPgpId& pgp_id,bool include_signatures) = 0; virtual bool GetPGPBase64StringAndCheckSum(const RsPgpId& gpg_id,std::string& gpg_base64_string,std::string& gpg_base64_checksum) = 0; diff --git a/libretroshare/src/retroshare/rsphoto.h b/libretroshare/src/retroshare/rsphoto.h index 7e5b6ea99..839b28a90 100644 --- a/libretroshare/src/retroshare/rsphoto.h +++ b/libretroshare/src/retroshare/rsphoto.h @@ -3,7 +3,7 @@ * * * libretroshare: retroshare core library * * * - * Copyright 2008-2012 by Robert Fernie * + * Copyright 2008-2020 by Robert Fernie * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -25,164 +25,72 @@ #include #include #include -#include "rsgxsservice.h" +#include "retroshare/rsgxsservice.h" +#include "retroshare/rsgxscommon.h" +#include "retroshare/rsgxsifacehelper.h" + /* The Main Interface Class - for information about your Peers */ class RsPhoto; extern RsPhoto *rsPhoto; -/******************* NEW STUFF FOR NEW CACHE SYSTEM *********/ - -#define RSPHOTO_MODE_NEW 1 -#define RSPHOTO_MODE_OWN 2 -#define RSPHOTO_MODE_REMOTE 3 - -class RsPhotoThumbnail -{ - public: - RsPhotoThumbnail() - :data(NULL), size(0), type("N/A") { return; } - - bool deleteImage(); - bool copyFrom(const RsPhotoThumbnail &nail); - - // Holds Thumbnail image. - uint8_t *data; - uint32_t size; - std::string type; -}; - -/* If these flags are no set - the Photo inherits values from the Album - */ - -#define RSPHOTO_FLAGS_ATTRIB_TITLE 0x0001 -#define RSPHOTO_FLAGS_ATTRIB_CAPTION 0x0002 -#define RSPHOTO_FLAGS_ATTRIB_DESC 0x0004 -#define RSPHOTO_FLAGS_ATTRIB_PHOTOGRAPHER 0x0008 -#define RSPHOTO_FLAGS_ATTRIB_WHERE 0x0010 -#define RSPHOTO_FLAGS_ATTRIB_WHEN 0x0020 -#define RSPHOTO_FLAGS_ATTRIB_OTHER 0x0040 -#define RSPHOTO_FLAGS_ATTRIB_CATEGORY 0x0080 -#define RSPHOTO_FLAGS_ATTRIB_HASHTAGS 0x0100 -#define RSPHOTO_FLAGS_ATTRIB_ORDER 0x0200 -#define RSPHOTO_FLAGS_ATTRIB_THUMBNAIL 0x0400 -#define RSPHOTO_FLAGS_ATTRIB_MODE 0x0800 -#define RSPHOTO_FLAGS_ATTRIB_AUTHOR 0x1000 // PUSH UP ORDER -#define RSPHOTO_FLAGS_ATTRIB_PHOTO 0x2000 // PUSH UP ORDER. - class RsPhotoPhoto -{ - public: - - RsMsgMetaData mMeta; - - RsPhotoPhoto(); - - // THESE ARE IN THE META DATA. - //std::string mAlbumId; - //std::string mId; - //std::string mTitle; // only used by Album. - std::string mCaption; - std::string mDescription; - std::string mPhotographer; - std::string mWhere; - std::string mWhen; - std::string mOther; - std::string mCategory; - - std::string mHashTags; - - uint32_t mSetFlags; - - int mOrder; - - RsPhotoThumbnail mThumbnail; - - int mMode; - - // These are not saved. - std::string path; // if in Mode NEW. - uint32_t mModFlags; -}; - -class RsPhotoAlbumShare -{ - public: - - uint32_t mShareType; - std::string mShareGroupId; - std::string mPublishKey; - uint32_t mCommentMode; - uint32_t mResizeMode; -}; - -class RsPhotoAlbum -{ - public: - RsPhotoAlbum(); - - RsGroupMetaData mMeta; - - // THESE ARE IN THE META DATA. - //std::string mAlbumId; - //std::string mTitle; // only used by Album. - - std::string mCaption; - std::string mDescription; - std::string mPhotographer; - std::string mWhere; - std::string mWhen; - std::string mOther; - std::string mCategory; - - std::string mHashTags; - - RsPhotoThumbnail mThumbnail; - - int mMode; - - std::string mPhotoPath; - RsPhotoAlbumShare mShareOptions; - - // These aren't saved. - uint32_t mSetFlags; - uint32_t mModFlags; -}; - -class RsGxsPhotoCommentItem; -class RsPhotoComment { public: - RsPhotoComment(); - explicit RsPhotoComment(const RsGxsPhotoCommentItem& comment); + RsMsgMetaData mMeta; - RsPhotoComment& operator=(const RsGxsPhotoCommentItem& comment); + RsPhotoPhoto(); - RsMsgMetaData mMeta; + // V2 PhotoMsg - keep it simple. + // mMeta.mTitle used for Photo Caption. + // mDescription optional field for addtional notes. + // mLowResImage - < 50k jpg of image. + // mPhotoFile - transfer details for original photo. + std::string mDescription; + uint32_t mOrder; + RsGxsImage mLowResImage; + RsGxsFile mPhotoFile; - std::string mComment; - uint32_t mCommentFlag; + // These are not saved. + std::string mPath; // if New photo +}; + +#define RSPHOTO_SHAREMODE_LOWRESONLY (1) +#define RSPHOTO_SHAREMODE_ORIGINAL (2) +#define RSPHOTO_SHAREMODE_DUP_ORIGINAL (3) +#define RSPHOTO_SHAREMODE_DUP_200K (4) +#define RSPHOTO_SHAREMODE_DUP_1M (5) + +struct RsPhotoAlbum: RsGxsGenericGroupData +{ + RsPhotoAlbum(); + + // V2 Album - keep it simple. + // mMeta.mTitle. + uint32_t mShareMode; + + std::string mCaption; + std::string mDescription; + std::string mPhotographer; + std::string mWhere; + std::string mWhen; + + RsGxsImage mThumbnail; + + // Below is not saved. + bool mAutoDownload; }; std::ostream &operator<<(std::ostream &out, const RsPhotoPhoto &photo); std::ostream &operator<<(std::ostream &out, const RsPhotoAlbum &album); typedef std::map > PhotoResult; -typedef std::map > PhotoCommentResult; -typedef std::map > PhotoRelatedCommentResult; -class RsPhoto +class RsPhoto: public RsGxsIfaceHelper, public RsGxsCommentService { - public: - - static const uint32_t FLAG_MSG_TYPE_PHOTO_POST; - static const uint32_t FLAG_MSG_TYPE_PHOTO_COMMENT; - static const uint32_t FLAG_MSG_TYPE_MASK; - - - RsPhoto() { return; } + explicit RsPhoto(RsGxsIface &gxs) : RsGxsIfaceHelper(gxs) { return; } virtual ~RsPhoto() { return; } @@ -249,7 +157,7 @@ public: virtual bool getMsgSummary(const uint32_t &token, MsgMetaResult &msgInfo) = 0; - /* Specific Service Data */ + /* Specific Service Data */ /*! * @param token token to be redeemed for album request @@ -266,22 +174,6 @@ public: virtual bool getPhoto(const uint32_t &token, PhotoResult &photo) = 0; - /* details are updated in album - to choose Album ID, and storage path */ - - /*! - * @param token token to be redeemed for photo request - * @param photo the photo returned for given request token - * @return false if request token is invalid, check token status for error report - */ - virtual bool getPhotoComment(const uint32_t &token, - PhotoCommentResult& comments) = 0; - /*! - * @param token token to be redeemed for photo request - * @param photo the photo returned for given request token - * @return false if request token is invalid, check token status for error report - */ - virtual bool getPhotoRelatedComment(const uint32_t &token, PhotoRelatedCommentResult &comments) = 0; - /*! * submits album, which returns a token that needs * to be acknowledge to get album grp id @@ -298,16 +190,6 @@ public: */ virtual bool submitPhoto(uint32_t& token, RsPhotoPhoto &photo) = 0; - /*! - * submits photo comment, which returns a token that needs - * to be acknowledged to get photo msg-grp id pair - * The mParentId needs to be set to an existing msg for which - * commenting is enabled - * @param token token to redeem for acknowledgement - * @param comment comment to be submitted - */ - virtual bool submitComment(uint32_t& token, RsPhotoComment &photo) = 0; - /*! * subscribes to group, and returns token which can be used * to be acknowledged to get group Id @@ -336,7 +218,29 @@ public: */ virtual bool acknowledgeGrp(const uint32_t& token, RsGxsGroupId& grpId) = 0; + // Blocking versions. + /*! + * request to create a new album. Blocks until process completes. + * @param album album to be submitted + * @return true if created false otherwise + */ + virtual bool createAlbum(RsPhotoAlbum &album) = 0; + /*! + * request to update an existing album. Blocks until process completes. + * @param album album to be submitted + * @return true if created false otherwise + */ + virtual bool updateAlbum(const RsPhotoAlbum &album) = 0; + + /*! + * retrieve albums based in groupIds. + * @param groupIds the ids to fetch. + * @param albums vector to be filled by request. + * @return true is successful, false otherwise. + */ + virtual bool getAlbums(const std::list &groupIds, + std::vector &albums) = 0; }; diff --git a/libretroshare/src/retroshare/rsposted.h b/libretroshare/src/retroshare/rsposted.h index a1dbdf83c..4cd6e8cf4 100644 --- a/libretroshare/src/retroshare/rsposted.h +++ b/libretroshare/src/retroshare/rsposted.h @@ -3,7 +3,8 @@ * * * libretroshare: retroshare core library * * * - * Copyright 2008-2012 by Robert Fernie, Christopher Evi-Parker * + * Copyright (C) 2008-2012 Robert Fernie, Christopher Evi-Parker * + * Copyright (C) 2020 Gioacchino Mazzurco * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -19,8 +20,7 @@ * along with this program. If not, see . * * * *******************************************************************************/ -#ifndef RETROSHARE_GXS_RSPOSTED_GUI_INTERFACE_H -#define RETROSHARE_GXS_RSPOSTED_GUI_INTERFACE_H +#pragma once #include #include @@ -32,20 +32,60 @@ #include "retroshare/rsgxscommon.h" #include "serialiser/rsserializable.h" -/* The Main Interface Class - for information about your Posted */ class RsPosted; -extern RsPosted *rsPosted; -class RsPostedPost; -class RsPostedGroup +/** + * Pointer to global instance of RsGxsChannels service implementation + * @jsonapi{development} + */ +extern RsPosted* rsPosted; + +struct RsPostedGroup: RsGxsGenericGroupData { - public: - RsPostedGroup() { return; } - - RsGroupMetaData mMeta; std::string mDescription; RsGxsImage mGroupImage; +}; +struct RsPostedPost: public RsGxsGenericMsgData +{ + RsPostedPost(): mHaveVoted(false), mUpVotes(0), mDownVotes(0), mComments(0), + mHotScore(0), mTopScore(0), mNewScore(0) {} + + bool calculateScores(rstime_t ref_time); + + std::string mLink; + std::string mNotes; + + bool mHaveVoted; + + // Calculated. + uint32_t mUpVotes; + uint32_t mDownVotes; + uint32_t mComments; + + + // and Calculated Scores:??? + double mHotScore; + double mTopScore; + double mNewScore; + + RsGxsImage mImage; + + /// @see RsSerializable + /*virtual void serial_process( RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx ) + { + RS_SERIAL_PROCESS(mImage); + RS_SERIAL_PROCESS(mMeta); + RS_SERIAL_PROCESS(mLink); + RS_SERIAL_PROCESS(mHaveVoted); + RS_SERIAL_PROCESS(mUpVotes); + RS_SERIAL_PROCESS(mDownVotes); + RS_SERIAL_PROCESS(mComments); + RS_SERIAL_PROCESS(mHotScore); + RS_SERIAL_PROCESS(mTopScore); + RS_SERIAL_PROCESS(mNewScore); + }*/ }; @@ -65,9 +105,6 @@ class RsPostedGroup #define RSPOSTED_VIEWMODE_COMMENTS 4 -std::ostream &operator<<(std::ostream &out, const RsPostedGroup &group); -std::ostream &operator<<(std::ostream &out, const RsPostedPost &post); - enum class RsPostedEventCode: uint8_t { UNKNOWN = 0x00, @@ -77,6 +114,7 @@ enum class RsPostedEventCode: uint8_t UPDATED_POSTED_GROUP = 0x04, UPDATED_MESSAGE = 0x05, READ_STATUS_CHANGED = 0x06, + STATISTICS_CHANGED = 0x07, }; @@ -98,25 +136,63 @@ struct RsGxsPostedEvent: RsEvent RS_SERIAL_PROCESS(mPostedGroupId); RS_SERIAL_PROCESS(mPostedMsgId); } + + ~RsGxsPostedEvent() override; }; class RsPosted : public RsGxsIfaceHelper, public RsGxsCommentService { - public: - - enum RankType {TopRankType, HotRankType, NewRankType }; - - //static const uint32_t FLAG_MSGTYPE_POST; - //static const uint32_t FLAG_MSGTYPE_MASK; - +public: explicit RsPosted(RsGxsIface& gxs) : RsGxsIfaceHelper(gxs) {} - virtual ~RsPosted() {} - /* Specific Service Data */ -virtual bool getGroupData(const uint32_t &token, std::vector &groups) = 0; -virtual bool getPostData(const uint32_t &token, std::vector &posts, std::vector &cmts) = 0; -virtual bool getPostData(const uint32_t &token, std::vector &posts) = 0; -//Not currently used + virtual bool getBoardsInfo( + const std::list& boardsIds, + std::vector& boardsInfo ) = 0; + + virtual bool getBoardsSummaries(std::list& groupInfo) =0; + + virtual bool getBoardAllContent( + const RsGxsGroupId& boardId, + std::vector& posts, + std::vector& comments, + std::vector& votes ) = 0; + + virtual bool getBoardContent( + const RsGxsGroupId& boardId, + const std::set& contentsIds, + std::vector& posts, + std::vector& comments, + std::vector& votes ) = 0; + + virtual bool editBoard(RsPostedGroup& board) =0; + + virtual bool createBoard(RsPostedGroup& board) =0; + + virtual bool getBoardStatistics(const RsGxsGroupId& boardId,GxsGroupStatistic& stat) =0; + + virtual bool getBoardsServiceStatistics(GxsServiceStatistic& stat) =0; + + enum RS_DEPRECATED RankType {TopRankType, HotRankType, NewRankType }; + + RS_DEPRECATED_FOR(getBoardsInfo) + virtual bool getGroupData( const uint32_t& token, + std::vector &groups ) = 0; + + RS_DEPRECATED_FOR(getBoardsContent) + virtual bool getPostData( + const uint32_t& token, std::vector& posts, + std::vector& cmts, std::vector& vots) = 0; + + RS_DEPRECATED_FOR(getBoardsContent) + virtual bool getPostData( + const uint32_t& token, std::vector& posts, + std::vector& cmts) = 0; + + RS_DEPRECATED_FOR(getBoardsContent) + virtual bool getPostData( + const uint32_t& token, std::vector& posts) = 0; + + //Not currently used //virtual bool getRelatedPosts(const uint32_t &token, std::vector &posts) = 0; /* From RsGxsCommentService */ @@ -134,62 +210,6 @@ virtual bool createPost(uint32_t &token, RsPostedPost &post) = 0; virtual bool updateGroup(uint32_t &token, RsPostedGroup &group) = 0; virtual bool groupShareKeys(const RsGxsGroupId& group,const std::set& peers) = 0 ; + + virtual ~RsPosted(); }; - - -class RsPostedPost -{ - public: - RsPostedPost() - { - //mMeta.mMsgFlags = RsPosted::FLAG_MSGTYPE_POST; - mUpVotes = 0; - mDownVotes = 0; - mComments = 0; - mHaveVoted = false; - - mHotScore = 0; - mTopScore = 0; - mNewScore = 0; - } - - bool calculateScores(rstime_t ref_time); - - RsMsgMetaData mMeta; - std::string mLink; - std::string mNotes; - - bool mHaveVoted; - - // Calculated. - uint32_t mUpVotes; - uint32_t mDownVotes; - uint32_t mComments; - - - // and Calculated Scores:??? - double mHotScore; - double mTopScore; - double mNewScore; - - RsGxsImage mImage; - - /// @see RsSerializable - /*virtual void serial_process( RsGenericSerializer::SerializeJob j, - RsGenericSerializer::SerializeContext& ctx ) - { - RS_SERIAL_PROCESS(mImage); - RS_SERIAL_PROCESS(mMeta); - RS_SERIAL_PROCESS(mLink); - RS_SERIAL_PROCESS(mHaveVoted); - RS_SERIAL_PROCESS(mUpVotes); - RS_SERIAL_PROCESS(mDownVotes); - RS_SERIAL_PROCESS(mComments); - RS_SERIAL_PROCESS(mHotScore); - RS_SERIAL_PROCESS(mTopScore); - RS_SERIAL_PROCESS(mNewScore); - }*/ -}; - - -#endif // RETROSHARE_GXS_RSPOSTED_GUI_INTERFACE_H diff --git a/libretroshare/src/retroshare/rstokenservice.h b/libretroshare/src/retroshare/rstokenservice.h index a7da6ee32..a0fbd7258 100644 --- a/libretroshare/src/retroshare/rstokenservice.h +++ b/libretroshare/src/retroshare/rstokenservice.h @@ -80,7 +80,7 @@ struct RsTokReqOptions { RsTokReqOptions() : mOptions(0), mStatusFilter(0), mStatusMask(0), mMsgFlagMask(0), mMsgFlagFilter(0), mReqType(0), mSubscribeFilter(0), - mSubscribeMask(0), mBefore(0), mAfter(0) {} + mSubscribeMask(0), mBefore(0), mAfter(0),mPriority(GxsRequestPriority::NORMAL) {} /** * Can be one or multiple RS_TOKREQOPT_* @@ -107,6 +107,8 @@ struct RsTokReqOptions // Time range... again applied after Options. rstime_t mBefore; rstime_t mAfter; + + GxsRequestPriority mPriority; }; /*! @@ -181,6 +183,25 @@ public: */ virtual bool requestMsgRelatedInfo(uint32_t &token, uint32_t ansType, const RsTokReqOptions &opts, const std::vector& msgIds) = 0; + /*! + * This request statistics on amount of data held + * number of groups + * number of groups subscribed + * number of messages + * size of db store + * total size of messages + * total size of groups + * @param token + */ + virtual void requestServiceStatistic(uint32_t& token, const RsTokReqOptions &opts) = 0; + + /*! + * To request statistic on a group + * @param token set to value to be redeemed to get statistic + * @param grpId the id of the group + */ + virtual void requestGroupStatistic(uint32_t& token, const RsGxsGroupId& grpId, const RsTokReqOptions &opts) = 0; + /* Poll */ @@ -194,25 +215,6 @@ public: */ virtual GxsRequestStatus requestStatus(const uint32_t token) = 0; - /*! - * This request statistics on amount of data held - * number of groups - * number of groups subscribed - * number of messages - * size of db store - * total size of messages - * total size of groups - * @param token - */ - virtual void requestServiceStatistic(uint32_t& token) = 0; - - /*! - * To request statistic on a group - * @param token set to value to be redeemed to get statistic - * @param grpId the id of the group - */ - virtual void requestGroupStatistic(uint32_t& token, const RsGxsGroupId& grpId) = 0; - /*! * @brief Cancel Request * If this function returns false, it may be that the request has completed @@ -222,6 +224,7 @@ public: */ virtual bool cancelRequest(const uint32_t &token) = 0; +#ifdef TO_REMOVE /** * Block caller while request is being processed. * Useful for blocking API implementation. @@ -231,8 +234,9 @@ public: */ RsTokenService::GxsRequestStatus waitToken( uint32_t token, - std::chrono::milliseconds maxWait = std::chrono::milliseconds(500), - std::chrono::milliseconds checkEvery = std::chrono::milliseconds(2)) + std::chrono::milliseconds maxWait = std::chrono::milliseconds(10000), + std::chrono::milliseconds checkEvery = std::chrono::milliseconds(20), + bool auto_delete_if_unsuccessful=true) { #if defined(__ANDROID__) && (__ANDROID_API__ < 24) auto wkStartime = std::chrono::steady_clock::now(); @@ -241,12 +245,13 @@ LLwaitTokenBeginLabel: #endif auto timeout = std::chrono::steady_clock::now() + maxWait; auto st = requestStatus(token); - while( !(st == RsTokenService::FAILED || st >= RsTokenService::COMPLETE) - && std::chrono::steady_clock::now() < timeout ) + while( !(st == RsTokenService::FAILED || st >= RsTokenService::COMPLETE) && std::chrono::steady_clock::now() < timeout ) { std::this_thread::sleep_for(checkEvery); st = requestStatus(token); } + if(st != RsTokenService::COMPLETE && auto_delete_if_unsuccessful) + cancelRequest(token); #if defined(__ANDROID__) && (__ANDROID_API__ < 24) /* Work around for very slow/old android devices, we don't expect this @@ -274,6 +279,7 @@ LLwaitTokenBeginLabel: return st; } +#endif RS_SET_CONTEXT_DEBUG_LEVEL(2) }; diff --git a/libretroshare/src/retroshare/rstypes.h b/libretroshare/src/retroshare/rstypes.h index 1f5107fe3..59474c99c 100644 --- a/libretroshare/src/retroshare/rstypes.h +++ b/libretroshare/src/retroshare/rstypes.h @@ -38,7 +38,6 @@ #define USE_NEW_CHUNK_CHECKING_CODE typedef Sha1CheckSum RsFileHash ; -typedef Sha1CheckSum RsMessageId ; const uint32_t FT_STATE_FAILED = 0x0000 ; const uint32_t FT_STATE_OKAY = 0x0001 ; @@ -201,7 +200,8 @@ struct FileInfo : RsSerializable std::string path; std::string fname; RsFileHash hash; - std::string ext; + + RS_DEPRECATED std::string ext; /// @deprecated unused uint64_t size; uint64_t avail; /// how much we have @@ -303,10 +303,16 @@ struct DirDetails : RsSerializable type(DIR_TYPE_UNKNOWN), count(0), mtime(0), max_mtime(0) {} + /* G10h4ck do we still need to keep this as void* instead of uint64_t for + * retroshare-gui sake? */ void* parent; + int prow; /* parent row */ + /* G10h4ck do we still need to keep this as void* instead of uint64_t for + * retroshare-gui sake? */ void* ref; + uint8_t type; RsPeerId id; std::string name; @@ -322,36 +328,18 @@ struct DirDetails : RsSerializable /// @see RsSerializable void serial_process(RsGenericSerializer::SerializeJob j, - RsGenericSerializer::SerializeContext& ctx) + RsGenericSerializer::SerializeContext& ctx) override { -#if defined(__GNUC__) && !defined(__clang__) -# pragma GCC diagnostic ignored "-Wstrict-aliasing" -#endif // defined(__GNUC__) && !defined(__clang__) + /* Enforce serialization as uint64_t because void* changes size + * depending (usually 4 bytes on 32bit arch and 8 bytes on 64bit archs) + */ + uint64_t handle = reinterpret_cast(ref); + RS_SERIAL_PROCESS(handle); + ref = reinterpret_cast(handle); - // (Cyril) We have to do this because on some systems (MacOS) uintptr_t is unsigned long which is not well defined. It is always - // preferable to force type serialization to the correct size rather than letting the compiler choose for us. - // /!\ This structure cannot be sent over the network. The serialization would be inconsistent. - - if(sizeof(ref) == 4) - { - std::uint32_t& handle(reinterpret_cast(ref)); - RS_SERIAL_PROCESS(handle); - std::uint32_t& parentHandle(reinterpret_cast(parent)); - RS_SERIAL_PROCESS(parentHandle); - } - else if(sizeof(ref) == 8) - { - std::uint64_t& handle(reinterpret_cast(ref)); - RS_SERIAL_PROCESS(handle); - std::uint64_t& parentHandle(reinterpret_cast(parent)); - RS_SERIAL_PROCESS(parentHandle); - } - else - std::cerr << __PRETTY_FUNCTION__ << ": cannot serialize raw pointer of size " << sizeof(ref) << std::endl; - -#if defined(__GNUC__) && !defined(__clang__) -# pragma GCC diagnostic pop -#endif // defined(__GNUC__) && !defined(__clang__) + uint64_t parentHandle = reinterpret_cast(parent); + RS_SERIAL_PROCESS(parentHandle); + parent = reinterpret_cast(parentHandle); RS_SERIAL_PROCESS(prow); RS_SERIAL_PROCESS(type); @@ -366,6 +354,8 @@ struct DirDetails : RsSerializable RS_SERIAL_PROCESS(children); RS_SERIAL_PROCESS(parent_groups); } + + ~DirDetails() override = default; }; class FileDetail diff --git a/libretroshare/src/retroshare/rswiki.h b/libretroshare/src/retroshare/rswiki.h index feb7dead2..401f2cbab 100644 --- a/libretroshare/src/retroshare/rswiki.h +++ b/libretroshare/src/retroshare/rswiki.h @@ -69,22 +69,17 @@ class CollectionRef std::string CollectionId; }; - -class RsWikiCollection +struct RsWikiCollection: RsGxsGenericGroupData { public: - - RsGroupMetaData mMeta; - std::string mDescription; std::string mCategory; std::string mHashTags; - //std::map linkReferences; + // std::map linkReferences; }; - class RsWikiSnapshot { public: @@ -129,6 +124,11 @@ virtual bool submitComment(uint32_t &token, RsWikiComment &comment) = 0; virtual bool updateCollection(uint32_t &token, RsWikiCollection &collection) = 0; + // Blocking Interfaces. +virtual bool createCollection(RsWikiCollection &collection) = 0; +virtual bool updateCollection(const RsWikiCollection &collection) = 0; +virtual bool getCollections(const std::list groupIds, std::vector &groups) = 0; + }; #endif diff --git a/libretroshare/src/retroshare/rswire.h b/libretroshare/src/retroshare/rswire.h index 580d3f279..34c953cee 100644 --- a/libretroshare/src/retroshare/rswire.h +++ b/libretroshare/src/retroshare/rswire.h @@ -3,7 +3,7 @@ * * * libretroshare: retroshare core library * * * - * Copyright 2012-2012 by Robert Fernie * + * Copyright 2012-2020 by Robert Fernie * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -28,28 +28,50 @@ #include "retroshare/rstokenservice.h" #include "retroshare/rsgxsifacehelper.h" +#include "retroshare/rsgxscommon.h" /* The Main Interface Class - for information about your Peers */ class RsWire; extern RsWire *rsWire; -class RsWireGroup +class RsWireGroup; +typedef std::shared_ptr RsWireGroupSPtr; +typedef std::shared_ptr RsWireGroupConstSPtr; + +class RsWireGroup: public RsGxsGenericGroupData { public: + RsWireGroup(); - RsGroupMetaData mMeta; - std::string mDescription; + std::string mTagline; + std::string mLocation; + + // Images max size should be enforced. + RsGxsImage mHeadshot; // max size? + RsGxsImage mMasthead; // max size? + + // Unserialised stuff --------------------- + + // These are this groups top-level msgs. + uint32_t mGroupPulses; + uint32_t mGroupRepublishes; + uint32_t mGroupLikes; + uint32_t mGroupReplies; + // how do we handle these. TODO + // uint32_t mGroupFollowing; + // uint32_t mGroupFollowers; + + // These are this groups REF / RESPONSE msgs from others. + uint32_t mRefMentions; // TODO how to handle this? + uint32_t mRefRepublishes; + uint32_t mRefLikes; + uint32_t mRefReplies; }; - /*********************************************************************** - * So pulses operate in the following modes. - * - * => Standard, a post to your own group. - * => @User, gets duplicated on each user's group. - * => RT, duplicated as child of original post. + * RsWire - is intended to be a Twitter clone - but fully decentralised. * * From Twitter: * twitter can be: embedded, replied to, favourited, unfavourited, @@ -65,14 +87,54 @@ class RsWireGroup * ***********************************************************************/ -class RsWirePlace -{ - public: - -}; +/************************************************************************ + * Pulse comes in three flavours. + * + * + * Original Msg Pulse + * - Spontaneous msg, on your own group. + * - mPulseType = WIRE_PULSE_TYPE_ORIGINAL_MSG + * - Ref fields are empty. + * + * Reply to a Pulse (i.e Retweet), has two parts. + * as we want the retweet to reference the original, and the original to know about reply. + * This info will be duplicated in two msgs - but allow data to spread easier. + * + * Reply Msg Pulse, will be Top-Level Msg on Publisher's Group. + * - mPulseMode = WIRE_PULSE_TYPE_RESPONSE | WIRE_PULSE_TYPE_REPLY + * - Ref fields refer to Parent (InReplyTo) Msg. + * + * Reply Reference, is Child Msg of Parent Msg, on Parent Publisher's Group. + * - mPulseMode = WIRE_PULSE_TYPE_REFERENCE | WIRE_PULSE_TYPE_REPLY + * - Ref fields refer to Reply Msg. + * - NB: This Msg requires Parent Msg for complete info, while other two are self-contained. + * + * Additionally need to sort out additional relationships. + * - Mentions. + * - Followers. + * - Following. + ***********************************************************************/ +#define WIRE_PULSE_TYPE_ORIGINAL (0x0001) +#define WIRE_PULSE_TYPE_RESPONSE (0x0002) +#define WIRE_PULSE_TYPE_REFERENCE (0x0004) + +#define WIRE_PULSE_RESPONSE_MASK (0x0f00) +#define WIRE_PULSE_TYPE_REPLY (0x0100) +#define WIRE_PULSE_TYPE_REPUBLISH (0x0200) +#define WIRE_PULSE_TYPE_LIKE (0x0400) + +#define WIRE_PULSE_SENTIMENT_NO_SENTIMENT (0x0000) +#define WIRE_PULSE_SENTIMENT_POSITIVE (0x0001) +#define WIRE_PULSE_SENTIMENT_NEUTRAL (0x0002) +#define WIRE_PULSE_SENTIMENT_NEGATIVE (0x0003) + +class RsWirePulse; + +typedef std::shared_ptr RsWirePulseSPtr; +typedef std::shared_ptr RsWirePulseConstSPtr; class RsWirePulse { @@ -80,19 +142,54 @@ class RsWirePulse RsMsgMetaData mMeta; - std::string mPulseText; // all the text is stored here. - std::string mHashTags; + // Store actual Pulse here. + std::string mPulseText; -// These will be added at some point. -// std::string mInReplyPulse; + uint32_t mPulseType; + uint32_t mSentiment; // sentiment can be asserted at any point. -// uint32_t mPulseFlags; + // These Ref to the related (parent or reply) if reply (RESPONSE set) + // Mode RESPONSE REFERENCE + RsGxsGroupId mRefGroupId; // PARENT_GrpId REPLY_GrpId + std::string mRefGroupName; // PARENT_GrpName REPLY_GrpName + RsGxsMessageId mRefOrigMsgId; // PARENT_OrigMsgId REPLY_OrigMsgId + RsGxsId mRefAuthorId; // PARENT_AuthorId REPLY_AuthorId + rstime_t mRefPublishTs; // PARENT_PublishTs REPLY_PublishTs + std::string mRefPulseText; // PARENT_PulseText REPLY_PulseText + uint32_t mRefImageCount; // PARENT_#Images REPLY_#Images -// std::list mMentions; -// std::list mHashTags; -// std::list mUrls; + // Additional Fields for version 2. + // Images, need to enforce 20k limit? + RsGxsImage mImage1; + RsGxsImage mImage2; + RsGxsImage mImage3; + RsGxsImage mImage4; -// RsWirePlace mPlace; + // Below Here is not serialised. + // They are additional fields linking pulses together or parsing elements of msg. + + // functions. + uint32_t ImageCount(); + + // can't have self referencial list, so need to use pointers. + // using SharedPointers to automatically cleanup. + + // Pointer to WireGroups + // mRefGroupPtr is opportunistically filled in, but will often be empty. + RsWireGroupSPtr mRefGroupPtr; // ORIG: N/A, RESP: Parent, REF: Reply Group + RsWireGroupSPtr mGroupPtr; // ORIG: Own, RESP: Own, REF: Parent Group + + // These are the direct children of this message + // split into likes, replies and retweets. + std::list mReplies; + std::list mLikes; + std::list mRepublishes; + + // parsed from msg. + // do we need references..? + std::list mHashTags; + std::list mMentions; + std::list mUrls; }; @@ -107,6 +204,13 @@ class RsWire: public RsGxsIfaceHelper explicit RsWire(RsGxsIface& gxs) : RsGxsIfaceHelper(gxs) {} virtual ~RsWire() {} + /*! + * To acquire a handle to token service handler + * needed to make requests to the service + * @return handle to token service for this gxs service + */ + virtual RsTokenService* getTokenService() = 0; + /* Specific Service Data */ virtual bool getGroupData(const uint32_t &token, std::vector &groups) = 0; virtual bool getPulseData(const uint32_t &token, std::vector &pulses) = 0; @@ -114,6 +218,33 @@ virtual bool getPulseData(const uint32_t &token, std::vector &pulse virtual bool createGroup(uint32_t &token, RsWireGroup &group) = 0; virtual bool createPulse(uint32_t &token, RsWirePulse &pulse) = 0; + // Blocking Interfaces. +virtual bool createGroup(RsWireGroup &group) = 0; +virtual bool updateGroup(const RsWireGroup &group) = 0; +virtual bool getGroups(const std::list grpIds, + std::vector &groups) = 0; + + // New Blocking Interfaces. + // Plan to migrate all GUI calls to these, and remove old interfaces above. + // These are not single requests, but return data graphs for display. +virtual bool createOriginalPulse(const RsGxsGroupId &grpId, RsWirePulseSPtr pPulse) = 0; +virtual bool createReplyPulse(RsGxsGroupId grpId, RsGxsMessageId msgId, + RsGxsGroupId replyWith, uint32_t reply_type, + RsWirePulseSPtr pPulse) = 0; + + + // Provide Individual Group Details for display. +virtual bool getWireGroup(const RsGxsGroupId &groupId, RsWireGroupSPtr &grp) = 0; +virtual bool getWirePulse(const RsGxsGroupId &groupId, const RsGxsMessageId &msgId, RsWirePulseSPtr &pPulse) = 0; + + // Provide list of pulses associated with groups. +virtual bool getPulsesForGroups(const std::list &groupIds, + std::list &pulsePtrs) = 0; + + // Provide pulse, and associated replies / like etc. +virtual bool getPulseFocus(const RsGxsGroupId &groupId, const RsGxsMessageId &msgId, + int type, RsWirePulseSPtr &pPulse) = 0; + }; #endif diff --git a/libretroshare/src/rs_upnp/upnphandler_miniupnp.cc b/libretroshare/src/rs_upnp/upnphandler_miniupnp.cc index 97b972cf1..ca21804af 100644 --- a/libretroshare/src/rs_upnp/upnphandler_miniupnp.cc +++ b/libretroshare/src/rs_upnp/upnphandler_miniupnp.cc @@ -642,14 +642,10 @@ void upnphandler::setExternalPort(unsigned short eport_in) dataMtx.unlock(); /*** UNLOCK MUTEX ***/ } - /* as determined by uPnP */ -bool upnphandler::getInternalAddress(struct sockaddr_storage &addr) +/* as determined by uPnP */ +bool upnphandler::getInternalAddress(struct sockaddr_storage &addr) { -// std::cerr << "UPnPHandler::getInternalAddress() pre Lock!" << std::endl; - dataMtx.lock(); /*** LOCK MUTEX ***/ -// std::cerr << "UPnPHandler::getInternalAddress() postLock!" << std::endl; - - std::cerr << "UPnPHandler::getInternalAddress()" << std::endl; + RS_STACK_MUTEX(dataMtx); // copy to universal addr. sockaddr_storage_clear(addr); @@ -657,18 +653,15 @@ bool upnphandler::getInternalAddress(struct sockaddr_storage &addr) bool valid = (upnpState >= RS_UPNP_S_ACTIVE); - dataMtx.unlock(); /*** UNLOCK MUTEX ***/ + Dbg2() << __PRETTY_FUNCTION__ << " valid: " << valid + << " addr: " << addr << std::endl; return valid; } -bool upnphandler::getExternalAddress(struct sockaddr_storage &addr) +bool upnphandler::getExternalAddress(sockaddr_storage &addr) { -// std::cerr << "UPnPHandler::getExternalAddress() pre Lock!" << std::endl; - dataMtx.lock(); /*** LOCK MUTEX ***/ -// std::cerr << "UPnPHandler::getExternalAddress() postLock!" << std::endl; - - std::cerr << "UPnPHandler::getExternalAddress()" << std::endl; + RS_STACK_MUTEX(dataMtx); // copy to universal addr. sockaddr_storage_clear(addr); @@ -676,7 +669,8 @@ bool upnphandler::getExternalAddress(struct sockaddr_storage &addr) bool valid = (upnpState == RS_UPNP_S_ACTIVE); - dataMtx.unlock(); /*** UNLOCK MUTEX ***/ + Dbg2() << __PRETTY_FUNCTION__ << " valid: " << valid + << " addr: " << addr << std::endl; return valid; } diff --git a/libretroshare/src/rs_upnp/upnphandler_miniupnp.h b/libretroshare/src/rs_upnp/upnphandler_miniupnp.h index d07304724..9449661e0 100644 --- a/libretroshare/src/rs_upnp/upnphandler_miniupnp.h +++ b/libretroshare/src/rs_upnp/upnphandler_miniupnp.h @@ -28,7 +28,7 @@ /* platform independent networking... */ #include "pqi/pqinetwork.h" #include "pqi/pqiassist.h" - +#include "util/rsdebug.h" #include "util/rsthreads.h" class upnpentry @@ -97,7 +97,7 @@ bool shutdown_upnp(); bool initUPnPState(); bool printUPnPState(); - private: +private: bool background_setup_upnp(bool, bool); bool checkUPnPActive(); @@ -123,4 +123,5 @@ bool checkUPnPActive(); /* active port forwarding */ std::list activeForwards; + RS_SET_CONTEXT_DEBUG_LEVEL(1) }; diff --git a/libretroshare/src/rsitems/rsgxscircleitems.cc b/libretroshare/src/rsitems/rsgxscircleitems.cc index 0acdd03de..f93068b5b 100644 --- a/libretroshare/src/rsitems/rsgxscircleitems.cc +++ b/libretroshare/src/rsitems/rsgxscircleitems.cc @@ -35,7 +35,9 @@ RsItem *RsGxsCircleSerialiser::create_item(uint16_t service, uint8_t item_sub_id switch(item_sub_id) { case RS_PKT_SUBTYPE_GXSCIRCLE_GROUP_ITEM: return new RsGxsCircleGroupItem(); +#ifdef TO_REMOVE case RS_PKT_SUBTYPE_GXSCIRCLE_MSG_ITEM: return new RsGxsCircleMsgItem(); +#endif case RS_PKT_SUBTYPE_GXSCIRCLE_SUBSCRIPTION_REQUEST_ITEM: return new RsGxsCircleSubscriptionRequestItem(); default: return NULL ; @@ -46,20 +48,27 @@ void RsGxsCircleSubscriptionRequestItem::clear() { time_stamp = 0 ; time_out = 0 ; - subscription_type = SUBSCRIPTION_REQUEST_UNKNOWN; + subscription_type = RsGxsCircleSubscriptionType::UNKNOWN; } +#ifdef TO_REMOVE void RsGxsCircleMsgItem::serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) { //RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_MSG,mMsg.stuff,"mMsg.stuff") ;//Should be this but not retrocompatible... RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_MSG,mMsg.stuff,"msg.stuff") ; } +void RsGxsCircleMsgItem::clear() +{ + mMsg.stuff.clear(); +} +#endif + void RsGxsCircleSubscriptionRequestItem::serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) { RsTypeSerializer::serial_process(j,ctx,time_stamp,"time_stamp") ; RsTypeSerializer::serial_process(j,ctx,time_out ,"time_out") ; - RsTypeSerializer::serial_process (j,ctx,subscription_type ,"subscription_type") ; + RsTypeSerializer::serial_process (j,ctx,subscription_type ,"subscription_type") ; } void RsGxsCircleGroupItem::serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) @@ -69,11 +78,6 @@ void RsGxsCircleGroupItem::serial_process(RsGenericSerializer::SerializeJob j,Rs RsTypeSerializer::serial_process(j,ctx,subCircleSet,"subCircleSet") ; } -void RsGxsCircleMsgItem::clear() -{ - mMsg.stuff.clear(); -} - void RsGxsCircleGroupItem::clear() { pgpIdSet.TlvClear(); diff --git a/libretroshare/src/rsitems/rsgxscircleitems.h b/libretroshare/src/rsitems/rsgxscircleitems.h index 6338fb368..fb720e070 100644 --- a/libretroshare/src/rsitems/rsgxscircleitems.h +++ b/libretroshare/src/rsitems/rsgxscircleitems.h @@ -64,6 +64,7 @@ public: RsTlvGxsCircleIdSet subCircleSet; }; +#ifdef TO_REMOVE class RsGxsCircleMsgItem : public RsGxsMsgItem { public: @@ -76,6 +77,7 @@ public: RsGxsCircleMsg mMsg; }; +#endif class RsGxsCircleSubscriptionRequestItem: public RsGxsMsgItem { @@ -86,17 +88,11 @@ public: void clear(); - enum { - SUBSCRIPTION_REQUEST_UNKNOWN = 0x00, - SUBSCRIPTION_REQUEST_SUBSCRIBE = 0x01, - SUBSCRIPTION_REQUEST_UNSUBSCRIBE = 0x02 - }; - virtual void serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx); uint32_t time_stamp ; uint32_t time_out ; - uint8_t subscription_type ; + RsGxsCircleSubscriptionType subscription_type ; }; class RsGxsCircleSerialiser : public RsServiceSerializer diff --git a/libretroshare/src/rsitems/rsgxsupdateitems.h b/libretroshare/src/rsitems/rsgxsupdateitems.h index 9e7a194ac..03bd21800 100644 --- a/libretroshare/src/rsitems/rsgxsupdateitems.h +++ b/libretroshare/src/rsitems/rsgxsupdateitems.h @@ -106,12 +106,22 @@ public: RsPeerId peerID; }; +struct RsPeerUpdateTsRecord +{ + RsPeerUpdateTsRecord() : mLastTsReceived(0), mTs(0) {} + + rstime_t mLastTsReceived; // last TS that was sent for this group by this peer ID. + rstime_t mTs; // time at which this TS was sent. +}; + class RsGxsServerGrpUpdate { public: RsGxsServerGrpUpdate() { grpUpdateTS = 0 ; } uint32_t grpUpdateTS; + + std::map grpUpdateTsRecords; }; class RsGxsServerGrpUpdateItem : public RsGxsNetServiceItem, public RsGxsServerGrpUpdate @@ -168,7 +178,13 @@ class RsGxsServerMsgUpdate public: RsGxsServerMsgUpdate() { msgUpdateTS = 0 ;} - uint32_t msgUpdateTS; // local time stamp this group last received a new msg + uint32_t msgUpdateTS; // local time stamp at which this group last received a new msg + + // Now we also store for each peer the last own TS the peer sent and when it did so. This allows to detect when transactions are stuck because of + // outqueues clogging. If that happens, we receive multiple times the same TS from the friend, in which case we do not send the list of msgs + // again until a significant amount of time has passed. These values are obviously initialized to 0. + + std::map msgUpdateTsRecords; }; class RsGxsServerMsgUpdateItem : public RsGxsNetServiceItem, public RsGxsServerMsgUpdate diff --git a/libretroshare/src/rsitems/rsitem.h b/libretroshare/src/rsitems/rsitem.h index d8495ab58..1c8ea9789 100644 --- a/libretroshare/src/rsitems/rsitem.h +++ b/libretroshare/src/rsitems/rsitem.h @@ -45,11 +45,12 @@ struct RsItem : RsMemoryManagement::SmallObject, RsSerializable /// TODO: Do this make sense with the new serialization system? virtual void clear() = 0; + /// @deprecated use << ostream operator instead + RS_DEPRECATED_FOR("<< ostream operator") virtual std::ostream &print(std::ostream &out, uint16_t /* indent */ = 0) { RsGenericSerializer::SerializeContext ctx( - NULL, 0, RsGenericSerializer::FORMAT_BINARY, - RsGenericSerializer::SERIALIZATION_FLAG_NONE ); + nullptr, 0, RsSerializationFlags::NONE ); serial_process(RsGenericSerializer::PRINT,ctx); return out; } diff --git a/libretroshare/src/rsitems/rsmsgitems.cc b/libretroshare/src/rsitems/rsmsgitems.cc index 99fc342a5..8060248f4 100644 --- a/libretroshare/src/rsitems/rsmsgitems.cc +++ b/libretroshare/src/rsitems/rsmsgitems.cc @@ -83,19 +83,19 @@ void RsMsgTagType::serial_process(RsGenericSerializer::SerializeJob j,RsGenericS void RsMsgTags::serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) { - RsTypeSerializer::serial_process(j,ctx,msgId,"msgId") ; + RsTypeSerializer::serial_process(j,ctx,msgId,"msgId") ; #warning this is not the correct way to serialise here. We should directly call serial_process >() but for backward compatibility, we cannot - if(j == RsGenericSerializer::DESERIALIZE) - while(ctx.mOffset < ctx.mSize) - { - uint32_t n ; + if(j == RsGenericSerializer::DESERIALIZE) + while(ctx.mOffset < ctx.mSize) + { + uint32_t n = 0;// No real need to initialize but otherwise the compiler complains. RsTypeSerializer::serial_process(j,ctx,n,"tagIds element") ; tagIds.push_back(n) ; - } - else - for(std::list::iterator it(tagIds.begin());it!=tagIds.end();++it) + } + else + for(std::list::iterator it(tagIds.begin());it!=tagIds.end();++it) RsTypeSerializer::serial_process(j,ctx,*it,"tagIds element") ; } @@ -147,8 +147,8 @@ void RsMsgItem::serial_process(RsGenericSerializer::SerializeJob j,RsGenericSeri RsTypeSerializer::serial_process(j,ctx,attachment,"attachment"); - if(ctx.mFlags & RsServiceSerializer::SERIALIZATION_FLAG_CONFIG) - RsTypeSerializer::serial_process(j,ctx,msgId,"msgId"); + if(!!(ctx.mFlags & RsSerializationFlags::CONFIG)) + RS_SERIAL_PROCESS(msgId); } void RsMsgTagType::clear() diff --git a/libretroshare/src/rsitems/rsmsgitems.h b/libretroshare/src/rsitems/rsmsgitems.h index ad84ec3ba..27447aec0 100644 --- a/libretroshare/src/rsitems/rsmsgitems.h +++ b/libretroshare/src/rsitems/rsmsgitems.h @@ -19,8 +19,7 @@ * along with this program. If not, see . * * * *******************************************************************************/ -#ifndef RS_MSG_ITEMS_H -#define RS_MSG_ITEMS_H +#pragma once #include @@ -33,10 +32,6 @@ #include "serialiser/rstlvfileitem.h" #include "grouter/grouteritems.h" -#if 0 -#include "serialiser/rstlvtypes.h" -#include "serialiser/rstlvfileitem.h" -#endif /**************************************************************************/ @@ -73,6 +68,7 @@ const uint32_t RS_MSG_FLAGS_LOAD_EMBEDDED_IMAGES = 0x00040000; const uint32_t RS_MSG_FLAGS_DECRYPTED = 0x00080000; const uint32_t RS_MSG_FLAGS_ROUTED = 0x00100000; const uint32_t RS_MSG_FLAGS_PUBLISH_KEY = 0x00200000; +const uint32_t RS_MSG_FLAGS_SPAM = 0x00400000; const uint32_t RS_MSG_FLAGS_SYSTEM = RS_MSG_FLAGS_USER_REQUEST | RS_MSG_FLAGS_FRIEND_RECOMMENDATION | RS_MSG_FLAGS_PUBLISH_KEY; @@ -218,17 +214,12 @@ class RsMsgParentId : public RsMessageItem class RsMsgSerialiser: public RsServiceSerializer { - public: - RsMsgSerialiser(SerializationFlags flags = RsServiceSerializer::SERIALIZATION_FLAG_NONE) - :RsServiceSerializer(RS_SERVICE_TYPE_MSG,RsGenericSerializer::FORMAT_BINARY,flags){} +public: + RsMsgSerialiser( + RsSerializationFlags flags = RsSerializationFlags::NONE ): + RsServiceSerializer(RS_SERVICE_TYPE_MSG, flags){} - virtual ~RsMsgSerialiser() {} + RsItem* create_item(uint16_t service,uint8_t type) const override; - virtual RsItem *create_item(uint16_t service,uint8_t type) const ; + ~RsMsgSerialiser() override = default; }; - -/**************************************************************************/ - -#endif /* RS_MSG_ITEMS_H */ - - diff --git a/libretroshare/src/rsitems/rsnxsitems.cc b/libretroshare/src/rsitems/rsnxsitems.cc index ab596a99b..7331bbaf5 100644 --- a/libretroshare/src/rsitems/rsnxsitems.cc +++ b/libretroshare/src/rsitems/rsnxsitems.cc @@ -173,9 +173,6 @@ void RsNxsMsg::clear() meta.TlvClear(); } -std::ostream&RsNxsMsg::print(std::ostream& out, uint16_t /*indent*/) -{ return out; } - void RsNxsGrp::clear() { grpId.clear(); diff --git a/libretroshare/src/rsitems/rsnxsitems.h b/libretroshare/src/rsitems/rsnxsitems.h index 56920a058..269f9c49a 100644 --- a/libretroshare/src/rsitems/rsnxsitems.h +++ b/libretroshare/src/rsitems/rsnxsitems.h @@ -378,7 +378,6 @@ struct RsNxsMsg : RsNxsItem RsGenericSerializer::SerializeContext& ctx ); virtual void clear(); - virtual std::ostream &print(std::ostream& out, uint16_t indent); uint8_t pos; /// used for splitting up msg uint8_t count; /// number of split up messages diff --git a/libretroshare/src/rsitems/rsphotoitems.cc b/libretroshare/src/rsitems/rsphotoitems.cc index e462d8ee9..f9200a83c 100644 --- a/libretroshare/src/rsitems/rsphotoitems.cc +++ b/libretroshare/src/rsitems/rsphotoitems.cc @@ -26,90 +26,60 @@ #include "serialiser/rstlvbinary.h" #include "serialiser/rstypeserializer.h" -#define GXS_PHOTO_SERIAL_DEBUG - - RsItem *RsGxsPhotoSerialiser::create_item(uint16_t service, uint8_t item_sub_id) const { - if(service != RS_SERVICE_GXS_TYPE_PHOTO) - return NULL ; + if(service != RS_SERVICE_GXS_TYPE_PHOTO) + return NULL ; - switch(item_sub_id) - { - case RS_PKT_SUBTYPE_PHOTO_COMMENT_ITEM: return new RsGxsPhotoCommentItem() ; - case RS_PKT_SUBTYPE_PHOTO_SHOW_ITEM: return new RsGxsPhotoAlbumItem() ; - case RS_PKT_SUBTYPE_PHOTO_ITEM: return new RsGxsPhotoPhotoItem() ; - default: - return NULL ; - } + switch(item_sub_id) + { + case RS_PKT_SUBTYPE_PHOTO_SHOW_ITEM: return new RsGxsPhotoPhotoItem() ; + case RS_PKT_SUBTYPE_PHOTO_ITEM: return new RsGxsPhotoAlbumItem() ; + default: + return RsGxsCommentSerialiser::create_item(service,item_sub_id) ; + } } void RsGxsPhotoAlbumItem::serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) { - RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_CAPTION, album.mCaption, "mCaption"); - RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_CATEGORY, album.mCategory, "mCategory"); - RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_DESCR, album.mDescription, "mDescription"); - RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_HASH_TAG, album.mHashTags, "mHashTags"); - RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_MSG, album.mOther, "mOther"); - RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_PATH, album.mPhotoPath, "mPhotoPath"); - RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_NAME, album.mPhotographer, "mPhotographer"); - RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_DATE, album.mWhen, "mWhen"); - RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_LOCATION, album.mWhere, "mWhere"); - RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_PIC_TYPE, album.mThumbnail.type,"mThumbnail.type"); + RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_UINT32_PARAM,album.mShareMode,"mShareMode"); + RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_CAPTION, album.mCaption, "mCaption"); + RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_DESCR, album.mDescription, "mDescription"); + RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_NAME, album.mPhotographer, "mPhotographer"); + RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_LOCATION, album.mWhere, "mWhere"); + RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_DATE, album.mWhen, "mWhen"); - RsTlvBinaryDataRef b(RS_SERVICE_GXS_TYPE_PHOTO, album.mThumbnail.data, album.mThumbnail.size); - RsTypeSerializer::serial_process(j, ctx, b, "thumbnail binary data") ; + album.mThumbnail.serial_process(j, ctx); } void RsGxsPhotoPhotoItem::serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) { - RsTypeSerializer::serial_process(j, ctx, TLV_TYPE_STR_CAPTION, photo.mCaption, "mCaption"); - RsTypeSerializer::serial_process(j, ctx, TLV_TYPE_STR_CATEGORY, photo.mCategory, "mCategory"); - RsTypeSerializer::serial_process(j, ctx, TLV_TYPE_STR_DESCR, photo.mDescription, "mDescription"); - RsTypeSerializer::serial_process(j, ctx, TLV_TYPE_STR_HASH_TAG, photo.mHashTags, "mHashTags"); - RsTypeSerializer::serial_process(j, ctx, TLV_TYPE_STR_MSG, photo.mOther, "mOther"); - RsTypeSerializer::serial_process(j, ctx, TLV_TYPE_STR_PIC_AUTH, photo.mPhotographer, "mPhotographer"); - RsTypeSerializer::serial_process(j, ctx, TLV_TYPE_STR_DATE, photo.mWhen, "mWhen"); - RsTypeSerializer::serial_process(j, ctx, TLV_TYPE_STR_LOCATION, photo.mWhere, "mWhere"); - RsTypeSerializer::serial_process(j, ctx, TLV_TYPE_STR_PIC_TYPE, photo.mThumbnail.type, "mThumbnail.type"); - - RsTlvBinaryDataRef b(RS_SERVICE_GXS_TYPE_PHOTO, photo.mThumbnail.data, photo.mThumbnail.size); - RsTypeSerializer::serial_process(j, ctx, b, "mThumbnail") ; -} -void RsGxsPhotoCommentItem::serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) -{ - RsTypeSerializer::serial_process (j,ctx,TLV_TYPE_STR_COMMENT,comment.mComment,"mComment"); - RsTypeSerializer::serial_process(j,ctx,comment.mCommentFlag,"mCommentFlag"); + RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_DESCR, photo.mDescription, "mDescription"); + RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_UINT32_PARAM,photo.mOrder,"mOrder"); + photo.mLowResImage.serial_process(j, ctx); + photo.mPhotoFile.serial_process(j, ctx); } void RsGxsPhotoAlbumItem::clear() { + album.mShareMode = RSPHOTO_SHAREMODE_LOWRESONLY; album.mCaption.clear(); - album.mCategory.clear(); album.mDescription.clear(); - album.mHashTags.clear(); - album.mOther.clear(); - album.mPhotoPath.clear(); album.mPhotographer.clear(); - album.mWhen.clear(); album.mWhere.clear(); - album.mThumbnail.deleteImage(); -} + album.mWhen.clear(); + album.mThumbnail.clear(); -void RsGxsPhotoCommentItem::clear() -{ - comment.mComment.clear(); - comment.mCommentFlag = 0; + // not saved + album.mAutoDownload = false; } void RsGxsPhotoPhotoItem::clear() { - photo.mCaption.clear(); - photo.mCategory.clear(); photo.mDescription.clear(); - photo.mHashTags.clear(); - photo.mOther.clear(); - photo.mPhotographer.clear(); - photo.mWhen.clear(); - photo.mWhere.clear(); - photo.mThumbnail.deleteImage(); + photo.mOrder = 0; + photo.mLowResImage.clear(); + photo.mPhotoFile.clear(); + + // not saved + photo.mPath.clear(); } diff --git a/libretroshare/src/rsitems/rsphotoitems.h b/libretroshare/src/rsitems/rsphotoitems.h index 92f99843c..a504ae29b 100644 --- a/libretroshare/src/rsitems/rsphotoitems.h +++ b/libretroshare/src/rsitems/rsphotoitems.h @@ -26,6 +26,7 @@ #include "rsitems/rsserviceids.h" #include "rsitems/rsgxsitems.h" +#include "rsitems/rsgxscommentitems.h" #include "serialiser/rsserial.h" #include "serialiser/rsserializer.h" @@ -34,7 +35,6 @@ const uint8_t RS_PKT_SUBTYPE_PHOTO_ITEM = 0x02; const uint8_t RS_PKT_SUBTYPE_PHOTO_SHOW_ITEM = 0x03; -const uint8_t RS_PKT_SUBTYPE_PHOTO_COMMENT_ITEM = 0x04; class RsGxsPhotoAlbumItem : public RsGxsGrpItem { @@ -66,24 +66,11 @@ public: RsPhotoPhoto photo; }; -class RsGxsPhotoCommentItem : public RsGxsMsgItem +class RsGxsPhotoSerialiser : public RsGxsCommentSerialiser { public: - RsGxsPhotoCommentItem(): RsGxsMsgItem(RS_SERVICE_GXS_TYPE_PHOTO, RS_PKT_SUBTYPE_PHOTO_COMMENT_ITEM) {} - virtual ~RsGxsPhotoCommentItem() {} - void clear(); - - virtual void serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx); - - RsPhotoComment comment; -}; - -class RsGxsPhotoSerialiser : public RsServiceSerializer -{ -public: - - RsGxsPhotoSerialiser() :RsServiceSerializer(RS_SERVICE_GXS_TYPE_PHOTO) {} + RsGxsPhotoSerialiser() :RsGxsCommentSerialiser(RS_SERVICE_GXS_TYPE_PHOTO) {} virtual ~RsGxsPhotoSerialiser() {} virtual RsItem *create_item(uint16_t service, uint8_t item_sub_id) const; diff --git a/libretroshare/src/rsitems/rsserviceids.h b/libretroshare/src/rsitems/rsserviceids.h index 3f690cf45..21c399fc0 100644 --- a/libretroshare/src/rsitems/rsserviceids.h +++ b/libretroshare/src/rsitems/rsserviceids.h @@ -64,6 +64,7 @@ enum class RsServiceType : uint16_t GXS_TRANS = 0x0230, JSONAPI = 0x0240, FORUMS_CONFIG = 0x0315, + POSTED_CONFIG = 0x0316, CHANNELS_CONFIG = 0x0317, RTT = 0x1011, /// Round Trip Time @@ -123,7 +124,9 @@ RS_DEPRECATED_FOR(RsServiceType) const uint16_t RS_SERVICE_GXS_TYPE_REPUTATION RS_DEPRECATED_FOR(RsServiceType) const uint16_t RS_SERVICE_TYPE_GXS_RECOGN = 0x0220; RS_DEPRECATED_FOR(RsServiceType) const uint16_t RS_SERVICE_TYPE_GXS_TRANS = 0x0230; RS_DEPRECATED_FOR(RsServiceType) const uint16_t RS_SERVICE_TYPE_JSONAPI = 0x0240; +/// used to save notification records in GXS and possible other service-based configuration RS_DEPRECATED_FOR(RsServiceType) const uint16_t RS_SERVICE_GXS_TYPE_FORUMS_CONFIG = 0x0315; +RS_DEPRECATED_FOR(RsServiceType) const uint16_t RS_SERVICE_GXS_TYPE_POSTED_CONFIG = 0x0316; RS_DEPRECATED_FOR(RsServiceType) const uint16_t RS_SERVICE_GXS_TYPE_CHANNELS_CONFIG = 0x0317; // Experimental Services. diff --git a/libretroshare/src/rsitems/rswireitems.cc b/libretroshare/src/rsitems/rswireitems.cc index eb3896f17..a618336f7 100644 --- a/libretroshare/src/rsitems/rswireitems.cc +++ b/libretroshare/src/rsitems/rswireitems.cc @@ -29,31 +29,69 @@ RsItem *RsGxsWireSerialiser::create_item(uint16_t service,uint8_t item_subtype) const { - if(service != RS_SERVICE_GXS_TYPE_WIRE) - return NULL ; + if(service != RS_SERVICE_GXS_TYPE_WIRE) + return NULL ; - switch(item_subtype) - { - case RS_PKT_SUBTYPE_WIRE_GROUP_ITEM: return new RsGxsWireGroupItem(); - case RS_PKT_SUBTYPE_WIRE_PULSE_ITEM: return new RsGxsWirePulseItem(); - default: - return NULL ; - } + switch(item_subtype) + { + case RS_PKT_SUBTYPE_WIRE_GROUP_ITEM: return new RsGxsWireGroupItem(); + case RS_PKT_SUBTYPE_WIRE_PULSE_ITEM: return new RsGxsWirePulseItem(); + default: + return NULL ; + } } void RsGxsWireGroupItem::clear() { - group.mDescription.clear(); + group.mTagline.clear(); + group.mLocation.clear(); + group.mHeadshot.clear(); + group.mMasthead.clear(); } void RsGxsWireGroupItem::serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) { - RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_DESCR,group.mDescription,"group.mDescription") ; + RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_DESCR,group.mTagline,"group.mTagline") ; + RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_LOCATION,group.mLocation,"group.mLocation") ; + group.mHeadshot.serial_process(j, ctx); + group.mMasthead.serial_process(j, ctx); +} + +void RsGxsWirePulseItem::clear() +{ + pulse.mPulseText.clear(); + pulse.mPulseType = 0; + pulse.mSentiment = 0; + pulse.mRefGroupId.clear(); + pulse.mRefGroupName.clear(); + pulse.mRefOrigMsgId.clear(); + pulse.mRefAuthorId.clear(); + pulse.mRefPublishTs = 0; + pulse.mRefPulseText.clear(); + pulse.mRefImageCount = 0; + + pulse.mImage1.clear(); + pulse.mImage2.clear(); + pulse.mImage3.clear(); + pulse.mImage4.clear(); } void RsGxsWirePulseItem::serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) { - RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_MSG,pulse.mPulseText,"pulse.mPulseText") ; - RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_HASH_TAG,pulse.mHashTags,"pulse.mHashTags") ; + RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_MSG,pulse.mPulseText,"pulse.mPulseText") ; + RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_UINT32_PARAM,pulse.mPulseType,"pulse.mPulseType") ; + RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_UINT32_PARAM,pulse.mSentiment,"pulse.mSentiment") ; + RsTypeSerializer::serial_process(j,ctx,pulse.mRefGroupId,"pulse.mRefGroupId") ; + RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_NAME,pulse.mRefGroupName,"pulse.mRefGroupName") ; + RsTypeSerializer::serial_process(j,ctx,pulse.mRefOrigMsgId,"pulse.mRefOrigMsgId") ; + RsTypeSerializer::serial_process(j,ctx,pulse.mRefAuthorId,"pulse.mRefAuthorId") ; + RsTypeSerializer::serial_process(j,ctx,pulse.mRefPublishTs,"pulse.mRefPublishTs") ; + RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_MSG,pulse.mRefPulseText,"pulse.mRefPulseText") ; + RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_UINT32_PARAM,pulse.mRefImageCount,"pulse.mRefImageCount") ; + + pulse.mImage1.serial_process(j, ctx); + pulse.mImage2.serial_process(j, ctx); + pulse.mImage3.serial_process(j, ctx); + pulse.mImage4.serial_process(j, ctx); } diff --git a/libretroshare/src/rsserver/p3face-server.cc b/libretroshare/src/rsserver/p3face-server.cc index 54605333b..9426b0471 100644 --- a/libretroshare/src/rsserver/p3face-server.cc +++ b/libretroshare/src/rsserver/p3face-server.cc @@ -41,11 +41,12 @@ #include "services/rseventsservice.h" -/**** -#define DEBUG_TICK 1 -****/ +/******************* +#define TICK_DEBUG 1 +*******************/ #define WARN_BIG_CYCLE_TIME (0.2) + #ifdef WINDOWS_SYS #include "util/rstime.h" #include @@ -73,14 +74,13 @@ static double getCurrentTS() // In some cases (VOIP) it's likely that we will need to set them temporarily to a very low // value, in order to favor a fast feedback -const double RsServer::minTimeDelta = 0.05; // 25; -const double RsServer::maxTimeDelta = 0.2; -const double RsServer::kickLimit = 0.15; +const double RsServer::minTickInterval = 0.05; +const double RsServer::maxTickInterval = 0.2; RsServer::RsServer() : - coreMutex("RsServer"), mShutdownCallback([](int){}), - coreReady(false) + coreMutex("RsServer"), mShutdownCallback([](int){}), + coreReady(false) { { RsEventsService* tmpRsEvtPtr = new RsEventsService(); @@ -108,21 +108,20 @@ RsServer::RsServer() : msgSrv = NULL; chatSrv = NULL; mStatusSrv = NULL; - mGxsTunnels = NULL; + mGxsTunnels = NULL; - mMin = 0; - mLoop = 0; + mLastts = getCurrentTS(); + mTickInterval = maxTickInterval ; + mAvgRunDuration = 0; + mLastRunDuration = 0; + mCycle1 = 0; + mCycle2 = 0; + mCycle3 = 0; + mCycle4 = 0; + /* caches (that need ticking) */ - mLastts = getCurrentTS(); - mLastSec = 0; /* for the slower ticked stuff */ - mTimeDelta = 0.25 ; - - mAvgTickRate = mTimeDelta; - - /* caches (that need ticking) */ - - /* Config */ + /* config */ mConfigMgr = NULL; mGeneralConfig = NULL; } @@ -132,143 +131,137 @@ RsServer::~RsServer() delete mGxsTrans; } - /* General Internal Helper Functions - ----> MUST BE LOCKED! - */ +// General Internal Helper Functions ----> MUST BE LOCKED! - - - /* Thread Fn: Run the Core */ void RsServer::threadTick() { - rstime::rs_usleep(mTimeDelta * 1000000); - - double ts = getCurrentTS(); - double delta = ts - mLastts; - - /* for the fast ticked stuff */ - if (delta > mTimeDelta) - { -#ifdef DEBUG_TICK - std::cerr << "Delta: " << delta << std::endl; - std::cerr << "Time Delta: " << mTimeDelta << std::endl; - std::cerr << "Avg Tick Rate: " << mAvgTickRate << std::endl; +#ifdef TICK_DEBUG + RsDbg() << "TICK_DEBUG ticking interval "<< mTickInterval << std::endl; #endif - mLastts = ts; +// we try to tick at a regular interval which depends on the load +// if there is time left, we sleep + double timeToSleep = mTickInterval - mAvgRunDuration; - /******************************** RUN SERVER *****************/ - lockRsCore(); +// never sleep less than 50 ms + if (timeToSleep < 0.050) + timeToSleep = 0.050; - int moreToTick = pqih->tick(); +#ifdef TICK_DEBUG + RsDbg() << "TICK_DEBUG will sleep " << (int) (1000 * timeToSleep) << " ms" << std::endl; +#endif + rstime::rs_usleep(timeToSleep * 1000000); -#ifdef DEBUG_TICK - std::cerr << "RsServer::run() ftserver->tick(): moreToTick: " << moreToTick << std::endl; + double ts = getCurrentTS(); + mLastts = ts; + +// stuff we do always +// tick the core +#ifdef TICK_DEBUG + RsDbg() << "TICK_DEBUG ticking server" << std::endl; +#endif + lockRsCore(); + int moreToTick = pqih->tick(); + unlockRsCore(); +// tick the managers +#ifdef TICK_DEBUG + RsDbg() << "TICK_DEBUG ticking mPeerMgr" << std::endl; +#endif + mPeerMgr->tick(); +#ifdef TICK_DEBUG + RsDbg() << "TICK_DEBUG ticking mLinkMgr" << std::endl; +#endif + mLinkMgr->tick(); +#ifdef TICK_DEBUG + RsDbg() << "TICK_DEBUG ticking mNetMgr" << std::endl; +#endif + mNetMgr->tick(); + + +// stuff we do every second + if (ts - mCycle1 > 1) + { +#ifdef TICK_DEBUG + RsDbg() << "TICK_DEBUG every second" << std::endl; +#endif + // slow services + if (rsPlugins) + rsPlugins->slowTickPlugins((rstime_t)ts); + // UDP keepalive + // tou_tick_stunkeepalive(); + // other stuff to tick + // update(); + mCycle1 = ts; + } + +// stuff we do every five seconds + if (ts - mCycle2 > 5) + { +#ifdef TICK_DEBUG + RsDbg() << "TICK_DEBUG every 5 seconds" << std::endl; +#endif + // save stuff + mConfigMgr->tick(); + mCycle2 = ts; + } + +// stuff we do every minute + if (ts - mCycle3 > 60) + { +#ifdef TICK_DEBUG + RsDbg() << "TICK_DEBUG every 60 seconds" << std::endl; +#endif + // force saving FileTransferStatus TODO + // ftserver->saveFileTransferStatus(); + // see if we need to resave certs + // AuthSSL::getAuthSSL()->CheckSaveCertificates(); + mCycle3 = ts; + } + +// stuff we do every hour + if (ts - mCycle4 > 3600) + { +#ifdef TICK_DEBUG + RsDbg() << "TICK_DEBUG every hour" << std::endl; +#endif + mCycle4 = ts; + } + +// ticking is done, now compute new values of mLastRunDuration, mAvgRunDuration and mTickInterval + ts = getCurrentTS(); + mLastRunDuration = ts - mLastts; + +// low-pass filter and don't let mAvgRunDuration exceeds maxTickInterval + mAvgRunDuration = 0.1 * mLastRunDuration + 0.9 * mAvgRunDuration; + if (mAvgRunDuration > maxTickInterval) + mAvgRunDuration = maxTickInterval; + +#ifdef TICK_DEBUG + RsDbg() << "TICK_DEBUG new mLastRunDuration " << mLastRunDuration << " mAvgRunDuration " << mAvgRunDuration << std::endl; + if (mLastRunDuration > WARN_BIG_CYCLE_TIME) + RsDbg() << "TICK_DEBUG excessively long cycle time " << mLastRunDuration << std::endl; +#endif + +// if the core has returned that there is more to tick we decrease the ticking interval, else we increse it +// this should be studied closer as I dont think that the core ever returns 1 +#ifdef TICK_DEBUG + RsDbg() << "TICK_DEBUG moreToTick " << moreToTick << std::endl; +#endif + if (moreToTick == 1) + mTickInterval = 0.9 * mTickInterval; + else + mTickInterval = 1.1 * mTickInterval; +#ifdef TICK_DEBUG + RsDbg() << "TICK_DEBUG new tick interval " << mTickInterval << std::endl; #endif - unlockRsCore(); - - /* tick the Managers */ - mPeerMgr->tick(); - mLinkMgr->tick(); - mNetMgr->tick(); - /******************************** RUN SERVER *****************/ - - /* adjust tick rate depending on whether there is more. - */ - - mAvgTickRate = 0.2 * mTimeDelta + 0.8 * mAvgTickRate; - - if (1 == moreToTick) - { - mTimeDelta = 0.9 * mAvgTickRate; - if (mTimeDelta > kickLimit) - { - /* force next tick in one sec - * if we are reading data. - */ - mTimeDelta = kickLimit; - mAvgTickRate = kickLimit; - } - } - else - { - mTimeDelta = 1.1 * mAvgTickRate; - } - - /* limiter */ - if (mTimeDelta < minTimeDelta) - { - mTimeDelta = minTimeDelta; - } - else if (mTimeDelta > maxTimeDelta) - { - mTimeDelta = maxTimeDelta; - } - - /* Fast Updates */ - - - /* now we have the slow ticking stuff */ - /* stuff ticked once a second (but can be slowed down) */ - if ((int) ts > mLastSec) - { - mLastSec = (int) ts; - - // Every second! (UDP keepalive). - //tou_tick_stunkeepalive(); - - // every five loops (> 5 secs) - if (mLoop % 5 == 0) - { - // update_quick_stats(); - - // Update All Every 5 Seconds. - // These Update Functions do the locking themselves. -#ifdef DEBUG_TICK - std::cerr << "RsServer::run() Updates()" << std::endl; -#endif - - mConfigMgr->tick(); /* saves stuff */ - - } - - // every 60 loops (> 1 min) - if (++mLoop >= 60) - { - mLoop = 0; - - /* force saving FileTransferStatus TODO */ - //ftserver->saveFileTransferStatus(); - - /* see if we need to resave certs */ - //AuthSSL::getAuthSSL()->CheckSaveCertificates(); - - /* hour loop */ - if (++mMin >= 60) - { - mMin = 0; - } - } - - /* Tick slow services */ - if(rsPlugins) - rsPlugins->slowTickPlugins((rstime_t)ts); - - // slow update tick as well. - // update(); - } // end of slow tick. - - } // end of only once a second. - -#ifdef DEBUG_TICK - double endCycleTs = getCurrentTS(); - double cycleTime = endCycleTs - ts; - if (cycleTime > WARN_BIG_CYCLE_TIME) - { - std::string out; - rs_sprintf(out, "RsServer::run() WARNING Excessively Long Cycle Time: %g secs => Please DEBUG", cycleTime); - std::cerr << out << std::endl; - } +// keep the tick interval target within allowed limits + if (mTickInterval < minTickInterval) + mTickInterval = minTickInterval; + else if (mTickInterval > maxTickInterval) + mTickInterval = maxTickInterval; +#ifdef TICK_DEBUG + RsDbg() << "TICK_DEBUG new tick interval after limiter " << mTickInterval << std::endl; #endif } + diff --git a/libretroshare/src/rsserver/p3face.h b/libretroshare/src/rsserver/p3face.h index 320c6f991..661cb244f 100644 --- a/libretroshare/src/rsserver/p3face.h +++ b/libretroshare/src/rsserver/p3face.h @@ -161,7 +161,9 @@ public: p3ChatService *chatSrv; p3StatusService *mStatusSrv; p3GxsTunnelService *mGxsTunnels; +#ifdef RS_USE_I2P_BOB p3I2pBob *mI2pBob; +#endif // This list contains all threaded services. It will be used to shut them down properly. @@ -172,8 +174,8 @@ public: // p3Posted *mPosted; // p3PhotoService *mPhoto; // p3GxsCircles *mGxsCircles; -// p3GxsNetService *mGxsNetService; -// p3IdService *mGxsIdService; +// p3GxsNetService *mGxsNetService; +// p3IdService *mGxsIdService; // p3GxsForums *mGxsForums; // p3GxsChannels *mGxsChannels; // p3Wire *mWire; @@ -188,16 +190,14 @@ public: // Worker Data..... - int mMin ; - int mLoop ; - int mLastts ; - long mLastSec ; - double mAvgTickRate ; - double mTimeDelta ; + double mLastts; + double mTickInterval; + double mLastRunDuration; + double mAvgRunDuration; + double mCycle1, mCycle2, mCycle3, mCycle4; - static const double minTimeDelta; // 25; - static const double maxTimeDelta; - static const double kickLimit; + static const double minTickInterval; + static const double maxTickInterval; /// @see RsControl::setShutdownCallback std::function mShutdownCallback; diff --git a/libretroshare/src/rsserver/p3msgs.cc b/libretroshare/src/rsserver/p3msgs.cc index 48a694fb0..b877c0282 100644 --- a/libretroshare/src/rsserver/p3msgs.cc +++ b/libretroshare/src/rsserver/p3msgs.cc @@ -376,11 +376,15 @@ bool p3Msgs::getMessageTagTypes(MsgTagType& tags) } bool p3Msgs::MessageStar(const std::string &mid, bool star) - { return mMsgSrv->setMsgFlag(mid, star ? RS_MSG_FLAGS_STAR : 0, RS_MSG_FLAGS_STAR); } +bool p3Msgs::MessageJunk(const std::string &mid, bool junk) +{ + return mMsgSrv->setMsgFlag(mid, junk ? RS_MSG_FLAGS_SPAM : 0, RS_MSG_FLAGS_SPAM); +} + bool p3Msgs::setMessageTagType(uint32_t tagId, std::string& text, uint32_t rgb_color) { return mMsgSrv->setMessageTagType(tagId, text, rgb_color); diff --git a/libretroshare/src/rsserver/p3msgs.h b/libretroshare/src/rsserver/p3msgs.h index 955018d26..91d044ee5 100644 --- a/libretroshare/src/rsserver/p3msgs.h +++ b/libretroshare/src/rsserver/p3msgs.h @@ -78,6 +78,7 @@ public: virtual bool MessageReplied(const std::string &mid, bool replied); virtual bool MessageForwarded(const std::string &mid, bool forwarded); virtual bool MessageStar(const std::string &mid, bool star); + virtual bool MessageJunk(const std::string &mid, bool junk); virtual bool MessageLoadEmbeddedImages(const std::string &mid, bool load); virtual bool getMsgParentId(const std::string &msgId, std::string &msgParentId); diff --git a/libretroshare/src/rsserver/p3peers.cc b/libretroshare/src/rsserver/p3peers.cc index d4c99d259..35be7e728 100644 --- a/libretroshare/src/rsserver/p3peers.cc +++ b/libretroshare/src/rsserver/p3peers.cc @@ -3,8 +3,9 @@ * * * libretroshare: retroshare core library * * * - * Copyright 2004-2008 by Robert Fernie * - * Copyright (C) 2015-2018 Gioacchino Mazzurco * + * Copyright (C) 2004-2008 Robert Fernie * + * Copyright (C) 2015-2020 Gioacchino Mazzurco * + * Copyright (C) 2020 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -20,7 +21,7 @@ * along with this program. If not, see . * * * *******************************************************************************/ -#include "util/radix64.h" + #include "pgp/pgpkeyutil.h" #include "rsserver/p3peers.h" @@ -35,7 +36,8 @@ #include "retroshare/rsinit.h" #include "retroshare/rsfiles.h" #include "util/rsurl.h" - +#include "util/radix64.h" +#include "util/rsbase64.h" #include "pgp/rscertificate.h" #include @@ -1095,51 +1097,59 @@ bool p3Peers::setProxyServer(const uint32_t type, const std::string &addr_str, c std::string p3Peers::getPGPKey(const RsPgpId& pgp_id,bool include_signatures) { - unsigned char *mem_block = NULL; + rs_owner_ptr mem_block = nullptr; size_t mem_block_size = 0; if( !AuthGPG::getAuthGPG()->exportPublicKey( RsPgpId(pgp_id), mem_block, mem_block_size, false, include_signatures ) ) { - std::cerr << "Cannot output certificate for id \"" << pgp_id - << "\". Sorry." << std::endl; - return "" ; + RsErr() << __PRETTY_FUNCTION__ + << " Failure retriving certificate for id " << pgp_id + << std::endl; + return ""; } - RsPeerDetails Detail; - if(!getGPGDetails(pgp_id,Detail)) return ""; + RsPeerDetails details; + if(!getGPGDetails(pgp_id, details)) return ""; - RsCertificate cert( Detail,mem_block,mem_block_size ); - delete[] mem_block ; + auto certPtr = + RsCertificate::fromMemoryBlock(details, mem_block, mem_block_size); - return cert.armouredPGPKey(); + free(mem_block); + + if(certPtr) return certPtr->armouredPGPKey(); + + return ""; } -bool p3Peers::GetPGPBase64StringAndCheckSum( const RsPgpId& gpg_id, - std::string& gpg_base64_string, - std::string& gpg_base64_checksum) +bool p3Peers::GetPGPBase64StringAndCheckSum( + const RsPgpId& gpg_id, + std::string& gpg_base64_string, std::string& gpg_base64_checksum ) { gpg_base64_string = "" ; gpg_base64_checksum = "" ; - unsigned char *mem_block ; - size_t mem_block_size ; + rs_owner_ptr mem_block = nullptr; + size_t mem_block_size = 0; + if(!AuthGPG::getAuthGPG()->exportPublicKey( + gpg_id,mem_block,mem_block_size,false,false )) + return false; - if(!AuthGPG::getAuthGPG()->exportPublicKey(gpg_id,mem_block,mem_block_size,false,false)) - return false ; + RsBase64::encode(mem_block, mem_block_size, gpg_base64_string, true, false); - Radix64::encode(mem_block,mem_block_size,gpg_base64_string) ; + uint32_t crc = PGPKeyManagement::compute24bitsCRC(mem_block,mem_block_size); - uint32_t crc = PGPKeyManagement::compute24bitsCRC((unsigned char *)mem_block,mem_block_size) ; + free(mem_block); - unsigned char tmp[3] = { uint8_t((crc >> 16) & 0xff), uint8_t((crc >> 8) & 0xff), uint8_t(crc & 0xff) } ; - Radix64::encode(tmp,3,gpg_base64_checksum) ; + unsigned char tmp[3] = { + uint8_t((crc >> 16) & 0xff), + uint8_t((crc >> 8) & 0xff), + uint8_t(crc & 0xff) } ; + RsBase64::encode(tmp, 3, gpg_base64_checksum, true, false); - delete[] mem_block ; - - return true ; + return true; } enum class RsShortInviteFieldType : uint8_t @@ -1543,7 +1553,7 @@ std::string p3Peers::GetRetroshareInvite( } RsCertificate cert(detail, mem_block, mem_block_size); - delete[] mem_block; + free(mem_block); return cert.toStdString(); } diff --git a/libretroshare/src/rsserver/p3peers.h b/libretroshare/src/rsserver/p3peers.h index 86e878192..e4ad390ed 100644 --- a/libretroshare/src/rsserver/p3peers.h +++ b/libretroshare/src/rsserver/p3peers.h @@ -141,7 +141,8 @@ public: virtual std::string GetRetroshareInvite( const RsPeerId& ssl_id = RsPeerId(), bool include_signatures = false, bool includeExtraLocators = true ); - virtual std::string getPGPKey(const RsPgpId& pgp_id,bool include_signatures); + RS_DEPRECATED /// @see RsPeers + std::string getPGPKey(const RsPgpId& pgp_id,bool include_signatures) override; virtual bool GetPGPBase64StringAndCheckSum(const RsPgpId& gpg_id,std::string& gpg_base64_string,std::string& gpg_base64_checksum); diff --git a/libretroshare/src/rsserver/rsinit.cc b/libretroshare/src/rsserver/rsinit.cc index 2de431589..fa21b82e7 100644 --- a/libretroshare/src/rsserver/rsinit.cc +++ b/libretroshare/src/rsserver/rsinit.cc @@ -114,6 +114,8 @@ RsLoginHelper* rsLoginHelper = nullptr; RsAccounts* rsAccounts = nullptr; +const RsInitErrorCategory RsInitErrorCategory::instance; + RsConfigOptions::RsConfigOptions() : autoLogin(false), @@ -737,12 +739,13 @@ RsGRouter *rsGRouter = NULL ; #include "pgp/pgpauxutils.h" #include "services/p3idservice.h" #include "services/p3gxscircles.h" -#include "services/p3wiki.h" #include "services/p3posted.h" -#include "services/p3photoservice.h" #include "services/p3gxsforums.h" #include "services/p3gxschannels.h" + +#include "services/p3wiki.h" #include "services/p3wire.h" +#include "services/p3photoservice.h" #endif // RS_ENABLE_GXS @@ -920,8 +923,10 @@ int RsServer::StartupRetroShare() mNetMgr->setManagers(mPeerMgr, mLinkMgr); rsAutoProxyMonitor *autoProxy = rsAutoProxyMonitor::instance(); +#ifdef RS_USE_I2P_BOB mI2pBob = new p3I2pBob(mPeerMgr); autoProxy->addProxy(autoProxyType::I2PBOB, mI2pBob); +#endif //load all the SSL certs as friends // std::list sslIds; @@ -1361,36 +1366,39 @@ int RsServer::StartupRetroShare() mGxsChannels->setNetworkExchangeService(gxschannels_ns) ; -#if 0 // PHOTO IS DISABLED FOR THE MOMENT +#ifdef RS_USE_PHOTO /**** Photo service ****/ RsGeneralDataService* photo_ds = new RsDataService(currGxsDir + "/", "photoV2_db", RS_SERVICE_GXS_TYPE_PHOTO, NULL, rsInitConfig->gxs_passwd); // init gxs services - mPhoto = new p3PhotoService(photo_ds, NULL, mGxsIdService); + p3PhotoService *mPhoto = new p3PhotoService(photo_ds, NULL, mGxsIdService); // create GXS photo service RsGxsNetService* photo_ns = new RsGxsNetService( RS_SERVICE_GXS_TYPE_PHOTO, photo_ds, nxsMgr, mPhoto, mPhoto->getServiceInfo(), - mGxsIdService, mGxsCircles,mGxsIdService, + mReputations, mGxsCircles,mGxsIdService, pgpAuxUtils); + + mPhoto->setNetworkExchangeService(photo_ns); #endif -#if 0 // WIRE IS DISABLED FOR THE MOMENT +#ifdef RS_USE_WIRE /**** Wire GXS service ****/ RsGeneralDataService* wire_ds = new RsDataService(currGxsDir + "/", "wire_db", - RS_SERVICE_GXS_TYPE_WIRE, - NULL, rsInitConfig->gxs_passwd); + RS_SERVICE_GXS_TYPE_WIRE, NULL, rsInitConfig->gxs_passwd); - mWire = new p3Wire(wire_ds, NULL, mGxsIdService); + p3Wire *mWire = new p3Wire(wire_ds, NULL, mGxsIdService); // create GXS photo service RsGxsNetService* wire_ns = new RsGxsNetService( RS_SERVICE_GXS_TYPE_WIRE, wire_ds, nxsMgr, mWire, mWire->getServiceInfo(), - mGxsIdService, mGxsCircles,mGxsIdService, + mReputations, mGxsCircles,mGxsIdService, pgpAuxUtils); + + mWire->setNetworkExchangeService(wire_ns); #endif // now add to p3service pqih->addService(gxsid_ns, true); @@ -1401,7 +1409,12 @@ int RsServer::StartupRetroShare() #endif pqih->addService(gxsforums_ns, true); pqih->addService(gxschannels_ns, true); - //pqih->addService(photo_ns, true); +#ifdef RS_USE_PHOTO + pqih->addService(photo_ns, true); +#endif +#ifdef RS_USE_WIRE + pqih->addService(wire_ns, true); +#endif # ifdef RS_GXS_TRANS RsGeneralDataService* gxstrans_ds = new RsDataService( @@ -1627,13 +1640,20 @@ int RsServer::StartupRetroShare() mConfigMgr->addConfiguration("gxschannels_srv.cfg", mGxsChannels); mConfigMgr->addConfiguration("gxscircles.cfg" , gxscircles_ns); mConfigMgr->addConfiguration("posted.cfg" , posted_ns); + mConfigMgr->addConfiguration("gxsposted_srv.cfg", mPosted); #ifdef RS_USE_WIKI mConfigMgr->addConfiguration("wiki.cfg", wiki_ns); #endif - //mConfigMgr->addConfiguration("photo.cfg", photo_ns); - //mConfigMgr->addConfiguration("wire.cfg", wire_ns); +#ifdef RS_USE_PHOTO + mConfigMgr->addConfiguration("photo.cfg", photo_ns); +#endif +#ifdef RS_USE_WIRE + mConfigMgr->addConfiguration("wire.cfg", wire_ns); +#endif #endif //RS_ENABLE_GXS +#ifdef RS_USE_I2P_BOB mConfigMgr->addConfiguration("I2PBOB.cfg", mI2pBob); +#endif mPluginsManager->addConfigurations(mConfigMgr) ; @@ -1708,7 +1728,7 @@ int RsServer::StartupRetroShare() // now enable bob bobSettings bs; autoProxy->taskSync(autoProxyType::I2PBOB, autoProxyTask::getSettings, &bs); - bs.enableBob = true; + bs.enable = true; autoProxy->taskSync(autoProxyType::I2PBOB, autoProxyTask::setSettings, &bs); } else { std::cerr << "RsServer::StartupRetroShare failed to receive keys" << std::endl; @@ -1779,7 +1799,9 @@ int RsServer::StartupRetroShare() /**************************************************************************/ // auto proxy threads +#ifdef RS_USE_I2P_BOB startServiceThread(mI2pBob, "I2P-BOB"); +#endif #ifdef RS_ENABLE_GXS // Must Set the GXS pointers before starting threads. @@ -1793,8 +1815,12 @@ int RsServer::StartupRetroShare() rsGxsChannels = mGxsChannels; rsGxsTrans = mGxsTrans; - //rsPhoto = mPhoto; - //rsWire = mWire; +#if RS_USE_PHOTO + rsPhoto = mPhoto; +#endif +#if RS_USE_WIRE + rsWire = mWire; +#endif /*** start up GXS core runner ***/ @@ -1808,8 +1834,12 @@ int RsServer::StartupRetroShare() startServiceThread(mGxsForums, "gxs forums"); startServiceThread(mGxsChannels, "gxs channels"); - //createThread(*mPhoto); - //createThread(*mWire); +#if RS_USE_PHOTO + startServiceThread(mPhoto, "gxs photo"); +#endif +#if RS_USE_WIRE + startServiceThread(mWire, "gxs wire"); +#endif // cores ready start up GXS net servers startServiceThread(gxsid_ns, "gxs id ns"); @@ -1821,8 +1851,12 @@ int RsServer::StartupRetroShare() startServiceThread(gxsforums_ns, "gxs forums ns"); startServiceThread(gxschannels_ns, "gxs channels ns"); - //createThread(*photo_ns); - //createThread(*wire_ns); +#if RS_USE_PHOTO + startServiceThread(photo_ns, "gxs photo ns"); +#endif +#if RS_USE_WIRE + startServiceThread(wire_ns, "gxs wire ns"); +#endif # ifdef RS_GXS_TRANS startServiceThread(mGxsTrans, "gxs trans"); @@ -1924,6 +1958,47 @@ void RsLoginHelper::getLocations(std::vector& store) } } +std::error_condition RsLoginHelper::createLocationV2( + RsPeerId& locationId, RsPgpId& pgpId, + const std::string& locationName, const std::string& pgpName, + const std::string& password ) +{ + if(isLoggedIn()) return RsInitErrorNum::ALREADY_LOGGED_IN; + if(locationName.empty()) return RsInitErrorNum::INVALID_LOCATION_NAME; + if(pgpId.isNull() && pgpName.empty()) + return RsInitErrorNum::PGP_NAME_OR_ID_NEEDED; + + std::string errorMessage; + if(pgpId.isNull() && !RsAccounts::GeneratePGPCertificate( + pgpName, "", password, pgpId, 4096, errorMessage ) ) + { + RS_ERR("Failure creating PGP key: ", errorMessage); + return RsInitErrorNum::PGP_KEY_CREATION_FAILED; + } + + std::string sslPassword = + RsRandom::random_alphaNumericString(RsInit::getSslPwdLen()); + + rsNotify->cachePgpPassphrase(password); + rsNotify->setDisableAskPassword(true); + + bool ret = RsAccounts::createNewAccount( + pgpId, "", locationName, "", false, false, sslPassword, + locationId, errorMessage ); + if(!ret) + { + RS_ERR("Failure creating SSL key: ", errorMessage); + return RsInitErrorNum::SSL_KEY_CREATION_FAILED; + } + + RsInit::LoadPassword(sslPassword); + ret = (RsInit::OK == attemptLogin(locationId, password)); + rsNotify->setDisableAskPassword(false); + + return (ret ? std::error_condition() : RsInitErrorNum::LOGIN_FAILED); +} + +#if !RS_VERSION_AT_LEAST(0,6,6) bool RsLoginHelper::createLocation( RsLoginHelper::Location& l, const std::string& password, std::string& errorMessage, bool makeHidden, bool makeAutoTor ) @@ -1965,6 +2040,7 @@ bool RsLoginHelper::createLocation( rsNotify->setDisableAskPassword(false); return ret; } +#endif // !RS_VERSION_AT_LEAST(0,6,6) bool RsLoginHelper::isLoggedIn() { diff --git a/libretroshare/src/rsserver/rsloginhandler.cc b/libretroshare/src/rsserver/rsloginhandler.cc index 8a0469e1d..dbe023235 100644 --- a/libretroshare/src/rsserver/rsloginhandler.cc +++ b/libretroshare/src/rsserver/rsloginhandler.cc @@ -26,6 +26,7 @@ #include "rsloginhandler.h" #include "util/rsdir.h" #include "retroshare/rsinit.h" +#include "util/rsdebug.h" //#define DEBUG_RSLOGINHANDLER 1 @@ -497,8 +498,15 @@ bool RsLoginHandler::enableAutoLogin(const RsPeerId& ssl_id,const std::string& s NULL); if (error) { + RsErr() << __PRETTY_FUNCTION__ + << " Could not store passwd using libsecret with" + << " error.code=" << error->code + << " error.domain=" << error->domain + << " error.message=\"" << error->message << "\"" << std::endl; + if (error->code == 2) + RsErr() << "Do have a key wallet installed?" << std::endl + << "Like gnome-keyring or other using \"Secret Service\" by DBus." << std::endl; g_error_free (error); - std::cerr << "Could not store passwd using libsecret" << std::endl; return false; } std::cout << "Stored passwd " << "************************" << " using libsecret" << std::endl; diff --git a/libretroshare/src/serialiser/rsserial.cc b/libretroshare/src/serialiser/rsserial.cc index dda560867..52123abf1 100644 --- a/libretroshare/src/serialiser/rsserial.cc +++ b/libretroshare/src/serialiser/rsserial.cc @@ -125,9 +125,7 @@ RsItem::RsItem(uint8_t ver, uint8_t cls, uint8_t t, uint8_t subtype) type = (ver << 24) + (cls << 16) + (t << 8) + subtype; } -RsItem::~RsItem() -{ -} +RsItem::~RsItem() = default; void RsItem::print_string(std::string &out, uint16_t indent) { @@ -243,10 +241,7 @@ uint32_t RsSerialType::PacketId() const -RsSerialiser::RsSerialiser() -{ - return; -} +RsSerialiser::RsSerialiser() = default; RsSerialiser::~RsSerialiser() @@ -559,17 +554,7 @@ std::ostream &RsRawItem::print(std::ostream &out, uint16_t indent) return out; } - -uint32_t getRsPktMaxSize() -{ - //return 65535; /* 2^16 (old artifical low size) */ - //return 1048575; /* 2^20 -1 (Too Big! - must remove fixed static buffers first) */ - /* Remember that every pqistreamer allocates an input buffer of this size! - * So don't make it too big! - */ - return 262143; /* 2^18 -1 */ -} - +uint32_t getRsPktMaxSize() { return RsSerialiser::MAX_SERIAL_SIZE; } uint32_t getRsPktBaseSize() { diff --git a/libretroshare/src/serialiser/rsserial.h b/libretroshare/src/serialiser/rsserial.h index 3ce56a424..168db8d75 100644 --- a/libretroshare/src/serialiser/rsserial.h +++ b/libretroshare/src/serialiser/rsserial.h @@ -19,16 +19,16 @@ * along with this program. If not, see . * * * *******************************************************************************/ -#ifndef RS_BASE_SERIALISER_H -#define RS_BASE_SERIALISER_H +#pragma once -#include -#include +#include #include #include #include -#include -#include +#include +#include + +#include "util/rsdeprecate.h" /******************************************************************* * This is the Top-Level serialiser/deserialise, @@ -66,7 +66,11 @@ class RsSerialType ; class RsSerialiser { - public: +public: + /** Remember that every pqistreamer allocates an input buffer of this size! + * So don't make it too big! */ + static constexpr uint32_t MAX_SERIAL_SIZE = 262143; /* 2^18 -1 */ + RsSerialiser(); ~RsSerialiser(); bool addSerialType(RsSerialType *type); @@ -76,7 +80,7 @@ class RsSerialiser RsItem * deserialise(void *data, uint32_t *size); - private: +private: std::map serialisers; }; @@ -95,6 +99,8 @@ uint16_t getRsItemService(uint32_t type); /* size constants */ uint32_t getRsPktBaseSize(); + +RS_DEPRECATED_FOR(RsSerialiser::MAX_SERIAL_SIZE) uint32_t getRsPktMaxSize(); @@ -106,5 +112,3 @@ std::ostream &printRsItemEnd(std::ostream &o, std::string n, uint16_t i); /* defined in rstlvtypes.cc - redeclared here for ease */ std::ostream &printIndent(std::ostream &out, uint16_t indent); /* Wrapper class for data that is serialised somewhere else */ - -#endif /* RS_BASE_SERIALISER_H */ diff --git a/libretroshare/src/serialiser/rsserializer.cc b/libretroshare/src/serialiser/rsserializer.cc index 71d7abea3..e59b7d9fd 100644 --- a/libretroshare/src/serialiser/rsserializer.cc +++ b/libretroshare/src/serialiser/rsserializer.cc @@ -4,6 +4,8 @@ * libretroshare: retroshare core library * * * * Copyright (C) 2016 Cyril Soler * + * Copyright (C) 2020 Gioacchino Mazzurco * + * Copyright (C) 2020 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -18,23 +20,17 @@ * You should have received a copy of the GNU Lesser General Public License * * along with this program. If not, see . * * * - *******************************************************************************/ + ******************************************************************************/ + #include #include "rsitems/rsitem.h" - #include "util/rsprint.h" #include "serialiser/rsserializer.h" #include "serialiser/rstypeserializer.h" #include "util/stacktrace.h" #include "util/rsdebug.h" -const SerializationFlags RsGenericSerializer::SERIALIZATION_FLAG_NONE ( 0x0000 ); -const SerializationFlags RsGenericSerializer::SERIALIZATION_FLAG_CONFIG ( 0x0001 ); -const SerializationFlags RsGenericSerializer::SERIALIZATION_FLAG_SIGNATURE ( 0x0002 ); -const SerializationFlags RsGenericSerializer::SERIALIZATION_FLAG_SKIP_HEADER ( 0x0004 ); -const SerializationFlags RsGenericSerializer::SERIALIZATION_FLAG_YIELDING ( 0x0008 ); - RsItem *RsServiceSerializer::deserialise(void *data, uint32_t *size) { if(!data || !size || !*size) @@ -47,11 +43,13 @@ RsItem *RsServiceSerializer::deserialise(void *data, uint32_t *size) return nullptr; } - if(mFlags & SERIALIZATION_FLAG_SKIP_HEADER) - { - std::cerr << "(EE) Cannot deserialise item with flags SERIALIZATION_FLAG_SKIP_HEADER. Check your code!" << std::endl; - return NULL ; - } + if(!!(mFlags & RsSerializationFlags::SKIP_HEADER)) + { + RsErr() << __PRETTY_FUNCTION__ << " Cannot deserialise item with flag " + << "SKIP_HEADER. Check your code!" << std::endl; + print_stacktrace(); + return nullptr; + } uint32_t rstype = getRsItemId(const_cast((const void*)data)) ; @@ -64,7 +62,9 @@ RsItem *RsServiceSerializer::deserialise(void *data, uint32_t *size) return NULL ; } - SerializeContext ctx(const_cast(static_cast(data)),*size,mFormat,mFlags); + SerializeContext ctx( + const_cast(static_cast(data)), *size, + mFlags ); ctx.mOffset = 8 ; item->serial_process(RsGenericSerializer::DESERIALIZE, ctx) ; @@ -85,11 +85,13 @@ RsItem *RsServiceSerializer::deserialise(void *data, uint32_t *size) } RsItem *RsConfigSerializer::deserialise(void *data, uint32_t *size) { - if(mFlags & SERIALIZATION_FLAG_SKIP_HEADER) - { - std::cerr << "(EE) Cannot deserialise item with flags SERIALIZATION_FLAG_SKIP_HEADER. Check your code!" << std::endl; - return NULL ; - } + if(!!(mFlags & RsSerializationFlags::SKIP_HEADER)) + { + RsErr() << __PRETTY_FUNCTION__ << " Cannot deserialise item with flag " + << "SKIP_HEADER. Check your code!" << std::endl; + print_stacktrace(); + return nullptr; + } uint32_t rstype = getRsItemId(const_cast((const void*)data)) ; @@ -102,7 +104,9 @@ RsItem *RsConfigSerializer::deserialise(void *data, uint32_t *size) return NULL ; } - SerializeContext ctx(const_cast(static_cast(data)),*size,mFormat,mFlags); + SerializeContext ctx( + const_cast(static_cast(data)), *size, + mFlags ); ctx.mOffset = 8 ; item->serial_process(DESERIALIZE, ctx) ; @@ -121,50 +125,44 @@ RsItem *RsConfigSerializer::deserialise(void *data, uint32_t *size) delete item ; return NULL ; } -bool RsGenericSerializer::serialise(RsItem *item,void *data,uint32_t *size) + +bool RsGenericSerializer::serialise(RsItem* item, void* data, uint32_t* size) { - SerializeContext ctx(static_cast(data),0,mFormat,mFlags); + uint32_t tlvsize = this->size(item); - uint32_t tlvsize = this->size(item) ; + constexpr auto fName = __PRETTY_FUNCTION__; + const auto failure = [=](std::error_condition ec) + { + RsErr() << fName << " " << ec << std::endl; + print_stacktrace(); + return false; + }; - if(tlvsize > *size) - throw std::runtime_error("Cannot serialise: not enough room.") ; + if(tlvsize > *size) return failure(std::errc::no_buffer_space); + SerializeContext ctx(static_cast(data), tlvsize, mFlags); - if(mFlags & SERIALIZATION_FLAG_SKIP_HEADER) - ctx.mOffset = 0; - else + if(!(mFlags & RsSerializationFlags::SKIP_HEADER)) { if(!setRsItemHeader(data, tlvsize, item->PacketId(), tlvsize)) - { - std::cerr << "RsSerializer::serialise_item(): ERROR. Not enough size!" << std::endl; - return false ; - } + return failure(std::errc::no_buffer_space); ctx.mOffset = 8; } - ctx.mSize = tlvsize; + item->serial_process(RsGenericSerializer::SERIALIZE,ctx); - item->serial_process(RsGenericSerializer::SERIALIZE,ctx) ; + if(ctx.mSize != ctx.mOffset) return failure(std::errc::message_size); - if(ctx.mSize != ctx.mOffset) - { - std::cerr << "RsSerializer::serialise(): ERROR. offset does not match expected size!" << std::endl; - return false ; - } - *size = ctx.mOffset ; - - return true ; + *size = ctx.mOffset; + return true; } uint32_t RsGenericSerializer::size(RsItem *item) { - SerializeContext ctx(NULL,0,mFormat,mFlags); + SerializeContext ctx(nullptr, 0, mFlags); - if(mFlags & SERIALIZATION_FLAG_SKIP_HEADER) - ctx.mOffset = 0; - else - ctx.mOffset = 8 ; // header size + if(!!(mFlags & RsSerializationFlags::SKIP_HEADER)) ctx.mOffset = 0; + else ctx.mOffset = 8; // header size item->serial_process(SIZE_ESTIMATE, ctx) ; return ctx.mOffset ; @@ -172,7 +170,7 @@ uint32_t RsGenericSerializer::size(RsItem *item) void RsGenericSerializer::print(RsItem *item) { - SerializeContext ctx(NULL,0,mFormat,mFlags); + SerializeContext ctx(nullptr, 0, mFlags); std::cerr << "***** RsItem class: \"" << typeid(*item).name() << "\" *****" << std::endl; item->serial_process(PRINT, ctx) ; @@ -255,7 +253,7 @@ RsItem *RsRawSerialiser::deserialise(void *data, uint32_t *pktsize) RsGenericSerializer::SerializeContext::SerializeContext( - uint8_t* data, uint32_t size, SerializationFlags flags, + uint8_t* data, uint32_t size, RsSerializationFlags flags, RsJson::AllocatorType* allocator ) : mData(data), mSize(size), mOffset(0), mOk(true), mFlags(flags), mJson(rapidjson::kObjectType, allocator) @@ -264,20 +262,23 @@ RsGenericSerializer::SerializeContext::SerializeContext( { if(size == 0) { - std::cerr << __PRETTY_FUNCTION__ << " data passed without " - << "size! This make no sense report to developers!" - << std::endl; + RsFatal() << __PRETTY_FUNCTION__ << " data passed without " + << "size! This make no sense report to developers!" + << std::endl; print_stacktrace(); + exit(-EINVAL); } - if(flags & SERIALIZATION_FLAG_YIELDING) + if(!!(flags & RsSerializationFlags::YIELDING)) { - std::cerr << __PRETTY_FUNCTION__ << " Attempt to create a " + RsFatal() << __PRETTY_FUNCTION__ + << " Attempt to create a " << "binary serialization context with " << "SERIALIZATION_FLAG_YIELDING! " << "This make no sense report to developers!" << std::endl; print_stacktrace(); + exit(-EINVAL); } } } diff --git a/libretroshare/src/serialiser/rsserializer.h b/libretroshare/src/serialiser/rsserializer.h index e9b60a0aa..1b67e2675 100644 --- a/libretroshare/src/serialiser/rsserializer.h +++ b/libretroshare/src/serialiser/rsserializer.h @@ -192,6 +192,39 @@ class RsRawSerialiser: public RsSerialType virtual RsItem * deserialise(void *data, uint32_t *size); }; +/** These are convenience flags to be used by the items when processing the + * data. The names of the flags are not very important. What matters is that + * the serial_process() method of each item correctly deals with the data + * when it sees the flags, if the serialiser sets them. + * By default the flags are not set and shouldn't be handled. + * When deriving a new serializer, the user can set his own flags, using + * compatible values + */ +enum class RsSerializationFlags +{ + NONE = 0, + CONFIG = 1, + SIGNATURE = 2, + SKIP_HEADER = 4, + + /** Used for JSON deserialization in JSON API, it causes the deserialization + * to continue even if some field is missing (or incorrect), this way the + * API is more user friendly as some methods need just part of the structs + * they take as parameters. */ + YIELDING = 8, + + /** When set integers typer are serialized/deserialized in Variable Length + * Quantity mode + * @see https://en.wikipedia.org/wiki/Variable-length_quantity + * This type of encoding is efficent when absoulte value is usually much + * smaller then the maximum representable with the original type. + * This encoding is also capable of representing big values at expences of a + * one more byte used. + */ + INTEGER_VLQ = 16 +}; +RS_REGISTER_ENUM_FLAGS_TYPE(RsSerializationFlags); + /// Top class for all services and config serializers. struct RsGenericSerializer : RsSerialType { @@ -200,63 +233,28 @@ struct RsGenericSerializer : RsSerialType SIZE_ESTIMATE = 0x01, SERIALIZE = 0x02, DESERIALIZE = 0x03, - PRINT = 0x04, + PRINT = 0x04, /// @deprecated use rsdebug.h << operator instead TO_JSON, FROM_JSON } SerializeJob; - - /** @deprecated use SerializeJob instead */ - RS_DEPRECATED typedef enum - { - FORMAT_BINARY = 0x01, - FORMAT_JSON = 0x02 - } SerializationFormat; - struct SerializeContext { /** Allow shared allocator usage to avoid costly JSON deepcopy for * nested RsSerializable */ SerializeContext( uint8_t* data = nullptr, uint32_t size = 0, - SerializationFlags flags = SERIALIZATION_FLAG_NONE, + RsSerializationFlags flags = RsSerializationFlags::NONE, RsJson::AllocatorType* allocator = nullptr); - RS_DEPRECATED SerializeContext( - uint8_t *data, uint32_t size, SerializationFormat format, - SerializationFlags flags, - RsJson::AllocatorType* allocator = nullptr) : - mData(data), mSize(size), mOffset(0), mOk(true), mFormat(format), - mFlags(flags), mJson(rapidjson::kObjectType, allocator) {} - unsigned char *mData; uint32_t mSize; uint32_t mOffset; bool mOk; - RS_DEPRECATED SerializationFormat mFormat; - SerializationFlags mFlags; + RsSerializationFlags mFlags; RsJson mJson; }; - /** These are convenience flags to be used by the items when processing the - * data. The names of the flags are not very important. What matters is that - * the serial_process() method of each item correctly deals with the data - * when it sees the flags, if the serialiser sets them. - * By default the flags are not set and shouldn't be handled. - * When deriving a new serializer, the user can set his own flags, using - * compatible values - */ - static const SerializationFlags SERIALIZATION_FLAG_NONE; // 0x0000 - static const SerializationFlags SERIALIZATION_FLAG_CONFIG; // 0x0001 - static const SerializationFlags SERIALIZATION_FLAG_SIGNATURE; // 0x0002 - static const SerializationFlags SERIALIZATION_FLAG_SKIP_HEADER; // 0x0004 - - /** Used for JSON deserialization in JSON API, it causes the deserialization - * to continue even if some field is missing (or incorrect), this way the - * API is more user friendly as some methods need just part of the structs - * they take as parameters. */ - static const SerializationFlags SERIALIZATION_FLAG_YIELDING; // 0x0008 - /** * The following functions overload RsSerialType. * They *should not* need to be further overloaded. @@ -269,18 +267,15 @@ struct RsGenericSerializer : RsSerialType protected: RsGenericSerializer( uint8_t serial_class, uint8_t serial_type, - SerializationFormat format, SerializationFlags flags ) : + RsSerializationFlags flags ): RsSerialType( RS_PKT_VERSION1, serial_class, serial_type), - mFormat(format), mFlags(flags) {} - - RsGenericSerializer( - uint16_t service, SerializationFormat format, - SerializationFlags flags ) : - RsSerialType( RS_PKT_VERSION_SERVICE, service ), mFormat(format), mFlags(flags) {} - SerializationFormat mFormat; - SerializationFlags mFlags; + RsGenericSerializer( + uint16_t service, RsSerializationFlags flags ): + RsSerialType( RS_PKT_VERSION_SERVICE, service ), mFlags(flags) {} + + RsSerializationFlags mFlags; }; @@ -290,9 +285,9 @@ protected: struct RsServiceSerializer : RsGenericSerializer { RsServiceSerializer( - uint16_t service_id, SerializationFormat format = FORMAT_BINARY, - SerializationFlags flags = SERIALIZATION_FLAG_NONE ) : - RsGenericSerializer(service_id, format, flags) {} + uint16_t service_id, + RsSerializationFlags flags = RsSerializationFlags::NONE ) : + RsGenericSerializer(service_id, flags) {} /*! should be overloaded to create the correct type of item depending on the * data */ @@ -309,11 +304,10 @@ struct RsServiceSerializer : RsGenericSerializer */ struct RsConfigSerializer : RsGenericSerializer { - RsConfigSerializer(uint8_t config_class, - uint8_t config_type, - SerializationFormat format = FORMAT_BINARY, - SerializationFlags flags = SERIALIZATION_FLAG_NONE) : - RsGenericSerializer(config_class,config_type,format,flags) {} + RsConfigSerializer( + uint8_t config_class, uint8_t config_type, + RsSerializationFlags flags = RsSerializationFlags::NONE ) : + RsGenericSerializer(config_class, config_type, flags) {} /*! should be overloaded to create the correct type of item depending on the * data */ diff --git a/libretroshare/src/serialiser/rstlvidset.h b/libretroshare/src/serialiser/rstlvidset.h index 732eee64c..2e8a3f882 100644 --- a/libretroshare/src/serialiser/rstlvidset.h +++ b/libretroshare/src/serialiser/rstlvidset.h @@ -92,11 +92,15 @@ template class RS_DEPRECATED_FOR(std::set<>) t ids.insert(id) ; } if(*offset != tlvend) + { std::cerr << "(EE) deserialisaiton error in " << __PRETTY_FUNCTION__ << std::endl; - else if(!ok) + ok = false; + } + + if(!ok) std::cerr << "(WW) something wrong in ID_CLASS.deserialise in " << __PRETTY_FUNCTION__ << std::endl; - return *offset == tlvend ; + return ok; } virtual std::ostream &print(std::ostream &out, uint16_t /* indent */) const { diff --git a/libretroshare/src/serialiser/rstypeserializer.cc b/libretroshare/src/serialiser/rstypeserializer.cc index 1b086576a..0c7bfc435 100644 --- a/libretroshare/src/serialiser/rstypeserializer.cc +++ b/libretroshare/src/serialiser/rstypeserializer.cc @@ -4,7 +4,8 @@ * libretroshare: retroshare core library * * * * Copyright (C) 2017 Cyril Soler * - * Copyright (C) 2018-2019 Gioacchino Mazzurco * + * Copyright (C) 2018-2020 Gioacchino Mazzurco * + * Copyright (C) 2020 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -25,7 +26,7 @@ #include "serialiser/rsbaseserial.h" #include "serialiser/rstlvkeys.h" #include "serialiser/rsserializable.h" -#include "util/radix64.h" +#include "util/rsbase64.h" #include "util/rsprint.h" #include "util/rstime.h" @@ -34,8 +35,6 @@ #include // for typeid #include -static constexpr uint32_t MAX_SERIALIZED_CHUNK_SIZE = 10*1024*1024 ; // 10 MB. - #ifdef RSSERIAL_DEBUG # define SAFE_GET_JSON_V() \ const char* mName = memberName.c_str(); \ @@ -56,32 +55,10 @@ static constexpr uint32_t MAX_SERIALIZED_CHUNK_SIZE = 10*1024*1024 ; // 10 MB. rapidjson::Value& v = jDoc[mName] #endif // ifdef RSSERIAL_DEBUG - //============================================================================// // std::string // //============================================================================// -template<> uint32_t RsTypeSerializer::serial_size(const std::string& str) -{ - return getRawStringSize(str); -} -template<> bool RsTypeSerializer::serialize( uint8_t data[], uint32_t size, - uint32_t& offset, - const std::string& str ) -{ - return setRawString(data, size, &offset, str); -} -template<> bool RsTypeSerializer::deserialize( const uint8_t data[], - uint32_t size, uint32_t &offset, - std::string& str ) -{ - return getRawString(data, size, &offset, str); -} -template<> void RsTypeSerializer::print_data( const std::string& n, - const std::string& str ) -{ - std::cerr << " [std::string] " << n << ": " << str << std::endl; -} template<> /*static*/ bool RsTypeSerializer::to_JSON( const std::string& membername, const std::string& member, RsJson& jDoc ) @@ -112,135 +89,11 @@ bool RsTypeSerializer::from_JSON( const std::string& memberName, return ret; } + //============================================================================// -// Integer types // +// Integral types // //============================================================================// -template<> bool RsTypeSerializer::serialize(uint8_t data[], uint32_t size, uint32_t &offset, const bool& member) -{ - return setRawUInt8(data,size,&offset,member); -} -template<> bool RsTypeSerializer::serialize(uint8_t /*data*/[], uint32_t /*size*/, uint32_t& /*offset*/, const int32_t& /*member*/) -{ - std::cerr << __PRETTY_FUNCTION__ << " Not implemented!" << std::endl; - print_stacktrace(); - return false; -} -template<> bool RsTypeSerializer::serialize(uint8_t data[], uint32_t size, uint32_t &offset, const uint8_t& member) -{ - return setRawUInt8(data,size,&offset,member); -} -template<> bool RsTypeSerializer::serialize(uint8_t data[], uint32_t size, uint32_t &offset, const uint16_t& member) -{ - return setRawUInt16(data,size,&offset,member); -} -template<> bool RsTypeSerializer::serialize(uint8_t data[], uint32_t size, uint32_t &offset, const uint32_t& member) -{ - return setRawUInt32(data,size,&offset,member); -} -template<> bool RsTypeSerializer::serialize(uint8_t data[], uint32_t size, uint32_t &offset, const uint64_t& member) -{ - return setRawUInt64(data,size,&offset,member); -} -template<> bool RsTypeSerializer::serialize(uint8_t data[], uint32_t size, uint32_t &offset, const rstime_t& member) -{ - return setRawTimeT(data,size,&offset,member); -} - -template<> bool RsTypeSerializer::deserialize(const uint8_t data[], uint32_t size, uint32_t &offset, bool& member) -{ - uint8_t m; - bool ok = getRawUInt8(data,size,&offset,&m); - member = m; - return ok; -} -template<> bool RsTypeSerializer::deserialize(const uint8_t /*data*/[], uint32_t /*size*/, uint32_t& /*offset*/, int32_t& /*member*/) -{ - std::cerr << __PRETTY_FUNCTION__ << " Not implemented!" << std::endl; - print_stacktrace(); - return false; -} -template<> bool RsTypeSerializer::deserialize(const uint8_t data[], uint32_t size, uint32_t &offset, uint8_t& member) -{ - return getRawUInt8(data,size,&offset,&member); -} -template<> bool RsTypeSerializer::deserialize(const uint8_t data[], uint32_t size, uint32_t &offset, uint16_t& member) -{ - return getRawUInt16(data,size,&offset,&member); -} -template<> bool RsTypeSerializer::deserialize(const uint8_t data[], uint32_t size, uint32_t &offset, uint32_t& member) -{ - return getRawUInt32(data,size,&offset,&member); -} -template<> bool RsTypeSerializer::deserialize(const uint8_t data[], uint32_t size, uint32_t &offset, uint64_t& member) -{ - return getRawUInt64(data,size,&offset,&member); -} -template<> bool RsTypeSerializer::deserialize(const uint8_t data[], uint32_t size, uint32_t &offset, rstime_t& member) -{ - return getRawTimeT(data,size,&offset,member); -} - -template<> uint32_t RsTypeSerializer::serial_size(const bool& /* member*/) -{ - return 1; -} -template<> uint32_t RsTypeSerializer::serial_size(const int32_t& /* member*/) -{ - std::cerr << __PRETTY_FUNCTION__ << " Not implemented!" << std::endl; - print_stacktrace(); - return 0; -} -template<> uint32_t RsTypeSerializer::serial_size(const uint8_t& /* member*/) -{ - return 1; -} -template<> uint32_t RsTypeSerializer::serial_size(const uint16_t& /* member*/) -{ - return 2; -} -template<> uint32_t RsTypeSerializer::serial_size(const uint32_t& /* member*/) -{ - return 4; -} -template<> uint32_t RsTypeSerializer::serial_size(const uint64_t& /* member*/) -{ - return 8; -} -template<> uint32_t RsTypeSerializer::serial_size(const rstime_t& /* member*/) -{ - return 8; -} - -template<> void RsTypeSerializer::print_data(const std::string& n, const bool & V) -{ - std::cerr << " [bool ] " << n << ": " << V << std::endl; -} -template<> void RsTypeSerializer::print_data(const std::string& n, const int32_t& V) -{ - std::cerr << " [int32_t ] " << n << ": " << std::to_string(V) << std::endl; -} -template<> void RsTypeSerializer::print_data(const std::string& n, const uint8_t & V) -{ - std::cerr << " [uint8_t ] " << n << ": " << std::to_string(V) << std::endl; -} -template<> void RsTypeSerializer::print_data(const std::string& n, const uint16_t& V) -{ - std::cerr << " [uint16_t ] " << n << ": " << std::to_string(V) << std::endl; -} -template<> void RsTypeSerializer::print_data(const std::string& n, const uint32_t& V) -{ - std::cerr << " [uint32_t ] " << n << ": " << std::to_string(V) << std::endl; -} -template<> void RsTypeSerializer::print_data(const std::string& n, const uint64_t& V) -{ - std::cerr << " [uint64_t ] " << n << ": " << std::to_string(V) << std::endl; -} -template<> void RsTypeSerializer::print_data(const std::string& n, const rstime_t& V) -{ - std::cerr << " [rstime_t ] " << n << ": " << V << " (" << time(NULL)-V << " secs ago)" << std::endl; -} - #define SIMPLE_TO_JSON_DEF(T) \ template<> bool RsTypeSerializer::to_JSON( const std::string& memberName, \ const T& member, RsJson& jDoc ) \ @@ -268,28 +121,45 @@ SIMPLE_TO_JSON_DEF(uint32_t) /** Be very careful in changing this constant as it would break 64 bit integers * members JSON string representation retrocompatibility */ -static constexpr char strReprSuffix[] = "_sixtyfour_str"; +static constexpr char strReprKey[] = "xstr64"; -/** While JSON doesn't have problems representing 64 bit integers JavaScript - * standard represents numbers in a double-like format thus it is not capable to - * handle safely integers outside the range [-(2^53 - 1), 2^53 - 1], so we add - * to JSON also the string representation for this types as a workaround for the - * sake of JavaScript clients @see https://stackoverflow.com/a/34989371 +/** Be very careful in changing this constant as it would break 64 bit integers + * members JSON string representation retrocompatibility */ +static constexpr char intReprKey[] = "xint64"; + +/** While JSON doesn't have problems representing 64 bits integers JavaScript, + * Dart and other languages represents numbers in a double-like format thus they + * are not capable to handle safely integers outside the range + * [-(2^53 - 1), 2^53 - 1]. + * To overcome this limitation we represent 64 bit integers as an object with + * two keys, one as integer and one as string representation. + * In our case we need to wrap those into an object instead of just adding a key + * with a suffix so support well also containers like std::map or std::vector. + * More discussion on the topic at @see https://stackoverflow.com/a/34989371 */ #define SIXTYFOUR_INTEGERS_TO_JSON_DEF(T) \ template<> bool RsTypeSerializer::to_JSON( const std::string& memberName, \ const T& member, RsJson& jDoc ) \ { \ - rapidjson::Document::AllocatorType& allocator = jDoc.GetAllocator(); \ + using namespace rapidjson; \ + Document::AllocatorType& allocator = jDoc.GetAllocator(); \ \ - rapidjson::Value key; \ + Document wrapper(rapidjson::kObjectType, &allocator); \ + \ + Value intKey; \ + intKey.SetString(intReprKey, allocator ); \ + Value intValue(member); \ + wrapper.AddMember(intKey, intValue, allocator); \ + \ + bool ok = to_JSON(strReprKey, std::to_string(member), wrapper); \ + \ + Value key; \ key.SetString( memberName.c_str(), \ static_cast(memberName.length()), \ allocator ); \ - rapidjson::Value value(member); \ - jDoc.AddMember(key, value, allocator); \ + jDoc.AddMember(key, wrapper, allocator); \ \ - return to_JSON(memberName + strReprSuffix, std::to_string(member), jDoc); \ + return ok; \ } SIXTYFOUR_INTEGERS_TO_JSON_DEF(int64_t); @@ -345,100 +215,70 @@ bool RsTypeSerializer::from_JSON( const std::string& memberName, return ret; } -/** While JSON doesn't have problems representing 64 bit integers JavaScript - * standard represents numbers in a double-like format thus it is not capable to - * handle safely integers outside the range [-(2^53 - 1), 2^53 - 1], so we look - * for the string representation in the JSON for this types as a workaround for - * the sake of JavaScript clients @see https://stackoverflow.com/a/34989371 - */ -template<> /*static*/ -bool RsTypeSerializer::from_JSON( - const std::string& memberName, int64_t& member, RsJson& jDoc ) -{ - const char* mName = memberName.c_str(); - if(jDoc.HasMember(mName)) - { - rapidjson::Value& v = jDoc[mName]; - if(v.IsInt64()) - { - member = v.GetInt64(); - return true; - } - } - - Dbg4() << __PRETTY_FUNCTION__ << " int64_t " << memberName << " not found " - << "in JSON then attempt to look for string representation" - << std::endl; - - const std::string str_key = memberName + strReprSuffix; - std::string str_value; - if(from_JSON(str_key, str_value, jDoc)) - { - try { member = std::stoll(str_value); } - catch (...) - { - RsErr() << __PRETTY_FUNCTION__ << " cannot convert " - << str_value << " to int64_t" << std::endl; - return false; - } - - return true; - } - - Dbg3() << __PRETTY_FUNCTION__ << " neither " << memberName << " nor its " - << "string representation " << str_key << " has been found " - << "in JSON" << std::endl; - - return false; -} - -/** While JSON doesn't have problems representing 64 bit integers JavaScript - * standard represents numbers in a double-like format thus it is not capable to - * handle safely integers outside the range [-(2^53 - 1), 2^53 - 1], so we look - * for the string representation in the JSON for this types as a workaround for - * the sake of JavaScript clients @see https://stackoverflow.com/a/34989371 - */ -template<> /*static*/ -bool RsTypeSerializer::from_JSON( - const std::string& memberName, uint64_t& member, RsJson& jDoc ) -{ - const char* mName = memberName.c_str(); - if(jDoc.HasMember(mName)) - { - rapidjson::Value& v = jDoc[mName]; - if(v.IsUint64()) - { - member = v.GetUint64(); - return true; - } - } - - Dbg4() << __PRETTY_FUNCTION__ << " uint64_t " << memberName << " not found " - << "in JSON then attempt to look for string representation" - << std::endl; - - const std::string str_key = memberName + strReprSuffix; - std::string str_value; - if(from_JSON(str_key, str_value, jDoc)) - { - try { member = std::stoull(str_value); } - catch (...) - { - RsErr() << __PRETTY_FUNCTION__ << " cannot convert " - << str_value << " to uint64_t" << std::endl; - return false; - } - - return true; - } - - Dbg3() << __PRETTY_FUNCTION__ << " neither " << memberName << " nor its " - << "string representation " << str_key << " has been found " - << "in JSON" << std::endl; - - return false; +/** inverse of @see SIXTYFOUR_INTEGERS_TO_JSON_DEF */ +#define SIXTYFOUR_INTEGERS_FROM_JSON_DEF(T, PRED, GET, CONV) \ +template<> bool RsTypeSerializer::from_JSON( \ + const std::string& memberName, T& member, RsJson& jDoc ) \ +{ \ + using namespace rapidjson; \ + \ + SAFE_GET_JSON_V(); \ + \ + /* For retro-compatibility take it directly if it is passed as integer */ \ + if(v.PRED()) \ + { \ + member = v.GET(); \ + return true; \ + } \ + \ + ret = ret && v.IsObject(); \ + \ + if(!ret) \ + { \ + RS_DBG3(memberName, " not found"); \ + return false; \ + } \ + \ + if(v.HasMember(intReprKey)) \ + { \ + Value& iVal = v[intReprKey]; \ + if(iVal.PRED()) \ + { \ + member = iVal.GET(); \ + return true; \ + } \ + } \ + \ + Dbg4() << __PRETTY_FUNCTION__ << " integer representation of " << memberName \ + << " not found in JSON then attempt to look for string representation" \ + << std::endl; \ + \ + \ + if(v.HasMember(strReprKey)) \ + { \ + Value& sVal = v[strReprKey]; \ + if(sVal.IsString()) \ + { \ + try { member = CONV(sVal.GetString()); } \ + catch (...) \ + { \ + RsErr() << __PRETTY_FUNCTION__ << " cannot convert " \ + << sVal.GetString() << " to integral type" << std::endl; \ + return false; \ + } \ + \ + return true; \ + } \ + } \ + \ + Dbg3() << __PRETTY_FUNCTION__ << " neither integral representation nor " \ + << "string representation found for: " << memberName << std::endl; \ + \ + return false; \ } +SIXTYFOUR_INTEGERS_FROM_JSON_DEF(uint64_t, IsUint64, GetUint64, std::stoull) +SIXTYFOUR_INTEGERS_FROM_JSON_DEF( int64_t, IsInt64, GetInt64, std::stoll) //============================================================================// // Floats // @@ -662,112 +502,180 @@ bool RsTypeSerializer::from_JSON( const std::string& /*memberName*/, // Binary blocks // //============================================================================// -template<> uint32_t RsTypeSerializer::serial_size(const RsTypeSerializer::TlvMemBlock_proxy& r) { return 4 + r.second ; } +#if __cplusplus < 201703L +/* Solve weird undefined reference error with C++ < 17 see: + * https://stackoverflow.com/questions/8016780/undefined-reference-to-static-constexpr-char + */ +/*static*/ decltype(RsTypeSerializer::RawMemoryWrapper::base64_key) constexpr +RsTypeSerializer::RawMemoryWrapper::base64_key; -template<> bool RsTypeSerializer::deserialize(const uint8_t data[],uint32_t size,uint32_t& offset,RsTypeSerializer::TlvMemBlock_proxy& r) +/*static*/ /* without this Android compilation breaks */ +constexpr uint32_t RsTypeSerializer::RawMemoryWrapper::MAX_SERIALIZED_CHUNK_SIZE; +#endif + +/*static*/ +void RsTypeSerializer::RawMemoryWrapper::serial_process( + RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx ) { - uint32_t saved_offset = offset ; + switch(j) + { + case RsGenericSerializer::SIZE_ESTIMATE: + RS_SERIAL_PROCESS(second); + ctx.mOffset += second; + break; + case RsGenericSerializer::SERIALIZE: + if(!ctx.mOk) break; + if(second > MAX_SERIALIZED_CHUNK_SIZE) + { + RsErr() << __PRETTY_FUNCTION__ + << std::errc::message_size << " " + << second << " > " << MAX_SERIALIZED_CHUNK_SIZE + << std::endl; + print_stacktrace(); + break; + } + RS_SERIAL_PROCESS(second); + if(!ctx.mOk) break; + ctx.mOk = ctx.mSize >= ctx.mOffset + second; + if(!ctx.mOk) + { + RsErr() << __PRETTY_FUNCTION__ << std::errc::no_buffer_space + << std::endl; + print_stacktrace(); + break; + } + memcpy(ctx.mData + ctx.mOffset, first, second); + ctx.mOffset += second; + break; + case RsGenericSerializer::DESERIALIZE: + freshMemCheck(); - bool ok = deserialize(data,size,offset,r.second) ; + RS_SERIAL_PROCESS(second); + if(!ctx.mOk) break; + ctx.mOk = (second <= MAX_SERIALIZED_CHUNK_SIZE); + if(!ctx.mOk) + { + RsErr() << __PRETTY_FUNCTION__ + << std::errc::message_size << " " + << second << " > " << MAX_SERIALIZED_CHUNK_SIZE + << std::endl; + clear(); + break; + } - if(r.second == 0) - { - r.first = NULL ; + if(!second) + { + Dbg3() << __PRETTY_FUNCTION__ << " Deserialized empty memory chunk" + << std::endl; + clear(); + break; + } - if(!ok) - offset = saved_offset ; + ctx.mOk = ctx.mSize >= ctx.mOffset + second; + if(!ctx.mOk) + { + RsErr() << __PRETTY_FUNCTION__ << std::errc::no_buffer_space + << std::endl; + print_stacktrace(); - return ok ; - } - if(r.second > MAX_SERIALIZED_CHUNK_SIZE) - { - std::cerr << "(EE) RsTypeSerializer::deserialize(): data chunk has size larger than safety size (" << MAX_SERIALIZED_CHUNK_SIZE << "). Item will be dropped." << std::endl; - offset = saved_offset ; - return false ; - } + clear(); + break; + } - r.first = (uint8_t*)rs_malloc(r.second) ; + first = reinterpret_cast(malloc(second)); + memcpy(first, ctx.mData + ctx.mOffset, second); + ctx.mOffset += second; + break; + case RsGenericSerializer::PRINT: break; + case RsGenericSerializer::TO_JSON: + { + if(!ctx.mOk) break; + std::string encodedValue; + RsBase64::encode(first, second, encodedValue, true, false); + ctx.mOk = ctx.mOk && + RsTypeSerializer::to_JSON(base64_key, encodedValue, ctx.mJson); + break; + } + case RsGenericSerializer::FROM_JSON: + { + freshMemCheck(); - ok = ok && (NULL != r.first); + const auto failure = [&]() -> void { ctx.mOk = false; clear(); }; + const bool yielding = !!( + RsSerializationFlags::YIELDING & ctx.mFlags ); + if(!(ctx.mOk || yielding)) return failure(); - memcpy(r.first,&data[offset],r.second) ; - offset += r.second ; + std::string encodedValue; + if(!RsTypeSerializer::from_JSON( + base64_key, encodedValue, ctx.mJson )) return failure(); - if(!ok) - offset = saved_offset ; + if( encodedValue.length() > + RsBase64::encodedSize(MAX_SERIALIZED_CHUNK_SIZE, true) ) + { + RsErr() << __PRETTY_FUNCTION__ << " " + << std::errc::message_size << std::endl; + print_stacktrace(); - return ok; + return failure(); + } + + std::vector decoded; + auto ec = RsBase64::decode(encodedValue, decoded); + if(ec) + { + RsErr() << __PRETTY_FUNCTION__ << " " << ec << std::endl; + print_stacktrace(); + + return failure(); + } + + const auto decodedSize = decoded.size(); + + if(!decodedSize) + { + clear(); + break; + } + + first = reinterpret_cast(malloc(decodedSize)); + second = static_cast(decodedSize); + + memcpy(first, decoded.data(), second); + break; + } + default: RsTypeSerializer::fatalUnknownSerialJob(j); + } } -template<> bool RsTypeSerializer::serialize(uint8_t data[],uint32_t size,uint32_t& offset,const RsTypeSerializer::TlvMemBlock_proxy& r) +void RsTypeSerializer::RawMemoryWrapper::clear() { - uint32_t saved_offset = offset ; - - bool ok = serialize(data,size,offset,r.second) ; - - memcpy(&data[offset],r.first,r.second) ; - offset += r.second ; - - if(!ok) - offset = saved_offset ; - - return ok; + free(first); + first = nullptr; + second = 0; } -template<> void RsTypeSerializer::print_data(const std::string& n, const RsTypeSerializer::TlvMemBlock_proxy& s) +bool RsTypeSerializer::RawMemoryWrapper::freshMemCheck() { - std::cerr << " [Binary data] " << n << ", length=" << s.second << " data=" << RsUtil::BinToHex((uint8_t*)s.first,std::min(50u,s.second)) << ((s.second>50)?"...":"") << std::endl; -} - -template<> /*static*/ -bool RsTypeSerializer::to_JSON( - const std::string& memberName, - const RsTypeSerializer::TlvMemBlock_proxy& member, RsJson& jDoc ) -{ - rapidjson::Document::AllocatorType& allocator = jDoc.GetAllocator(); - - rapidjson::Value key; - key.SetString( memberName.c_str(), - static_cast(memberName.length()), - allocator ); - - std::string encodedValue; - Radix64::encode( reinterpret_cast(member.first), - static_cast(member.second), encodedValue ); - - rapidjson::Value value; - value.SetString(encodedValue.data(), allocator); - - jDoc.AddMember(key, value, allocator); + if(first || second) + { + /* Items are created anew before deserialization so buffer pointer + * must be null and size 0 at this point */ + RsWarn() << __PRETTY_FUNCTION__ << " got uninitialized " + << " or pre-allocated buffer! Buffer pointer: " << first + << " must be null and size: " << second << " must be 0 at " + << "this point. Does your item costructor initialize them " + << "properly?" << std::endl; + print_stacktrace(); + return false; + } return true; } -template<> /*static*/ -bool RsTypeSerializer::from_JSON( const std::string& memberName, - RsTypeSerializer::TlvMemBlock_proxy& member, - RsJson& jDoc) -{ - SAFE_GET_JSON_V(); - ret = ret && v.IsString(); - if(ret) - { - std::string encodedValue = v.GetString(); - std::vector decodedValue = Radix64::decode(encodedValue); - member.second = decodedValue.size(); - - if(member.second == 0) - { - member.first = nullptr; - return ret; - } - - member.first = rs_malloc(member.second); - ret = ret && member.first && - memcpy(member.first, decodedValue.data(), member.second); - } - return ret; -} +//============================================================================// +// std::error_condition // +//============================================================================// void RsTypeSerializer::ErrConditionWrapper::serial_process( RsGenericSerializer::SerializeJob j, @@ -778,12 +686,12 @@ void RsTypeSerializer::ErrConditionWrapper::serial_process( case RsGenericSerializer::SIZE_ESTIMATE: // fallthrough case RsGenericSerializer::DESERIALIZE: // fallthrough case RsGenericSerializer::SERIALIZE: // fallthrough - case RsGenericSerializer::FROM_JSON: + case RsGenericSerializer::FROM_JSON: // [[fallthrough]] + case RsGenericSerializer::PRINT: RsFatal() << __PRETTY_FUNCTION__ << " SerializeJob: " << j << "is not supported on std::error_condition " << std::endl; print_stacktrace(); exit(-2); - case RsGenericSerializer::PRINT: // fallthrough case RsGenericSerializer::TO_JSON: { constexpr RsGenericSerializer::SerializeJob rj = diff --git a/libretroshare/src/serialiser/rstypeserializer.h b/libretroshare/src/serialiser/rstypeserializer.h index 55568632a..6474f6971 100644 --- a/libretroshare/src/serialiser/rstypeserializer.h +++ b/libretroshare/src/serialiser/rstypeserializer.h @@ -4,7 +4,8 @@ * libretroshare: retroshare core library * * * * Copyright (C) 2017 Cyril Soler * - * Copyright (C) 2018-2019 Gioacchino Mazzurco * + * Copyright (C) 2018-2020 Gioacchino Mazzurco * + * Copyright (C) 2020 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -24,125 +25,164 @@ #include // for typeid #include -#include +#include #include - +#include +#include #include "serialiser/rsserial.h" #include "serialiser/rstlvbase.h" #include "serialiser/rstlvlist.h" - #include "retroshare/rsflags.h" #include "retroshare/rsids.h" - +#include "util/rsendian.h" #include "serialiser/rsserializer.h" #include "serialiser/rsserializable.h" #include "util/rsjson.h" -#include "util/rsdebug.h" - -/* INTERNAL ONLY helper to avoid copy paste code for std::{vector,list,set} - * Can't use a template function because T is needed for const_cast */ -#define RsTypeSerializer_PRIVATE_TO_JSON_ARRAY() do \ -{ \ - using namespace rapidjson; \ -\ - Document::AllocatorType& allocator = ctx.mJson.GetAllocator(); \ -\ - Value arrKey; arrKey.SetString(memberName.c_str(), allocator); \ -\ - Value arr(kArrayType); \ -\ - for (auto& el : v) \ - { \ - /* Use same allocator to avoid deep copy */\ - RsGenericSerializer::SerializeContext elCtx(\ - nullptr, 0, ctx.mFlags, &allocator );\ -\ - /* If el is const the default serial_process template is matched */ \ - /* also when specialization is necessary so the compilation break */ \ - serial_process(j, elCtx, const_cast(el), memberName); \ -\ - elCtx.mOk = elCtx.mOk && elCtx.mJson.HasMember(arrKey);\ - if(elCtx.mOk) arr.PushBack(elCtx.mJson[arrKey], allocator);\ - else\ - {\ - ctx.mOk = false;\ - break;\ - }\ - }\ -\ - ctx.mJson.AddMember(arrKey, arr, allocator);\ -} while (false) - -/* INTERNAL ONLY helper to avoid copy paste code for std::{vector,list,set} - * Can't use a template function because std::{vector,list,set} has different - * name for insert/push_back function - */ -#define RsTypeSerializer_PRIVATE_FROM_JSON_ARRAY(INSERT_FUN) do\ -{\ - using namespace rapidjson;\ -\ - bool ok = ctx.mOk || ctx.mFlags & RsGenericSerializer::SERIALIZATION_FLAG_YIELDING;\ - Document& jDoc(ctx.mJson);\ - Document::AllocatorType& allocator = jDoc.GetAllocator();\ -\ - Value arrKey;\ - arrKey.SetString(memberName.c_str(), memberName.length());\ -\ - ok = ok && jDoc.IsObject();\ - ok = ok && jDoc.HasMember(arrKey);\ -\ - if(ok && jDoc[arrKey].IsArray())\ - {\ - for (auto&& arrEl : jDoc[arrKey].GetArray())\ - {\ - Value arrKeyT;\ - arrKeyT.SetString(memberName.c_str(), memberName.length());\ -\ - RsGenericSerializer::SerializeContext elCtx(\ - nullptr, 0, ctx.mFlags, &allocator );\ - elCtx.mJson.AddMember(arrKeyT, arrEl, allocator);\ -\ - T el;\ - serial_process(j, elCtx, el, memberName); \ - ok = ok && elCtx.mOk;\ - ctx.mOk &= ok;\ - if(ok) v.INSERT_FUN(el);\ - else break;\ - }\ - }\ - else\ - {\ - ctx.mOk = false;\ - }\ -\ -} while(false) +#include "util/rsdebuglevel1.h" +#include "util/cxx14retrocompat.h" struct RsTypeSerializer { - /** This type should be used to pass a parameter to drive the serialisation - * if needed */ - struct TlvMemBlock_proxy : std::pair + /** Use this wrapper to serialize raw memory chunks + * RsTypeSerializer::RawMemoryWrapper chunkWrapper(chunk_data, chunk_size); + * RsTypeSerializer::serial_process(j, ctx, chunkWrapper, "chunk_data"); + **/ + struct RawMemoryWrapper: std::pair, RsSerializable { - TlvMemBlock_proxy(void*& p, uint32_t& s) : - std::pair(p,s) {} - TlvMemBlock_proxy(uint8_t*& p,uint32_t& s) : - std::pair(*(void**)&p,s) {} + RawMemoryWrapper(uint8_t*& p,uint32_t& s) : + std::pair(p,s) {} + RawMemoryWrapper(void*& p, uint32_t& s) : + std::pair(*(uint8_t**)(&p),s) {} + + /// Maximum supported size 10MB + static constexpr uint32_t MAX_SERIALIZED_CHUNK_SIZE = 10*1024*1024; + + /** Key used for JSON serialization. + * @note Changing this value breaks JSON API retro-compatibility */ + static constexpr char base64_key[] = "base64"; + + /// @see RsSerializable + void serial_process( + RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx ) override; + private: + void clear(); + bool freshMemCheck(); }; - /// Generic types + /// Most types are not valid sequence containers + template + struct is_sequence_container : std::false_type {}; + + /// Trait to match supported strings types + template + struct is_string : std::is_same, std::string> {}; + + /// Integral types + template + typename std::enable_if::value>::type + static /*void*/ serial_process( + RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx, + INTT& member, const std::string& member_name ) + { + const bool VLQ_ENCODING = !!( + RsSerializationFlags::INTEGER_VLQ & ctx.mFlags ); + + switch(j) + { + case RsGenericSerializer::SIZE_ESTIMATE: + if(VLQ_ENCODING) ctx.mOffset += VLQ_size(member); + else ctx.mOffset += sizeof(INTT); + break; + case RsGenericSerializer::SERIALIZE: + { + if(!ctx.mOk) break; + if(VLQ_ENCODING) + ctx.mOk = VLQ_serialize( + ctx.mData, ctx.mSize, ctx.mOffset, member ); + else + { + ctx.mOk = ctx.mSize >= ctx.mOffset + sizeof(INTT); + if(!ctx.mOk) + { + RsErr() << __PRETTY_FUNCTION__ << " Cannot serialise " + << typeid(INTT).name() << " " + << " ctx.mSize: " << ctx.mSize + << " ctx.mOffset: " << ctx.mOffset + << " sizeof(INTT): " << sizeof(INTT) + << std::error_condition(std::errc::no_buffer_space) + << std::endl; + print_stacktrace(); + break; + } + INTT netorder_num = rs_endian_fix(member); + memcpy(ctx.mData + ctx.mOffset, &netorder_num, sizeof(INTT)); + ctx.mOffset += sizeof(INTT); + } + break; + } + case RsGenericSerializer::DESERIALIZE: + if(!ctx.mOk) break; + if(VLQ_ENCODING) ctx.mOk = VLQ_deserialize( + ctx.mData, ctx.mSize, ctx.mOffset, member ); + else + { + ctx.mOk = ctx.mSize >= ctx.mOffset + sizeof(INTT); + if(!ctx.mOk) + { + RsErr() << __PRETTY_FUNCTION__ << " Cannot deserialise " + << typeid(INTT).name() << " " + << " ctx.mSize: " << ctx.mSize + << " ctx.mOffset: " << ctx.mOffset + << " sizeof(INTT): " << sizeof(INTT) + << std::error_condition(std::errc::no_buffer_space) + << std::endl; + print_stacktrace(); + exit(-1); + break; + } + memcpy(&member, ctx.mData + ctx.mOffset, sizeof(INTT)); + member = rs_endian_fix(member); + ctx.mOffset += sizeof(INTT); + } + break; + case RsGenericSerializer::PRINT: break; + case RsGenericSerializer::TO_JSON: + ctx.mOk = ctx.mOk && to_JSON(member_name, member, ctx.mJson); + break; + case RsGenericSerializer::FROM_JSON: + ctx.mOk &= ( ctx.mOk || + !!(RsSerializationFlags::YIELDING & ctx.mFlags) ) + && from_JSON(member_name, member, ctx.mJson); + break; + default: fatalUnknownSerialJob(j); + } + } + +//============================================================================// +// Generic types // +//============================================================================// + template typename std::enable_if< std::is_same::value || !( + std::is_integral::value || std::is_base_of::value || std::is_enum::value || std::is_base_of::value || - std::is_same::value ) >::type + std::is_same::value || + is_sequence_container::value || is_string::value ) >::type static /*void*/ serial_process( RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx, - T& member, const std::string& member_name ) + T& memberC, const std::string& member_name ) { + // Avoid problems with const sneaking into template paramether + using m_t = std::remove_const_t; + m_t& member = const_cast(memberC); + switch(j) { case RsGenericSerializer::SIZE_ESTIMATE: @@ -163,19 +203,24 @@ struct RsTypeSerializer ctx.mOk = ctx.mOk && to_JSON(member_name, member, ctx.mJson); break; case RsGenericSerializer::FROM_JSON: - ctx.mOk &= (ctx.mOk || ctx.mFlags & RsGenericSerializer::SERIALIZATION_FLAG_YIELDING) + ctx.mOk &= ( ctx.mOk || + !!(ctx.mFlags & RsSerializationFlags::YIELDING) ) && from_JSON(member_name, member, ctx.mJson); break; default: fatalUnknownSerialJob(j); } } +//============================================================================// +// Generic types + type_id // +//============================================================================// + /// Generic types + type_id - template - static void serial_process( RsGenericSerializer::SerializeJob j, - RsGenericSerializer::SerializeContext& ctx, - uint16_t type_id, T& member, - const std::string& member_name ) + template RS_DEPRECATED + static void serial_process( + RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx, + uint16_t type_id, T& member, const std::string& member_name ) { switch(j) { @@ -190,101 +235,73 @@ struct RsTypeSerializer ctx.mOk = ctx.mOk && serialize(ctx.mData,ctx.mSize,ctx.mOffset,type_id,member); break; - case RsGenericSerializer::PRINT: - print_data(member_name, member); - break; + case RsGenericSerializer::PRINT: break; case RsGenericSerializer::TO_JSON: ctx.mOk = ctx.mOk && to_JSON(member_name, type_id, member, ctx.mJson); break; case RsGenericSerializer::FROM_JSON: ctx.mOk &= - (ctx.mOk || ctx.mFlags & RsGenericSerializer::SERIALIZATION_FLAG_YIELDING) + (ctx.mOk || !!(ctx.mFlags & RsSerializationFlags::YIELDING)) && from_JSON(member_name, type_id, member, ctx.mJson); break; default: fatalUnknownSerialJob(j); } } +//============================================================================// +// std::map // +//============================================================================// + /// std::map template static void serial_process( RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx, - std::map& v, + std::map& member, const std::string& memberName ) { switch(j) { - case RsGenericSerializer::SIZE_ESTIMATE: + case RsGenericSerializer::SIZE_ESTIMATE: // [[falltrough]] + case RsGenericSerializer::SERIALIZE: { - ctx.mOffset += 4; - for(typename std::map::iterator it(v.begin());it!=v.end();++it) + uint32_t mapSize = member.size(); + RS_SERIAL_PROCESS(mapSize); + + for( auto it = member.begin(); + ctx.mOk && it != member.end(); ++it ) { - serial_process( j, ctx, const_cast(it->first), - "map::*it->first" ); - serial_process( j,ctx,const_cast(it->second), - "map::*it->second" ); + RS_SERIAL_PROCESS(const_cast(it->first)); + RS_SERIAL_PROCESS(const_cast(it->second)); } break; } case RsGenericSerializer::DESERIALIZE: { - uint32_t n=0; - serial_process(j,ctx,n,"temporary size"); + uint32_t mapSize = 0; + RS_SERIAL_PROCESS(mapSize); - for(uint32_t i=0; ifirst"); - serial_process(j, ctx, u, "map::*it->second"); - v[t] = u; - } - break; - } - case RsGenericSerializer::SERIALIZE: - { - uint32_t n=v.size(); - serial_process(j,ctx,n,"temporary size"); - - for(typename std::map::iterator it(v.begin());it!=v.end();++it) - { - serial_process( j, ctx, const_cast(it->first), - "map::*it->first" ); - serial_process( j, ctx, const_cast(it->second), - "map::*it->second" ); - } - break; - } - case RsGenericSerializer::PRINT: - { - if(v.empty()) - std::cerr << " Empty map \"" << memberName << "\"" - << std::endl; - else - std::cerr << " std::map of " << v.size() << " elements: \"" - << memberName << "\"" << std::endl; - - for(typename std::map::iterator it(v.begin());it!=v.end();++it) - { - std::cerr << " "; - - serial_process( j, ctx,const_cast(it->first), - "map::*it->first" ); - serial_process( j, ctx, const_cast(it->second), - "map::*it->second" ); + RS_SERIAL_PROCESS(t); + RS_SERIAL_PROCESS(u); + member[t] = u; } break; } + case RsGenericSerializer::PRINT: break; case RsGenericSerializer::TO_JSON: { using namespace rapidjson; Document::AllocatorType& allocator = ctx.mJson.GetAllocator(); - Value arrKey; arrKey.SetString(memberName.c_str(), - memberName.length(), allocator); + Value arrKey; arrKey.SetString( + memberName.c_str(), + static_cast(memberName.length()), allocator ); Value arr(kArrayType); - for (auto& kv : v) + for (auto& kv : member) { // Use same allocator to avoid deep copy RsGenericSerializer::SerializeContext kCtx( @@ -313,12 +330,13 @@ struct RsTypeSerializer { using namespace rapidjson; - bool ok = ctx.mOk || ctx.mFlags & RsGenericSerializer::SERIALIZATION_FLAG_YIELDING; + bool ok = ctx.mOk || !!(ctx.mFlags & RsSerializationFlags::YIELDING); Document& jDoc(ctx.mJson); Document::AllocatorType& allocator = jDoc.GetAllocator(); Value arrKey; - arrKey.SetString(memberName.c_str(), memberName.length()); + arrKey.SetString( memberName.c_str(), + static_cast(memberName.length()) ); ok = ok && jDoc.IsObject(); ok = ok && jDoc.HasMember(arrKey); @@ -350,35 +368,37 @@ struct RsTypeSerializer vCtx.mOk ); ctx.mOk &= ok; - if(ok) v.insert(std::pair(key,value)); + if(ok) member.insert(std::pair(key,value)); else break; } } - else - { - ctx.mOk = false; - } + else ctx.mOk = false; break; } default: fatalUnknownSerialJob(j); } } + +//============================================================================// +// std::pair // +//============================================================================// + /// std::pair template - static void serial_process( RsGenericSerializer::SerializeJob j, - RsGenericSerializer::SerializeContext& ctx, - std::pair& p, - const std::string& memberName ) + static void serial_process( + RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx, + std::pair& member, const std::string& memberName ) { switch(j) { - case RsGenericSerializer::SIZE_ESTIMATE: // fallthrough - case RsGenericSerializer::DESERIALIZE: // fallthrough - case RsGenericSerializer::SERIALIZE: // fallthrough + case RsGenericSerializer::SIZE_ESTIMATE: // [[fallthrough]] + case RsGenericSerializer::DESERIALIZE: // [[fallthrough]] + case RsGenericSerializer::SERIALIZE: // [[fallthrough]] case RsGenericSerializer::PRINT: - serial_process(j, ctx, p.first, "pair::first"); - serial_process(j, ctx, p.second, "pair::second"); + RS_SERIAL_PROCESS(member.first); + RS_SERIAL_PROCESS(member.second); break; case RsGenericSerializer::TO_JSON: { @@ -389,11 +409,13 @@ struct RsTypeSerializer RsGenericSerializer::SerializeContext lCtx( nullptr, 0, ctx.mFlags, &allocator ); - serial_process(j, lCtx, p.first, "first"); - serial_process(j, lCtx, p.second, "second"); + serial_process(j, lCtx, member.first, "first"); + serial_process(j, lCtx, member.second, "second"); rapidjson::Value key; - key.SetString(memberName.c_str(), memberName.length(), allocator); + key.SetString( memberName.c_str(), + static_cast(memberName.length()), + allocator); /* Because the passed allocator is reused it doesn't go out of scope * and there is no need of deep copy and we can take advantage of @@ -408,16 +430,14 @@ struct RsTypeSerializer RsJson& jDoc(ctx.mJson); const char* mName = memberName.c_str(); bool hasMember = jDoc.HasMember(mName); - bool yielding = ctx.mFlags & - RsGenericSerializer::SERIALIZATION_FLAG_YIELDING; + bool yielding = !!(ctx.mFlags & RsSerializationFlags::YIELDING); if(!hasMember) { if(!yielding) { - std::cerr << __PRETTY_FUNCTION__ << " \"" << memberName - << "\" not found in JSON:" << std::endl - << jDoc << std::endl << std::endl; + RsErr() << __PRETTY_FUNCTION__ << " \"" << memberName + << "\" not found in JSON" << std::endl; print_stacktrace(); } ctx.mOk = false; @@ -429,8 +449,8 @@ struct RsTypeSerializer RsGenericSerializer::SerializeContext lCtx(nullptr, 0, ctx.mFlags); lCtx.mJson.SetObject() = v; // Beware of move semantic!! - serial_process(j, lCtx, p.first, "first"); - serial_process(j, lCtx, p.second, "second"); + serial_process(j, lCtx, member.first, "first"); + serial_process(j, lCtx, member.second, "second"); ctx.mOk &= lCtx.mOk; break; @@ -439,176 +459,237 @@ struct RsTypeSerializer } } - /// std::vector +//============================================================================// +// Sequence containers // +//============================================================================// + + /** std::list is supported */ template + struct is_sequence_container>: std::true_type {}; + + /** std::set is supported */ template + struct is_sequence_container>: std::true_type {}; + + /** std::vector is supported */ template + struct is_sequence_container>: std::true_type {}; + + + /// STL compatible sequence containers std::list, std::set, std::vector... template - static void serial_process( RsGenericSerializer::SerializeJob j, - RsGenericSerializer::SerializeContext& ctx, - std::vector& v, - const std::string& memberName ) + typename std::enable_if::value>::type + static serial_process( + RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx, + T& member, const std::string& memberName ) { + using el_t = typename T::value_type; + switch(j) { - case RsGenericSerializer::SIZE_ESTIMATE: + case RsGenericSerializer::SIZE_ESTIMATE: // [[falltrough]]; + case RsGenericSerializer::SERIALIZE: { - ctx.mOffset += 4; - for(uint32_t i=0;i RsSerialiser::MAX_SERIAL_SIZE) { - std::cerr << " " ; - serial_process(j,ctx,v[i],memberName); + ctx.mOk = false; + RsErr() << __PRETTY_FUNCTION__ << " attempt to deserialize a " + << "sequence with apparently malformed elements count." + << " elCount: " << elCount + << " ctx.mSize: " << ctx.mSize + << " ctx.mOffset: " << ctx.mOffset << " " + << std::errc::argument_out_of_domain + << std::endl; + print_stacktrace(); + break; + } + + for(uint32_t i=0; ctx.mOk && i < elCount; ++i ) + { + el_t elem; + RS_SERIAL_PROCESS(elem); + member.insert(member.end(), elem); } break; } + case RsGenericSerializer::PRINT: break; case RsGenericSerializer::TO_JSON: - RsTypeSerializer_PRIVATE_TO_JSON_ARRAY(); + { + using namespace rapidjson; + + Document::AllocatorType& allocator = ctx.mJson.GetAllocator(); + + Value arrKey; arrKey.SetString(memberName.c_str(), allocator); + Value arr(kArrayType); + + for(auto& const_el : member) + { + auto el = const_cast(const_el); + + /* Use same allocator to avoid deep copy */ + RsGenericSerializer::SerializeContext elCtx( + nullptr, 0, ctx.mFlags, &allocator ); + serial_process(j, elCtx, el, memberName); + + elCtx.mOk = elCtx.mOk && elCtx.mJson.HasMember(arrKey); + if(elCtx.mOk) arr.PushBack(elCtx.mJson[arrKey], allocator); + else + { + ctx.mOk = false; + break; + } + } + + ctx.mJson.AddMember(arrKey, arr, allocator); break; + } case RsGenericSerializer::FROM_JSON: - RsTypeSerializer_PRIVATE_FROM_JSON_ARRAY(push_back); + { + using namespace rapidjson; + + bool ok = ctx.mOk || !!(ctx.mFlags & RsSerializationFlags::YIELDING); + Document& jDoc(ctx.mJson); + Document::AllocatorType& allocator = jDoc.GetAllocator(); + + Value arrKey; + arrKey.SetString( memberName.c_str(), + static_cast(memberName.length()) ); + + ok = ok && jDoc.IsObject() && jDoc.HasMember(arrKey) + && jDoc[arrKey].IsArray(); + if(!ok) { ctx.mOk = false; break; } + + for (auto&& arrEl : jDoc[arrKey].GetArray()) + { + Value arrKeyT; + arrKeyT.SetString( + memberName.c_str(), + static_cast(memberName.length()) ); + + RsGenericSerializer::SerializeContext elCtx( + nullptr, 0, ctx.mFlags, &allocator ); + elCtx.mJson.AddMember(arrKeyT, arrEl, allocator); + + el_t el; + serial_process(j, elCtx, el, memberName); + ok = ok && elCtx.mOk; + ctx.mOk &= ok; + if(ok) member.insert(member.end(), el); + else break; + } + break; + } default: fatalUnknownSerialJob(j); } } - /// std::set - template - static void serial_process( RsGenericSerializer::SerializeJob j, - RsGenericSerializer::SerializeContext& ctx, - std::set& v, const std::string& memberName ) - { - switch(j) - { - case RsGenericSerializer::SIZE_ESTIMATE: - { - ctx.mOffset += 4; - for(typename std::set::iterator it(v.begin());it!=v.end();++it) - // the const cast here is a hack to avoid serial_process to - // instantiate serialise(const T&) - serial_process(j,ctx,const_cast(*it) ,memberName); - break; - } - case RsGenericSerializer::DESERIALIZE: - { - uint32_t n=0; - serial_process(j,ctx,n,"temporary size"); - for(uint32_t i=0; i::iterator it(v.begin());it!=v.end();++it) - // the const cast here is a hack to avoid serial_process to - // instantiate serialise(const T&) - serial_process(j,ctx,const_cast(*it) ,memberName); - break; - } - case RsGenericSerializer::PRINT: - { - if(v.empty()) - std::cerr << " Empty set \"" << memberName << "\"" - << std::endl; - else - std::cerr << " Set of " << v.size() << " elements: \"" - << memberName << "\"" << std::endl; - break; - } - case RsGenericSerializer::TO_JSON: - RsTypeSerializer_PRIVATE_TO_JSON_ARRAY(); - break; - case RsGenericSerializer::FROM_JSON: - RsTypeSerializer_PRIVATE_FROM_JSON_ARRAY(insert); - break; - default: fatalUnknownSerialJob(j); - } - } +//============================================================================// +// Strings // +//============================================================================// - /// std::list + /// Strings template - static void serial_process( RsGenericSerializer::SerializeJob j, - RsGenericSerializer::SerializeContext& ctx, - std::list& v, - const std::string& memberName ) + typename std::enable_if::value>::type + static serial_process( + RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx, + T& memberC, + const std::string& memberName ) { + // Avoid problems with const sneaking into template paramether + using m_t = std::remove_const_t; + m_t& member = const_cast(memberC); + switch(j) { case RsGenericSerializer::SIZE_ESTIMATE: { - ctx.mOffset += 4; - for(typename std::list::iterator it(v.begin());it!=v.end();++it) - serial_process(j,ctx,*it ,memberName); - break; - } - case RsGenericSerializer::DESERIALIZE: - { - uint32_t n=0; - serial_process(j,ctx,n,"temporary size"); - for(uint32_t i=0;i(member.size()); + RS_SERIAL_PROCESS(aSize); + ctx.mOffset += aSize; break; } case RsGenericSerializer::SERIALIZE: { - uint32_t n=v.size(); - serial_process(j,ctx,n,"temporary size"); - for(typename std::list::iterator it(v.begin());it!=v.end();++it) - serial_process(j,ctx,*it ,memberName); + uint32_t len = static_cast(member.length()); + RS_SERIAL_PROCESS(len); + if(len + ctx.mOffset > ctx.mSize) + { + RsErr() << __PRETTY_FUNCTION__ << std::errc::no_buffer_space + << std::endl; + ctx.mOk = false; + } + memcpy(ctx.mData + ctx.mOffset, member.c_str(), len); + ctx.mOffset += len; break; } - case RsGenericSerializer::PRINT: + case RsGenericSerializer::DESERIALIZE: { - if(v.empty()) - std::cerr << " Empty list \"" << memberName << "\"" - << std::endl; - else - std::cerr << " List of " << v.size() << " elements: \"" - << memberName << "\"" << std::endl; + uint32_t len; + RS_SERIAL_PROCESS(len); + if(!ctx.mOk) break; + + if(len > RsSerialiser::MAX_SERIAL_SIZE) + { + ctx.mOk = false; + RsErr() << __PRETTY_FUNCTION__ << " attempt to deserialize a " + << "string with apparently malformed length." + << " len: " << len + << " ctx.mSize: " << ctx.mSize + << " ctx.mOffset: " << ctx.mOffset << " " + << std::errc::argument_out_of_domain << std::endl; + print_stacktrace(); + break; + } + + if(len + ctx.mOffset > ctx.mSize) + { + ctx.mOk = false; + RsErr() << __PRETTY_FUNCTION__ << " attempt to deserialize a " + << "string with a length bigger available data." + << " len: " << len + << " ctx.mSize: " << ctx.mSize + << " ctx.mOffset: " << ctx.mOffset << " " + << std::errc::no_buffer_space << std::endl; + print_stacktrace(); + break; + } + + member.resize(len); + memcpy(&member[0], ctx.mData + ctx.mOffset, len); + ctx.mOffset += len; break; } + case RsGenericSerializer::PRINT: break; case RsGenericSerializer::TO_JSON: - RsTypeSerializer_PRIVATE_TO_JSON_ARRAY(); + ctx.mOk = ctx.mOk && to_JSON(memberName, member, ctx.mJson); break; case RsGenericSerializer::FROM_JSON: - RsTypeSerializer_PRIVATE_FROM_JSON_ARRAY(push_back); + { + bool ok = ctx.mOk || !!( + ctx.mFlags & RsSerializationFlags::YIELDING ); + ctx.mOk = ok && from_JSON(memberName, member, ctx.mJson) && ctx.mOk; break; + } default: fatalUnknownSerialJob(j); } } @@ -619,46 +700,10 @@ struct RsTypeSerializer RsGenericSerializer::SerializeContext& ctx, t_RsFlags32& v, const std::string& memberName ) - { - switch(j) - { - case RsGenericSerializer::SIZE_ESTIMATE: ctx.mOffset += 4; break; - case RsGenericSerializer::DESERIALIZE: - { - uint32_t n=0; - deserialize(ctx.mData,ctx.mSize,ctx.mOffset,n); - v = t_RsFlags32(n); - break; - } - case RsGenericSerializer::SERIALIZE: - { - uint32_t n=v.toUInt32(); - serialize(ctx.mData,ctx.mSize,ctx.mOffset,n); - break; - } - case RsGenericSerializer::PRINT: - std::cerr << " Flags of type " << std::hex << N << " : " - << v.toUInt32() << std::endl; - break; - case RsGenericSerializer::TO_JSON: - ctx.mOk = to_JSON(memberName, v.toUInt32(), ctx.mJson); - break; - case RsGenericSerializer::FROM_JSON: - { - uint32_t f = 0; - ctx.mOk &= - (ctx.mOk || ctx.mFlags & RsGenericSerializer::SERIALIZATION_FLAG_YIELDING) - && from_JSON(memberName, f, ctx.mJson) - && (v = t_RsFlags32(f), true); - break; - } - default: fatalUnknownSerialJob(j); - } - } + { serial_process(j, ctx, v._bits, memberName); } /** * @brief serial process enum types - * * On declaration of your member of enum type you must specify the * underlying type otherwise the serialization format may differ in an * uncompatible way depending on the compiler/platform. @@ -670,12 +715,9 @@ struct RsTypeSerializer E& member, const std::string& memberName ) { -#ifdef RSSERIAL_DEBUG - std::cerr << __PRETTY_FUNCTION__ << " processing enum: " - << typeid(E).name() << " as " - << typeid(typename std::underlying_type::type).name() - << std::endl; -#endif + RS_DBG4( "processing enum: ", typeid(E).name(), " as ", + typeid(typename std::underlying_type::type).name() ); + serial_process( j, ctx, reinterpret_cast::type&>(member), @@ -685,11 +727,14 @@ struct RsTypeSerializer /// RsSerializable and derivatives template typename std::enable_if::value>::type - static /*void*/ serial_process( RsGenericSerializer::SerializeJob j, - RsGenericSerializer::SerializeContext& ctx, - T& member, - const std::string& memberName ) + static serial_process( + RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx, + T& memberC, const std::string& memberName ) { + using m_t = std::remove_const_t; + m_t& member = const_cast(memberC); + switch(j) { case RsGenericSerializer::SIZE_ESTIMATE: // fallthrough @@ -710,11 +755,14 @@ struct RsTypeSerializer member.serial_process(j, lCtx); rapidjson::Value key; - key.SetString(memberName.c_str(), memberName.length(), allocator); + key.SetString( + memberName.c_str(), + static_cast(memberName.length()), + allocator ); - /* Because the passed allocator is reused it doesn't go out of scope and - * there is no need of deep copy and we can take advantage of the much - * faster rapidjson move semantic */ + /* Because the passed allocator is reused it doesn't go out of scope + * and there is no need of deep copy and we can take advantage of + * the much faster rapidjson move semantic */ jDoc.AddMember(key, lCtx.mJson, allocator); ctx.mOk = ctx.mOk && lCtx.mOk; @@ -725,16 +773,15 @@ struct RsTypeSerializer RsJson& jDoc(ctx.mJson); const char* mName = memberName.c_str(); bool hasMember = jDoc.HasMember(mName); - bool yielding = ctx.mFlags & - RsGenericSerializer::SERIALIZATION_FLAG_YIELDING; + bool yielding = !!(ctx.mFlags & RsSerializationFlags::YIELDING); if(!hasMember) { if(!yielding) { - std::cerr << __PRETTY_FUNCTION__ << " \"" << memberName - << "\" not found in JSON:" << std::endl - << jDoc << std::endl << std::endl; + RsErr() << __PRETTY_FUNCTION__ << " \"" << memberName + << "\" not found in JSON:" << std::endl + << jDoc << std::endl << std::endl; print_stacktrace(); } ctx.mOk = false; @@ -745,9 +792,9 @@ struct RsTypeSerializer if(!v.IsObject()) { - std::cerr << __PRETTY_FUNCTION__ << " \"" << memberName - << "\" has wrong type in JSON, object expected, got:" - << std::endl << jDoc << std::endl << std::endl; + RsErr() << __PRETTY_FUNCTION__ << " \"" << memberName + << "\" has wrong type in JSON, object expected, got:" + << std::endl << jDoc << std::endl << std::endl; print_stacktrace(); ctx.mOk = false; break; @@ -768,15 +815,13 @@ struct RsTypeSerializer /// RsTlvItem derivatives only template typename std::enable_if< - std::is_base_of::value && !std::is_same::value - >::type - static /*void*/ serial_process( RsGenericSerializer::SerializeJob j, - RsGenericSerializer::SerializeContext& ctx, - T& member, - const std::string& memberName ) - { - serial_process(j, ctx, static_cast(member), memberName); - } + std::is_base_of::value && + !std::is_same::value >::type + static serial_process( + RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx, + T& member, const std::string& memberName ) + { serial_process(j, ctx, static_cast(member), memberName); } /** std::error_condition * supports only TO_JSON ErrConditionWrapper::serial_process will explode @@ -786,13 +831,15 @@ struct RsTypeSerializer static /*void*/ serial_process( RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx, - const T& cond, - const std::string& member_name ) + T& cond, const std::string& member_name ) { ErrConditionWrapper ew(cond); serial_process(j, ctx, ew, member_name); } + RS_DEPRECATED_FOR(RawMemoryWrapper) + typedef RawMemoryWrapper TlvMemBlock_proxy; + protected: //============================================================================// @@ -820,27 +867,32 @@ protected: // Generic types + type_id declarations // //============================================================================// - template static bool serialize( + template RS_DEPRECATED + static bool serialize( uint8_t data[], uint32_t size, uint32_t &offset, uint16_t type_id, const T& member ); - template static bool deserialize( + template RS_DEPRECATED + static bool deserialize( const uint8_t data[], uint32_t size, uint32_t &offset, uint16_t type_id, T& member ); - template static uint32_t serial_size( - uint16_t type_id,const T& member ); + template RS_DEPRECATED + static uint32_t serial_size(uint16_t type_id,const T& member); - template static void print_data( const std::string& n, - uint16_t type_id,const T& member ); + template RS_DEPRECATED + static void print_data( + const std::string& n, uint16_t type_id,const T& member ); - template static bool to_JSON( const std::string& membername, - uint16_t type_id, - const T& member, RsJson& jVal ); + template RS_DEPRECATED + static bool to_JSON( + const std::string& membername, uint16_t type_id, + const T& member, RsJson& jVal ); - template static bool from_JSON( const std::string& memberName, - uint16_t type_id, - T& member, RsJson& jDoc ); + template RS_DEPRECATED + static bool from_JSON( + const std::string& memberName, uint16_t type_id, + T& member, RsJson& jDoc ); //============================================================================// // t_RsGenericId<...> declarations // @@ -910,16 +962,195 @@ protected: t_RsTlvList& member, RsJson& jDoc ); - [[noreturn]] static void fatalUnknownSerialJob(int j) +//============================================================================// +// Integral types VLQ // +//============================================================================// + + /** + * Size calculation of unsigned integers as Variable Lenght Quantity + * @see RsSerializationFlags::INTEGER_VLQ + * @see https://en.wikipedia.org/wiki/Variable-length_quantity + * @see https://golb.hplar.ch/2019/06/variable-length-int-java.html + * @see https://techoverflow.net/2013/01/25/efficiently-encoding-variable-length-integers-in-cc/ + */ + template static + std::enable_if_t>::value, uint32_t> + VLQ_size(T member) { - RsFatal() << " Unknown serial job: " << j << std::endl; - print_stacktrace(); - exit(EINVAL); + std::decay_t memberBackup = member; + uint32_t ret = 1; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wbool-compare" + while(member > 127) { ++ret; member >>= 7; } +#pragma GCC diagnostic pop + Dbg2() << __PRETTY_FUNCTION__ << " memberBackup: " << memberBackup + << " return: " << ret << std::endl; + return ret; } + /** + * Serialization of unsigned integers as Variable Lenght Quantity + * @see RsSerializationFlags::INTEGER_VLQ + * @see https://en.wikipedia.org/wiki/Variable-length_quantity + * @see https://golb.hplar.ch/2019/06/variable-length-int-java.html + * @see https://techoverflow.net/2013/01/25/efficiently-encoding-variable-length-integers-in-cc/ + */ + template static + std::enable_if_t>::value, bool> + VLQ_serialize( + uint8_t data[], uint32_t size, uint32_t &offset, T member ) + { + std::decay_t backupMember = member; +#if RS_DEBUG_LEVEL >= 3 + uint32_t offsetBackup = offset; +#endif + + bool ok = true; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wbool-compare" + /* Check with < and not with <= here as we write last byte after + * the loop. Order of && operands very important here! */ + while(member > 127 && (ok = (offset < size))) + { + // | 128: Set the next byte flag + data[offset++] = (static_cast(member & 127)) | 128; + // Remove the seven bits we just wrote + member >>= 7; + } +#pragma GCC diagnostic pop + + if(!(ok = ok && offset <= size)) + { + RsErr() << __PRETTY_FUNCTION__ << " Cannot serialise " + << typeid(T).name() + << " member " << member + << " size: " << size + << " offset: " << offset + << " backupMember: " << backupMember + << std::error_condition(std::errc::no_buffer_space) + << std::endl; + print_stacktrace(); + return false; + } + + data[offset++] = static_cast(member & 127); + +#if RS_DEBUG_LEVEL >= 3 + RsDbg tdbg( __PRETTY_FUNCTION__, " backupMember: ", backupMember, + " offsetBackup: ", offsetBackup, " offeset: ", offset, + " serialized as: " ); + for(; offsetBackup < offset; ++offsetBackup) + tdbg << " " << std::bitset<8>(data[offsetBackup]); +#endif + return ok; + } + + /** + * Deserialization for unsigned integers as Variable Lenght Quantity + * @see RsSerializationFlags::INTEGER_VLQ + * @see https://en.wikipedia.org/wiki/Variable-length_quantity + * @see https://golb.hplar.ch/2019/06/variable-length-int-java.html + * @see https://techoverflow.net/2013/01/25/efficiently-encoding-variable-length-integers-in-cc/ + */ + template static + std::enable_if_t>::value, bool> + VLQ_deserialize( + const uint8_t data[], uint32_t size, uint32_t& offset, T& member ) + { + member = 0; + uint32_t offsetBackup = offset; + + /* In a reasonable VLQ coding representing an integer + * could take at maximum sizeof(integer) + 1 space, if it is + * not the case something fishy is happening. */ + for (size_t i = 0; offset < size && i <= sizeof(T); ++i) + { + member |= (data[offset] & 127) << (7 * i); + // If the next-byte flag is not set. ++ is after on purpose + if(!(data[offset++] & 128)) + { + Dbg2() << __PRETTY_FUNCTION__ + << " size: " << size + << " backupOffset " << offsetBackup + << " offset: " << offset + << " member " << member << std::endl; + return true; + } + } + + /* If return is not triggered inside the for loop, either the buffer + * ended before we encountered the end of the number, or the number + * is VLQ encoded improperly */ + RsErr rserr; + rserr << __PRETTY_FUNCTION__ << std::errc::illegal_byte_sequence + << " size: " << size + << " offsetBackup: " << offsetBackup + << " offset: " << offset << " bytes: "; + for(; offsetBackup < offset; ++offsetBackup) + rserr << " " << std::bitset<8>(data[offsetBackup]); + print_stacktrace(); + + return false; + } + + /** + * Size calculation of signed integers as Variable Lenght Quantity + * @see RsSerializationFlags::INTEGER_VLQ + * @see https://en.wikipedia.org/wiki/Variable-length_quantity#Zigzag_encoding + * @see https://golb.hplar.ch/2019/06/variable-length-int-java.html + */ + template static + std::enable_if_t>::value, uint32_t> + VLQ_size(T member) + { + member = (member << 1) ^ (member >> (sizeof(T)-1)); // ZigZag encoding + return VLQ_size( + static_cast::type>(member)); + } + + /** + * Serialization of signed integers as Variable Lenght Quantity + * @see RsSerializationFlags::INTEGER_VLQ + * @see https://en.wikipedia.org/wiki/Variable-length_quantity#Zigzag_encoding + * @see https://golb.hplar.ch/2019/06/variable-length-int-java.html + */ + template static + std::enable_if_t>::value, bool> + VLQ_serialize( + uint8_t data[], uint32_t size, uint32_t &offset, T member ) + { + member = (member << 1) ^ (member >> (sizeof(T)-1)); // ZigZag encoding + return VLQ_serialize( + data, size, offset, + static_cast::type>(member)); + } + + /** + * Deserialization for signed integers as Variable Lenght Quantity + * @see RsSerializationFlags::INTEGER_VLQ + * @see https://en.wikipedia.org/wiki/Variable-length_quantity#Zigzag_encoding + * @see https://golb.hplar.ch/2019/06/variable-length-int-java.html + */ + template static + std::enable_if_t>::value, bool> + VLQ_deserialize( + const uint8_t data[], uint32_t size, uint32_t& offset, T& member ) + { + using DT = std::decay_t; + typename std::make_unsigned
::type temp = 0; + bool ok = VLQ_deserialize(data, size, offset, temp); + // ZizZag decoding + member = (static_cast
(temp) >> 1) ^ -(static_cast
(temp) & 1); + return ok; + } + +//============================================================================// +// Error Condition Wrapper // +//============================================================================// + struct ErrConditionWrapper : RsSerializable { - ErrConditionWrapper(const std::error_condition& ec): mec(ec) {} + explicit ErrConditionWrapper(const std::error_condition& ec): mec(ec) {} /** supports only TO_JSON if a different SerializeJob is passed it will * explode at runtime */ @@ -931,6 +1162,17 @@ protected: const std::error_condition& mec; }; +//============================================================================// +// Miscellanea // +//============================================================================// + + [[noreturn]] static void fatalUnknownSerialJob(int j) + { + RsFatal() << " Unknown serial job: " << j << std::endl; + print_stacktrace(); + exit(EINVAL); + } + RS_SET_CONTEXT_DEBUG_LEVEL(1) }; @@ -1079,7 +1321,6 @@ bool RsTypeSerializer::from_JSON( const std::string& memberName, return true; } - //============================================================================// // Not implemented types macros // //============================================================================// diff --git a/libretroshare/src/services/autoproxy/p3i2pbob.cc b/libretroshare/src/services/autoproxy/p3i2pbob.cc index 2cdec3b4f..f9eb3d9b3 100644 --- a/libretroshare/src/services/autoproxy/p3i2pbob.cc +++ b/libretroshare/src/services/autoproxy/p3i2pbob.cc @@ -43,21 +43,14 @@ static const std::string kConfigKeyOutLength = "OUT_LENGTH"; static const std::string kConfigKeyOutQuantity = "OUT_QUANTITY"; static const std::string kConfigKeyOutVariance = "OUT_VARIANCE"; -static const bool kDefaultBOBEnable = false; -static const int8_t kDefaultLength = 3; -static const int8_t kDefaultQuantity = 4; -static const int8_t kDefaultVariance = 0; - -/// Sleep duration for receiving loop -static const useconds_t sleepTimeRecv = 10; // times 1000 = 10ms +/// Sleep duration for receiving loop in error/no-data case +static const useconds_t sleepTimeRecv = 250; // times 1000 = 250ms /// Sleep duration for everything else static const useconds_t sleepTimeWait = 50; // times 1000 = 50ms or 0.05s static const int sleepFactorDefault = 10; // 0.5s static const int sleepFactorFast = 1; // 0.05s static const int sleepFactorSlow = 20; // 1s -static struct RsLog::logInfo i2pBobLogInfo = {RsLog::Default, "p3I2pBob"}; - static const rstime_t selfCheckPeroid = 30; void doSleep(useconds_t timeToSleepMS) { @@ -74,15 +67,7 @@ p3I2pBob::p3I2pBob(p3PeerMgr *peerMgr) mProcessing(NULL), mLock("I2P-BOB") { // set defaults - mSetting.enableBob = kDefaultBOBEnable; - mSetting.keys = ""; - mSetting.addr = ""; - mSetting.inLength = kDefaultLength; - mSetting.inQuantity = kDefaultQuantity; - mSetting.inVariance = kDefaultVariance; - mSetting.outLength = kDefaultLength; - mSetting.outQuantity = kDefaultQuantity; - mSetting.outVariance = kDefaultVariance; + mSetting.initDefault(); mCommands.clear(); } @@ -90,12 +75,12 @@ p3I2pBob::p3I2pBob(p3PeerMgr *peerMgr) bool p3I2pBob::isEnabled() { RS_STACK_MUTEX(mLock); - return mSetting.enableBob; + return mSetting.enable; } bool p3I2pBob::initialSetup(std::string &addr, uint16_t &/*port*/) { - std::cout << "p3I2pBob::initialSetup" << std::endl; + RS_DBG(""); // update config { @@ -108,7 +93,7 @@ bool p3I2pBob::initialSetup(std::string &addr, uint16_t &/*port*/) } } - std::cout << "p3I2pBob::initialSetup config updated" << std::endl; + RS_DBG("config updated"); // request keys // p3I2pBob::stateMachineBOB expects mProcessing to be set therefore @@ -118,12 +103,12 @@ bool p3I2pBob::initialSetup(std::string &addr, uint16_t &/*port*/) fakeTicket->task = autoProxyTask::receiveKey; processTaskAsync(fakeTicket); - std::cout << "p3I2pBob::initialSetup fakeTicket requested" << std::endl; + RS_DBG("fakeTicket requested"); // now start thread start("I2P-BOB gen key"); - std::cout << "p3I2pBob::initialSetup thread started" << std::endl; + RS_DBG("thread started"); int counter = 0; // wait for keys @@ -137,24 +122,24 @@ bool p3I2pBob::initialSetup(std::string &addr, uint16_t &/*port*/) break; if (++counter > 30) { - std::cout << "p3I2pBob::initialSetup timeout!" << std::endl; + RS_DBG4("timeout!"); return false; } } - std::cout << "p3I2pBob::initialSetup got keys" << std::endl; + RS_DBG("got keys"); // stop thread fullstop(); - std::cout << "p3I2pBob::initialSetup thread stopped" << std::endl; + RS_DBG("thread stopped"); { RS_STACK_MUTEX(mLock); - addr = mSetting.addr; + addr = mSetting.address.base32; } - std::cout << "p3I2pBob::initialSetup addr '" << addr << "'" << std::endl; + RS_DBG4("addr ", addr); return true; } @@ -172,7 +157,7 @@ void p3I2pBob::processTaskAsync(taskTicket *ticket) } break; default: - rslog(RsLog::Warning, &i2pBobLogInfo, "p3I2pBob::processTaskAsync unknown task"); + RS_DBG("unknown task"); rsAutoProxyMonitor::taskError(ticket); break; } @@ -187,7 +172,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) case autoProxyTask::status: // check if everything needed is set if (!data) { - rslog(RsLog::Warning, &i2pBobLogInfo, "p3I2pBob::status autoProxyTask::status data is missing"); + RS_DBG("autoProxyTask::status data is missing"); rsAutoProxyMonitor::taskError(ticket); break; } @@ -201,7 +186,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) case autoProxyTask::getSettings: // check if everything needed is set if (!data) { - rslog(RsLog::Warning, &i2pBobLogInfo, "p3I2pBob::data_tick autoProxyTask::getSettings data is missing"); + RS_DBG("autoProxyTask::getSettings data is missing"); rsAutoProxyMonitor::taskError(ticket); break; } @@ -215,7 +200,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) case autoProxyTask::setSettings: // check if everything needed is set if (!data) { - rslog(RsLog::Warning, &i2pBobLogInfo, "p3I2pBob::data_tick autoProxyTask::setSettings data is missing"); + RS_DBG("autoProxyTask::setSettings data is missing"); rsAutoProxyMonitor::taskError(ticket); break; } @@ -235,7 +220,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) break; case autoProxyTask::getErrorInfo: if (!data) { - rslog(RsLog::Warning, &i2pBobLogInfo, "p3I2pBob::data_tick autoProxyTask::getErrorInfo data is missing"); + RS_DBG("autoProxyTask::getErrorInfo data is missing"); rsAutoProxyMonitor::taskError(ticket); } else { RS_STACK_MUTEX(mLock); @@ -244,34 +229,12 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) } break; default: - rslog(RsLog::Warning, &i2pBobLogInfo, "p3I2pBob::processTaskSync unknown task"); + RS_DBG("unknown task"); rsAutoProxyMonitor::taskError(ticket); break; } } -std::string p3I2pBob::keyToBase32Addr(const std::string &key) -{ - std::string copy(key); - - // replace I2P specific chars - std::replace(copy.begin(), copy.end(), '~', '/'); - std::replace(copy.begin(), copy.end(), '-', '+'); - - // decode - std::vector bin = Radix64::decode(copy); - // hash - std::vector sha256 = RsUtil::BinToSha256(bin); - // encode - std::string out = Radix32::encode(sha256); - - // i2p uses lowercase - std::transform(out.begin(), out.end(), out.begin(), ::tolower); - out.append(".b32.i2p"); - - return out; -} - bool inline isAnswerOk(const std::string &answer) { return (answer.compare(0, 2, "OK") == 0); } @@ -284,10 +247,8 @@ void p3I2pBob::threadTick() { int sleepTime = 0; { - RS_STACK_MUTEX(mLock); - std::stringstream ss; - ss << "data_tick mState: " << mState << " mTask: " << mTask << " mBOBState: " << mBOBState << " mPending: " << mPending.size(); - rslog(RsLog::Debug_All, &i2pBobLogInfo, ss.str()); + RS_STACK_MUTEX(mLock); + RS_DBG4("data_tick mState: ", mState, " mTask: ", mTask, " mBOBState: ", mBOBState, " mPending: ", mPending.size()); } sleepTime += stateMachineController(); @@ -326,15 +287,13 @@ int p3I2pBob::stateMachineBOB() if (mBOBState == bsList) { int counter = 0; while (answer.find("OK Listing done") == std::string::npos) { - std::stringstream ss; - ss << "stateMachineBOB status check: read loop, counter: " << counter; - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, ss.str()); + RS_DBG3("stateMachineBOB status check: read loop, counter: ", counter); answer += recv(); counter++; } if (answer.find(mTunnelName) == std::string::npos) { - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineBOB status check: tunnel down!"); + RS_DBG("status check: tunnel down!"); // signal error *((bool *)mProcessing->data) = true; } @@ -346,12 +305,12 @@ int p3I2pBob::stateMachineBOB() switch (mBOBState) { case bsNewkeysN: key = answer.substr(3, answer.length()-3); - mSetting.addr = keyToBase32Addr(key); + mSetting.address.base32 = i2p::keyToBase32Addr(key); IndicateConfigChanged(); break; case bsGetkeys: key = answer.substr(3, answer.length()-3); - mSetting.keys = key; + mSetting.address.privateKey = key; IndicateConfigChanged(); break; default: @@ -374,8 +333,8 @@ int p3I2pBob::stateMachineBOB_locked_failure(const std::string &answer, const bo return sleepFactorDefault; } - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineBOB FAILED to run command '" + currentState.command + "'"); - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineBOB '" + answer + "'"); + RS_DBG("FAILED to run command: ", currentState.command); + RS_DBG("answer: ", answer); mErrorMsg.append("FAILED to run command '" + currentState.command + "'" + '\n'); mErrorMsg.append("reason '" + answer + "'" + '\n'); @@ -422,14 +381,14 @@ int p3I2pBob::stateMachineController() return stateMachineController_locked_idle(); case csDoConnect: if (!connectI2P()) { - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController doConnect: unable to connect"); + RS_DBG("doConnect: unable to connect"); mStateOld = mState; mState = csError; mErrorMsg = "unable to connect to BOB port"; return sleepFactorSlow; } - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController doConnect: connected"); + RS_DBG4("doConnect: connected"); mState = csConnected; break; case csConnected: @@ -437,7 +396,7 @@ int p3I2pBob::stateMachineController() case csWaitForBob: // check connection problems if (mSocket == 0) { - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController waitForBob: conection lost"); + RS_DBG("waitForBob: conection lost"); mStateOld = mState; mState = csError; mErrorMsg = "connection lost to BOB"; @@ -447,21 +406,21 @@ int p3I2pBob::stateMachineController() // check for finished BOB protocol if (mBOBState == bsCleared) { // done - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController waitForBob: mBOBState == bsCleared"); + RS_DBG4("waitForBob: mBOBState == bsCleared"); mState = csDoDisconnect; } break; case csDoDisconnect: if (!disconnectI2P() || mSocket != 0) { // just in case - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController doDisconnect: can't disconnect"); + RS_DBG("doDisconnect: can't disconnect"); mStateOld = mState; mState = csError; mErrorMsg = "unable to disconnect from BOB"; return sleepFactorDefault; } - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController doDisconnect: disconnected"); + RS_DBG4("doDisconnect: disconnected"); mState = csDisconnected; break; case csDisconnected: @@ -487,12 +446,12 @@ int p3I2pBob::stateMachineController_locked_idle() mProcessing = mPending.front(); mPending.pop(); - if (!mSetting.enableBob && ( + if (!mSetting.enable && ( mProcessing->task == autoProxyTask::start || mProcessing->task == autoProxyTask::stop || mProcessing->task == autoProxyTask::proxyStatusCheck)) { // skip since we are not enabled - rslog(RsLog::Debug_Alert, &i2pBobLogInfo, "stateMachineController_locked_idle: disabled -> skipping ticket"); + RS_DBG1("disabled -> skipping ticket"); rsAutoProxyMonitor::taskDone(mProcessing, autoProxyStatus::disabled); mProcessing = NULL; } else { @@ -514,7 +473,7 @@ int p3I2pBob::stateMachineController_locked_idle() mTask = ctRunCheck; break; default: - rslog(RsLog::Debug_Alert, &i2pBobLogInfo, "stateMachineController_locked_idle unknown async task"); + RS_DBG1("unknown async task"); rsAutoProxyMonitor::taskError(mProcessing); mProcessing = NULL; break; @@ -561,29 +520,29 @@ int p3I2pBob::stateMachineController_locked_connected() switch (mTask) { case ctRunSetUp: // when we have a key use it for server tunnel! - if(mSetting.keys.empty()) { - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController_locked_connected: setting mBOBState = setnickC"); + if(mSetting.address.privateKey.empty()) { + RS_DBG4("setting mBOBState = setnickC"); mBOBState = bsSetnickC; } else { - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController_locked_connected: setting mBOBState = setnickS"); + RS_DBG4("setting mBOBState = setnickS"); mBOBState = bsSetnickS; } break; case ctRunShutDown: // shut down existing tunnel - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController_locked_connected: setting mBOBState = getnick"); + RS_DBG4("setting mBOBState = getnick"); mBOBState = bsGetnick; break; case ctRunCheck: - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController_locked_connected: setting mBOBState = list"); + RS_DBG4("setting mBOBState = list"); mBOBState = bsList; break; case ctRunGetKeys: - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController_locked_connected: setting mBOBState = setnickN"); + RS_DBG4("setting mBOBState = setnickN"); mBOBState = bsSetnickN; break; case ctIdle: - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_connected: task is idle. This should not happen!"); + RS_DBG("task is idle. This should not happen!"); break; } @@ -599,7 +558,7 @@ int p3I2pBob::stateMachineController_locked_disconnected() if(errorHappened) { // reset old state mStateOld = csIdel; - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_disconnected: error during process!"); + RS_DBG("error during process!"); } // answer ticket @@ -628,12 +587,12 @@ int p3I2pBob::stateMachineController_locked_disconnected() mTask = mTaskOld; if (!errorHappened) { - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController_locked_disconnected: run check result: ok"); + RS_DBG4("run check result: ok"); break; } // switch to error newState = csError; - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_disconnected: run check result: error"); + RS_DBG("run check result: error"); mErrorMsg = "Connection check failed. Will try to restart tunnel."; break; @@ -656,7 +615,7 @@ int p3I2pBob::stateMachineController_locked_disconnected() mTask = mTaskOld; break; case ctIdle: - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_disconnected: task is idle. This should not happen!"); + RS_DBG("task is idle. This should not happen!"); rsAutoProxyMonitor::taskError(mProcessing); } mProcessing = NULL; @@ -672,14 +631,12 @@ int p3I2pBob::stateMachineController_locked_error() { // wait for bob protocoll if (mBOBState != bsCleared) { - rslog(RsLog::Debug_All, &i2pBobLogInfo, "stateMachineController_locked_error: waiting for BOB"); + RS_DBG4("waiting for BOB"); return sleepFactorFast; } #if 0 - std::stringstream ss; - ss << "stateMachineController_locked_error: mProcessing: " << (mProcessing ? "not null" : "null"); - rslog(RsLog::Debug_All, &i2pBobLogInfo, ss.str()); + RS_DBG4("stateMachineController_locked_error: mProcessing: ", (mProcessing ? "not null" : "null")); #endif // try to finish ticket @@ -687,7 +644,7 @@ int p3I2pBob::stateMachineController_locked_error() switch (mTask) { case ctRunCheck: // connection check failed at some point - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_error: failed to check proxy status (it's likely dead)!"); + RS_DBG("failed to check proxy status (it's likely dead)!"); *((bool *)mProcessing->data) = true; mState = csDoDisconnect; mStateOld = csIdel; @@ -695,7 +652,7 @@ int p3I2pBob::stateMachineController_locked_error() break; case ctRunShutDown: // not a big deal though - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_error: failed to shut down tunnel (it's likely dead though)!"); + RS_DBG("failed to shut down tunnel (it's likely dead though)!"); mState = csDoDisconnect; mStateOld = csIdel; mErrorMsg.clear(); @@ -703,14 +660,14 @@ int p3I2pBob::stateMachineController_locked_error() case ctIdle: // should not happen but we need to deal with it // this will produce some error messages in the log and finish the task (marked as failed) - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_error: task is idle. This should not happen!"); + RS_DBG("task is idle. This should not happen!"); mState = csDoDisconnect; mStateOld = csIdel; mErrorMsg.clear(); break; case ctRunGetKeys: case ctRunSetUp: - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_error: failed to receive key / start up"); + RS_DBG("failed to receive key / start up"); mStateOld = csError; mState = csDoDisconnect; // keep the error message @@ -721,7 +678,7 @@ int p3I2pBob::stateMachineController_locked_error() // periodically retry if (mLastProxyCheck < time(NULL) - (selfCheckPeroid >> 1) && mTask == ctRunSetUp) { - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_error: retrying"); + RS_DBG("retrying"); mLastProxyCheck = time(NULL); mErrorMsg.clear(); @@ -734,7 +691,7 @@ int p3I2pBob::stateMachineController_locked_error() // check for new tickets if (!mPending.empty()) { - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController_locked_error: processing new ticket"); + RS_DBG4("processing new ticket"); // reset and try new task mTask = ctIdle; @@ -765,16 +722,16 @@ RsSerialiser *p3I2pBob::setupSerialiser() bool p3I2pBob::saveList(bool &cleanup, std::list &lst) { - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "saveList"); + RS_DBG4(""); cleanup = true; RsConfigKeyValueSet *vitem = new RsConfigKeyValueSet; RsTlvKeyValue kv; RS_STACK_MUTEX(mLock); - addKVS(vitem, kv, kConfigKeyBOBEnable, mSetting.enableBob ? "TRUE" : "FALSE") - addKVS(vitem, kv, kConfigKeyBOBKey, mSetting.keys) - addKVS(vitem, kv, kConfigKeyBOBAddr, mSetting.addr) + addKVS(vitem, kv, kConfigKeyBOBEnable, mSetting.enable ? "TRUE" : "FALSE") + addKVS(vitem, kv, kConfigKeyBOBKey, mSetting.address.privateKey) + addKVS(vitem, kv, kConfigKeyBOBAddr, mSetting.address.base32) addKVSInt(vitem, kv, kConfigKeyInLength, mSetting.inLength) addKVSInt(vitem, kv, kConfigKeyInQuantity, mSetting.inQuantity) addKVSInt(vitem, kv, kConfigKeyInVariance, mSetting.inVariance) @@ -800,7 +757,7 @@ bool p3I2pBob::saveList(bool &cleanup, std::list &lst) bool p3I2pBob::loadList(std::list &load) { - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "loadList"); + RS_DBG4(""); for(std::list::const_iterator it = load.begin(); it!=load.end(); ++it) { RsConfigKeyValueSet *vitem = dynamic_cast(*it); @@ -808,11 +765,11 @@ bool p3I2pBob::loadList(std::list &load) RS_STACK_MUTEX(mLock); for(std::list::const_iterator kit = vitem->tlvkvs.pairs.begin(); kit != vitem->tlvkvs.pairs.end(); ++kit) { if (kit->key == kConfigKeyBOBEnable) - mSetting.enableBob = kit->value == "TRUE"; + mSetting.enable = kit->value == "TRUE"; else if (kit->key == kConfigKeyBOBKey) - mSetting.keys = kit->value; + mSetting.address.privateKey = kit->value; else if (kit->key == kConfigKeyBOBAddr) - mSetting.addr = kit->value; + mSetting.address.base32 = kit->value; getKVSUInt(kit, kConfigKeyInLength, mSetting.inLength) getKVSUInt(kit, kConfigKeyInQuantity, mSetting.inQuantity) getKVSUInt(kit, kConfigKeyInVariance, mSetting.inVariance) @@ -820,7 +777,7 @@ bool p3I2pBob::loadList(std::list &load) getKVSUInt(kit, kConfigKeyOutQuantity, mSetting.outQuantity) getKVSUInt(kit, kConfigKeyOutVariance, mSetting.outVariance) else - rslog(RsLog::Warning, &i2pBobLogInfo, "loadList unknown key: " + kit->key); + RS_DBG("unknown key: ", kit->key); } } delete vitem; @@ -884,7 +841,7 @@ void p3I2pBob::getStates(bobStates *bs) std::string p3I2pBob::executeCommand(const std::string &command) { - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "executeCommand_locked running '" + command + "'"); + RS_DBG4("running: ", command); std::string copy = command; copy.push_back('\n'); @@ -896,7 +853,7 @@ std::string p3I2pBob::executeCommand(const std::string &command) // receive answer (trailing new line is already removed!) std::string ans = recv(); - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "executeCommand_locked answer '" + ans + "'"); + RS_DBG4("answer: ", ans); return ans; } @@ -906,7 +863,7 @@ bool p3I2pBob::connectI2P() // there is only one thread that touches mSocket - no need for a lock if (mSocket != 0) { - rslog(RsLog::Warning, &i2pBobLogInfo, "connectI2P_locked mSocket != 0"); + RS_DBG("mSocket != 0"); return false; } @@ -914,21 +871,21 @@ bool p3I2pBob::connectI2P() mSocket = unix_socket(PF_INET, SOCK_STREAM, 0); if (mSocket < 0) { - rslog(RsLog::Warning, &i2pBobLogInfo, "connectI2P_locked Failed to open socket! Socket Error: " + socket_errorType(errno)); + RS_DBG("Failed to open socket! Socket Error: ", socket_errorType(errno)); return false; } // connect int err = unix_connect(mSocket, mI2PProxyAddr); if (err != 0) { - rslog(RsLog::Warning, &i2pBobLogInfo, "connectI2P_locked Failed to connect to BOB! Socket Error: " + socket_errorType(errno)); + RS_DBG("Failed to connect to BOB! Socket Error: ", socket_errorType(errno)); return false; } // receive hello msg recv(); - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "connectI2P_locked done"); + RS_DBG4("done"); return true; } @@ -937,17 +894,17 @@ bool p3I2pBob::disconnectI2P() // there is only one thread that touches mSocket - no need for a lock if (mSocket == 0) { - rslog(RsLog::Warning, &i2pBobLogInfo, "disconnectI2P_locked mSocket == 0"); + RS_DBG("mSocket == 0"); return true; } int err = unix_close(mSocket); if (err != 0) { - rslog(RsLog::Warning, &i2pBobLogInfo, "disconnectI2P_locked Failed to close socket! Socket Error: " + socket_errorType(errno)); + RS_DBG("Failed to close socket! Socket Error: ", socket_errorType(errno)); return false; } - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "disconnectI2P_locked done"); + RS_DBG4("done"); mSocket = 0; return true; } @@ -968,7 +925,7 @@ std::string toString(const std::string &a, const int8_t b) { void p3I2pBob::finalizeSettings_locked() { - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "finalizeSettings_locked"); + RS_DBG4(""); sockaddr_storage_clear(mI2PProxyAddr); // get i2p proxy addr @@ -979,8 +936,8 @@ void p3I2pBob::finalizeSettings_locked() sockaddr_storage_setipv4(mI2PProxyAddr, (sockaddr_in*)&proxy); sockaddr_storage_setport(mI2PProxyAddr, 2827); - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "finalizeSettings_locked using " + sockaddr_storage_tostring(mI2PProxyAddr)); - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "finalizeSettings_locked using " + mSetting.addr); + RS_DBG4("using ", mI2PProxyAddr); + RS_DBG4("using ", mSetting.address.base32); peerState ps; mPeerMgr->getOwnNetStatus(ps); @@ -988,21 +945,17 @@ void p3I2pBob::finalizeSettings_locked() // setup commands // new lines are appended later! - // generate random suffix for name - // RSRandom::random_alphaNumericString can return very weird looking strings like: ,,@z+M - // use base32 instead - size_t len = 5; // 5 characters = 8 base32 symbols - std::vector tmp(len); - RSRandom::random_bytes(tmp.data(), len); - const std::string location = Radix32::encode(tmp.data(), len); - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "finalizeSettings_locked using suffix " + location); + // generate 8 characater long random suffix for name + constexpr size_t len = 8; + const std::string location = RsRandom::alphaNumeric(len); + RS_DBG4("using suffix ", location); mTunnelName = "RetroShare-" + location; const std::string setnick = "setnick RetroShare-" + location; const std::string getnick = "getnick RetroShare-" + location; const std::string newkeys = "newkeys"; const std::string getkeys = "getkeys"; - const std::string setkeys = "setkeys " + mSetting.keys; + const std::string setkeys = "setkeys " + mSetting.address.privateKey; const std::string inhost = "inhost " + sockaddr_storage_iptostring(proxy); const std::string inport = toString("inport ", sockaddr_storage_port(proxy)); const std::string outhost = "outhost " + sockaddr_storage_iptostring(ps.localaddr); @@ -1063,7 +1016,7 @@ void p3I2pBob::finalizeSettings_locked() void p3I2pBob::updateSettings_locked() { - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "updateSettings_locked"); + RS_DBG4(""); sockaddr_storage proxy; mPeerMgr->getProxyServerAddress(RS_HIDDEN_TYPE_I2P, proxy); @@ -1071,7 +1024,7 @@ void p3I2pBob::updateSettings_locked() peerState ps; mPeerMgr->getOwnNetStatus(ps); - const std::string setkeys = "setkeys " + mSetting.keys; + const std::string setkeys = "setkeys " + mSetting.address.privateKey; const std::string inhost = "inhost " + sockaddr_storage_iptostring(proxy); const std::string inport = toString("inport ", sockaddr_storage_port(proxy)); const std::string outhost = "outhost " + sockaddr_storage_iptostring(ps.localaddr); @@ -1103,35 +1056,62 @@ void p3I2pBob::updateSettings_locked() std::string p3I2pBob::recv() { + // BOB works line based + // -> \n indicates and of the line + + constexpr uint16_t bufferSize = 128; + char buffer[bufferSize]; + std::string ans; - ssize_t length; - const uint16_t bufferSize = 128; - std::vector buffer(bufferSize); + uint16_t retry = 10; do { - doSleep(sleepTimeRecv); + memset(buffer, 0, bufferSize); - // there is only one thread that touches mSocket - no need for a lock - length = ::recv(mSocket, buffer.data(), buffer.size(), 0); - if (length < 0) + // peek at data + auto length = ::recv(mSocket, buffer, bufferSize, MSG_PEEK); + if (length <= 0) { + if (length < 0) { + // error + perror(__PRETTY_FUNCTION__); + } + retry--; + doSleep(sleepTimeRecv); continue; + } - ans.append(buffer.begin(), buffer.end()); + // at least one byte was read - // clean received string - ans.erase(std::remove(ans.begin(), ans.end(), '\0'), ans.end()); - ans.erase(std::remove(ans.begin(), ans.end(), '\n'), ans.end()); + // search for new line + auto bufferStr = std::string(buffer); + size_t pos = bufferStr.find('\n'); -#if 0 - std::stringstream ss; - ss << "recv length: " << length << " (bufferSize: " << bufferSize << ") ans: " << ans.length(); - rslog(RsLog::Debug_All, &i2pBobLogInfo, ss.str()); -#endif + if (pos == std::string::npos) { + // no new line found -> more to read - // clear and resize buffer again - buffer.clear(); - buffer.resize(bufferSize); - } while(length == bufferSize || ans.size() < 4); + // sanity check + if (length != bufferSize) { + // expectation: a full buffer was peeked) + RS_DBG1("peeked less than bufferSize but also didn't found a new line character"); + } + // this should never happen + assert(length <= bufferSize); + } else { + // new line found -> end of message + + // calculate how much there is to read, read the \n, too! + length = pos + 1; + + // end loop + retry = 0; + } + + // now read for real + memset(buffer, 0, bufferSize); + length = ::recv(mSocket, buffer, length, 0); + bufferStr = std::string(buffer); + ans.append(bufferStr); + } while(retry > 0); return ans; } diff --git a/libretroshare/src/services/autoproxy/p3i2pbob.h b/libretroshare/src/services/autoproxy/p3i2pbob.h index e6ef60bae..29bcbcb61 100644 --- a/libretroshare/src/services/autoproxy/p3i2pbob.h +++ b/libretroshare/src/services/autoproxy/p3i2pbob.h @@ -30,9 +30,10 @@ #include #endif +#include "pqi/p3cfgmgr.h" #include "services/autoproxy/rsautoproxymonitor.h" #include "util/rsthreads.h" -#include "pqi/p3cfgmgr.h" +#include "util/i2pcommon.h" /* * This class implements I2P BOB (BASIC OPEN BRIDGE) communication to allow RS @@ -49,7 +50,7 @@ * * Note 3: * BOB needs a unique name as an ID for each tunnel. - * We use 'RetroShare-' + 8 base32 characters. + * We use 'RetroShare-' + 8 random base32 characters. * * Design: * The service uses three state machines to manage its task: @@ -72,7 +73,7 @@ * mCommands[bobState::quit] = {quit, bobState::cleared}; * * stateMachineController: - * This state machone manages the high level tasks. + * This state machine manages the high level tasks. * It is controlled by mState and mTask. * * mTast: @@ -162,19 +163,7 @@ struct bobStateInfo { bobState nextState; }; -struct bobSettings { - bool enableBob; ///< This field is used by the pqi subsystem to determinine whether SOCKS proxy or BOB is used for I2P connections - std::string keys; ///< (optional) server keys - std::string addr; ///< (optional) hidden service addr. in base32 form - - int8_t inLength; - int8_t inQuantity; - int8_t inVariance; - - int8_t outLength; - int8_t outQuantity; - int8_t outVariance; -}; +struct bobSettings : i2p::settings {}; /// /// \brief The bobStates struct @@ -203,8 +192,6 @@ public: void processTaskAsync(taskTicket *ticket); void processTaskSync(taskTicket *ticket); - static std::string keyToBase32Addr(const std::string &key); - void threadTick() override; /// @see RsTickingThread private: diff --git a/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc b/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc index d198bd207..d58c871e3 100644 --- a/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc +++ b/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc @@ -22,6 +22,7 @@ #include "rsautoproxymonitor.h" #include /* for usleep() */ +#include "util/rsdebug.h" #include "util/rstime.h" rsAutoProxyMonitor *rsAutoProxyMonitor::mInstance = NULL; @@ -42,8 +43,10 @@ rsAutoProxyMonitor *rsAutoProxyMonitor::instance() void rsAutoProxyMonitor::addProxy(autoProxyType::autoProxyType_enum type, autoProxyService *service) { RS_STACK_MUTEX(mLock); - if (mProxies.find(type) != mProxies.end()) - std::cerr << "sAutoProxyMonitor::addProxy type " << type << " already added - OVERWRITING" << std::endl; + if (mProxies.find(type) != mProxies.end()) { + RS_ERR("type ", type, " already added - OVERWRITING"); + print_stacktrace(); + } mProxies[type] = service; } @@ -117,7 +120,7 @@ void rsAutoProxyMonitor::stopAllRSShutdown() do { rstime::rs_usleep(1000 * 1000); RS_STACK_MUTEX(mLock); - std::cout << "(II) waiting for auto proxy service(s) to shut down " << t << "/" << timeout << " (remaining: " << mProxies.size() << ")" << std::endl; + RS_DBG("waiting for auto proxy service(s) to shut down ", t, "/", timeout, " (remaining: ", mProxies.size(), ")"); if (mProxies.empty()) break; t++; @@ -146,13 +149,16 @@ void rsAutoProxyMonitor::task(taskTicket *ticket) { // sanity checks if (!ticket->async && ticket->types.size() > 1) { - std::cerr << "(WW) rsAutoProxyMonitor::task synchronous call to multiple services. This can cause problems!" << std::endl; + RS_ERR("synchronous call to multiple services. This can cause problems!"); + print_stacktrace(); } if (ticket->async && !ticket->cb && ticket->data) { - std::cerr << "(WW) rsAutoProxyMonitor::task asynchronous call with data but no callback. This will likely causes memory leak!" << std::endl; + RS_ERR("asynchronous call with data but no callback. This will likely causes memory leak!"); + print_stacktrace(); } if (ticket->types.size() > 1 && ticket->data) { - std::cerr << "(WW) rsAutoProxyMonitor::task call with data to multiple services. This will likely causes memory leak!" << std::endl; + RS_ERR("call with data to multiple services. This will likely causes memory leak!"); + print_stacktrace(); } std::vector::const_iterator it; @@ -168,7 +174,11 @@ void rsAutoProxyMonitor::task(taskTicket *ticket) *tt = *ticket; tt->types.clear(); tt->types.push_back(*it); - s->processTaskAsync(tt); + + // it's async! + RsThread::async([s, tt] { + s->processTaskAsync(tt); + }); } else { s->processTaskSync(ticket); } @@ -187,7 +197,8 @@ void rsAutoProxyMonitor::taskAsync(std::vector if (isAsyncTask(task)) { // Usually the services will reject this ticket. // Just print a warning - maybe there is some special case where this is a good idea. - std::cerr << "(WW) rsAutoProxyMonitor::taskSync called with an asynchronous task!" << std::endl; + RS_ERR("called with an asynchronous task!"); + print_stacktrace(); } taskTicket *tt = getTicket(); @@ -244,7 +256,8 @@ void rsAutoProxyMonitor::taskDone(taskTicket *t, autoProxyStatus::autoProxyStatu t->cb->taskFinished(t); if (t != NULL) { // callack did not clean up properly - std::cerr << "(WW) rsAutoProxyMonitor::taskFinish callback did not clean up!" << std::endl; + RS_ERR("callback did not clean up!"); + print_stacktrace(); cleanUp = true; } } else if (t->async){ @@ -252,12 +265,13 @@ void rsAutoProxyMonitor::taskDone(taskTicket *t, autoProxyStatus::autoProxyStatu // we must take care of deleting cleanUp = true; if(t->data) - std::cerr << "(WW) rsAutoProxyMonitor::taskFinish async call with data attached but no callback set!" << std::endl; + RS_ERR("async call with data attached but no callback set!"); } if (cleanUp) { if (t->data) { - std::cerr << "(WW) rsAutoProxyMonitor::taskFinish will try to delete void pointer!" << std::endl; + RS_ERR("will try to delete void pointer!"); + print_stacktrace(); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdelete-incomplete" delete t->data; @@ -290,7 +304,8 @@ void rsAutoProxyMonitor::taskFinished(taskTicket *&ticket) // clean up if (ticket->data) { - std::cerr << "rsAutoProxyMonitor::taskFinished data set. Will try to delete void pointer" << std::endl; + RS_ERR(" data set. Will try to delete void pointer"); + print_stacktrace(); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdelete-incomplete" delete ticket->data; @@ -308,7 +323,7 @@ autoProxyService *rsAutoProxyMonitor::lookUpService(autoProxyType::autoProxyType if ((itService = mProxies.find(t)) != mProxies.end()) { return itService->second; } - std::cerr << "sAutoProxyMonitor::lookUpService no service for type " << t << " found!" << std::endl; + RS_DBG("no service for type ", t, " found!"); return NULL; } diff --git a/libretroshare/src/services/p3gxschannels.cc b/libretroshare/src/services/p3gxschannels.cc index 2a63b3cdb..ab5990bae 100644 --- a/libretroshare/src/services/p3gxschannels.cc +++ b/libretroshare/src/services/p3gxschannels.cc @@ -80,12 +80,15 @@ p3GxsChannels::p3GxsChannels( RS_SERVICE_GXS_TYPE_CHANNELS, gixs, channelsAuthenPolicy() ), RsGxsChannels(static_cast(*this)), GxsTokenQueue(this), mSubscribedGroupsMutex("GXS channels subscribed groups cache"), - mKnownChannelsMutex("GXS channels known channels timestamp cache"), + mKnownChannelsMutex("GXS channels known channels timestamp cache") +#ifdef TO_REMOVE mSearchCallbacksMapMutex("GXS channels search callbacks map"), mDistantChannelsCallbacksMapMutex("GXS channels distant channels callbacks map") +#endif { // For Dummy Msgs. mGenActive = false; + mLastDistantSearchNotificationTS = 0; mCommentService = new p3GxsCommentService(this, RS_SERVICE_GXS_TYPE_CHANNELS); RsTickEvent::schedule_in(CHANNEL_PROCESS, 0); @@ -136,14 +139,14 @@ uint32_t p3GxsChannels::channelsAuthenPolicy() static const uint32_t GXS_CHANNELS_CONFIG_MAX_TIME_NOTIFY_STORAGE = 86400*30*2 ; // ignore notifications for 2 months static const uint8_t GXS_CHANNELS_CONFIG_SUBTYPE_NOTIFY_RECORD = 0x01 ; -struct RsGxsGroupNotifyRecordsItem: public RsItem +struct RsGxsChannelNotifyRecordsItem: public RsItem { - RsGxsGroupNotifyRecordsItem() + RsGxsChannelNotifyRecordsItem() : RsItem(RS_PKT_VERSION_SERVICE,RS_SERVICE_GXS_TYPE_CHANNELS_CONFIG,GXS_CHANNELS_CONFIG_SUBTYPE_NOTIFY_RECORD) {} - virtual ~RsGxsGroupNotifyRecordsItem() {} + virtual ~RsGxsChannelNotifyRecordsItem() {} void serial_process( RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx ) @@ -167,7 +170,7 @@ public: switch(item_sub_id) { - case GXS_CHANNELS_CONFIG_SUBTYPE_NOTIFY_RECORD: return new RsGxsGroupNotifyRecordsItem(); + case GXS_CHANNELS_CONFIG_SUBTYPE_NOTIFY_RECORD: return new RsGxsChannelNotifyRecordsItem(); default: return NULL; } @@ -178,7 +181,7 @@ bool p3GxsChannels::saveList(bool &cleanup, std::list&saveList) { cleanup = true ; - RsGxsGroupNotifyRecordsItem *item = new RsGxsGroupNotifyRecordsItem ; + RsGxsChannelNotifyRecordsItem *item = new RsGxsChannelNotifyRecordsItem ; { RS_STACK_MUTEX(mKnownChannelsMutex); @@ -198,7 +201,7 @@ bool p3GxsChannels::loadList(std::list& loadList) rstime_t now = time(NULL); - RsGxsGroupNotifyRecordsItem *fnr = dynamic_cast(item) ; + RsGxsChannelNotifyRecordsItem *fnr = dynamic_cast(item) ; if(fnr) { @@ -206,7 +209,7 @@ bool p3GxsChannels::loadList(std::list& loadList) mKnownChannels.clear(); for(auto it(fnr->records.begin());it!=fnr->records.end();++it) - if( it->second + GXS_CHANNELS_CONFIG_MAX_TIME_NOTIFY_STORAGE < now) + if( now < it->second + GXS_CHANNELS_CONFIG_MAX_TIME_NOTIFY_STORAGE) mKnownChannels.insert(*it) ; } @@ -234,12 +237,11 @@ RsGenExchange::ServiceCreate_Return p3GxsChannels::service_CreateGroup(RsGxsGrpI void p3GxsChannels::notifyChanges(std::vector &changes) { -#ifdef GXSCHANNELS_DEBUG - std::cerr << "p3GxsChannels::notifyChanges() : " << changes.size() << "changes to notify" << std::endl; +#ifdef GXSCHANNEL_DEBUG + RsDbg() << " Processing " << changes.size() << " channel changes..." << std::endl; #endif - - /* iterate through and grab any new messages */ - std::list unprocessedGroups; + /* iterate through and grab any new messages */ + std::set unprocessedGroups; std::vector::iterator it; for(it = changes.begin(); it != changes.end(); ++it) @@ -253,16 +255,12 @@ void p3GxsChannels::notifyChanges(std::vector &changes) /* message received */ if (rsEvents) { - std::map > &msgChangeMap = msgChange->msgChangeMap; - for (auto mit = msgChangeMap.begin(); mit != msgChangeMap.end(); ++mit) - for (auto mit1 = mit->second.begin(); mit1 != mit->second.end(); ++mit1) - { - auto ev = std::make_shared(); - ev->mChannelMsgId = *mit1; - ev->mChannelGroupId = mit->first; - ev->mChannelEventCode = RsChannelEventCode::NEW_MESSAGE; - rsEvents->postEvent(ev); - } + auto ev = std::make_shared(); + + ev->mChannelMsgId = msgChange->mMsgId; + ev->mChannelGroupId = msgChange->mGroupId; + ev->mChannelEventCode = RsChannelEventCode::NEW_MESSAGE; + rsEvents->postEvent(ev); } } @@ -273,25 +271,21 @@ void p3GxsChannels::notifyChanges(std::vector &changes) std::cerr << std::endl; #endif - std::map > &msgChangeMap = msgChange->msgChangeMap; - for(auto mit = msgChangeMap.begin(); mit != msgChangeMap.end(); ++mit) +#ifdef GXSCHANNELS_DEBUG + std::cerr << "p3GxsChannels::notifyChanges() Msgs for Group: " << mit->first; + std::cerr << std::endl; +#endif { #ifdef GXSCHANNELS_DEBUG - std::cerr << "p3GxsChannels::notifyChanges() Msgs for Group: " << mit->first; + std::cerr << "p3GxsChannels::notifyChanges() AutoDownload for Group: " << mit->first; std::cerr << std::endl; #endif - bool enabled = false; - if (autoDownloadEnabled(mit->first, enabled) && enabled) - { -#ifdef GXSCHANNELS_DEBUG - std::cerr << "p3GxsChannels::notifyChanges() AutoDownload for Group: " << mit->first; - std::cerr << std::endl; -#endif - /* problem is most of these will be comments and votes, - * should make it occasional - every 5mins / 10minutes TODO */ - unprocessedGroups.push_back(mit->first); - } + /* problem is most of these will be comments and votes, should make it occasional - every 5mins / 10minutes TODO */ + // We do not call if(autoDownLoadEnabled()) here, because it would be too costly when + // many msgs are received from the same group. We back the groupIds and then request one by one. + + unprocessedGroups.insert(msgChange->mGroupId); } } } @@ -300,101 +294,124 @@ void p3GxsChannels::notifyChanges(std::vector &changes) if (grpChange && rsEvents) { +#ifdef GXSCHANNEL_DEBUG + RsDbg() << " Grp Change Event or type " << grpChange->getType() << ":" << std::endl; +#endif + switch (grpChange->getType()) { - default: case RsGxsNotify::TYPE_PROCESSED: // happens when the group is subscribed { - std::list &grpList = grpChange->mGrpIdList; - std::list::iterator git; - for (git = grpList.begin(); git != grpList.end(); ++git) - { - auto ev = std::make_shared(); - ev->mChannelGroupId = *git; - ev->mChannelEventCode = RsChannelEventCode::SUBSCRIBE_STATUS_CHANGED; - rsEvents->postEvent(ev); - } - + auto ev = std::make_shared(); + ev->mChannelGroupId = grpChange->mGroupId; + ev->mChannelEventCode = RsChannelEventCode::SUBSCRIBE_STATUS_CHANGED; + rsEvents->postEvent(ev); } break; + case RsGxsNotify::TYPE_STATISTICS_CHANGED: + { + auto ev = std::make_shared(); + ev->mChannelGroupId = grpChange->mGroupId; + ev->mChannelEventCode = RsChannelEventCode::STATISTICS_CHANGED; + rsEvents->postEvent(ev); + } + break; + case RsGxsNotify::TYPE_PUBLISHED: case RsGxsNotify::TYPE_RECEIVED_NEW: { /* group received */ - std::list &grpList = grpChange->mGrpIdList; - std::list::iterator git; - RS_STACK_MUTEX(mKnownChannelsMutex); - for (git = grpList.begin(); git != grpList.end(); ++git) - { - if(mKnownChannels.find(*git) == mKnownChannels.end()) - { - mKnownChannels.insert(std::make_pair(*git,time(NULL))) ; - IndicateConfigChanged(); - auto ev = std::make_shared(); - ev->mChannelGroupId = *git; - ev->mChannelEventCode = RsChannelEventCode::NEW_CHANNEL; - rsEvents->postEvent(ev); - } - else - std::cerr << "(II) Not notifying already known channel " << *git << std::endl; - } - break; + RS_STACK_MUTEX(mKnownChannelsMutex); + +#ifdef GXSCHANNEL_DEBUG + RsDbg() << " Type = Published/New " << std::endl; +#endif + if(mKnownChannels.find(grpChange->mGroupId) == mKnownChannels.end()) + { +#ifdef GXSCHANNEL_DEBUG + RsDbg() << " Status: unknown. Sending notification event." << std::endl; +#endif + + mKnownChannels.insert(std::make_pair(grpChange->mGroupId,time(NULL))) ; + IndicateConfigChanged(); + + auto ev = std::make_shared(); + ev->mChannelGroupId = grpChange->mGroupId; + ev->mChannelEventCode = RsChannelEventCode::NEW_CHANNEL; + rsEvents->postEvent(ev); + } +#ifdef GXSCHANNEL_DEBUG + else + RsDbg() << " Not notifying already known channel " << grpChange->mGroupId << std::endl; +#endif } + break; case RsGxsNotify::TYPE_RECEIVED_PUBLISHKEY: { /* group received */ - std::list &grpList = grpChange->mGrpIdList; - std::list::iterator git; - for (git = grpList.begin(); git != grpList.end(); ++git) - { - auto ev = std::make_shared(); - ev->mChannelGroupId = *git; - ev->mChannelEventCode = RsChannelEventCode::RECEIVED_PUBLISH_KEY; + auto ev = std::make_shared(); + ev->mChannelGroupId = grpChange->mGroupId; + ev->mChannelEventCode = RsChannelEventCode::RECEIVED_PUBLISH_KEY; - rsEvents->postEvent(ev); - } + rsEvents->postEvent(ev); + } + break; + + default: + RsErr() << " Got a GXS event of type " << grpChange->getType() << " Currently not handled." << std::endl; break; } - } + } - RsGxsDistantSearchResultChange *dsrChange = dynamic_cast(*it); - - if(dsrChange && rsEvents) - { - auto ev = std::make_shared(); - ev->mChannelGroupId = dsrChange->mGroupId; - ev->mChannelEventCode = RsChannelEventCode::RECEIVED_DISTANT_SEARCH_RESULT; - ev->mDistantSearchRequestId = dsrChange->mRequestId; - - rsEvents->postEvent(ev); - } - /* shouldn't need to worry about groups - as they need to be subscribed to */ delete *it; } - if(!unprocessedGroups.empty()) - request_SpecificSubscribedGroups(unprocessedGroups); + std::list grps; + for(auto& grp_id:unprocessedGroups) + { + bool enabled = false; + if (autoDownloadEnabled(grp_id, enabled) && enabled) // costly call, that's why it's packed down here. + grps.push_back(grp_id); + } + + if(!grps.empty()) + request_SpecificSubscribedGroups(grps); } void p3GxsChannels::service_tick() { static rstime_t last_dummy_tick = 0; + rstime_t now = time(NULL); if (time(NULL) > last_dummy_tick + 5) { dummy_tick(); - last_dummy_tick = time(NULL); + last_dummy_tick = now; } RsTickEvent::tick_events(); GxsTokenQueue::checkRequests(); mCommentService->comment_tick(); + + // Notify distant search results, not more than once per sec. Normally we should + // rather send one item for all, but that needs another class type + + if(now > mLastDistantSearchNotificationTS+2 && !mSearchResultsToNotify.empty()) + { + auto ev = std::make_shared(); + ev->mSearchResultsMap = mSearchResultsToNotify; + + mLastDistantSearchNotificationTS = now; + mSearchResultsToNotify.clear(); + + rsEvents->postEvent(ev); + } } bool p3GxsChannels::getGroupData(const uint32_t &token, std::vector &groups) @@ -453,22 +470,23 @@ bool p3GxsChannels::groupShareKeys( bool p3GxsChannels::getPostData( const uint32_t &token, std::vector &msgs, - std::vector &cmts ) + std::vector &cmts, + std::vector &vots) { #ifdef GXSCHANNELS_DEBUG - std::cerr << __PRETTY_FUNCTION__ << std::cerr << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << std::endl; #endif GxsMsgDataMap msgData; if(!RsGenExchange::getMsgData(token, msgData)) { - std::cerr << __PRETTY_FUNCTION__ <<" ERROR in request" << std::endl; + RsErr() << __PRETTY_FUNCTION__ << " ERROR in request" << std::endl; return false; } GxsMsgDataMap::iterator mit = msgData.begin(); - for(; mit != msgData.end(); ++mit) + for(; mit != msgData.end(); ++mit) { std::vector& msgItems = mit->second; std::vector::iterator vit = msgItems.begin(); @@ -496,7 +514,7 @@ bool p3GxsChannels::getPostData( cmt = cmtItem->mMsg; cmt.mMeta = mi->meta; #ifdef GXSCOMMENT_DEBUG - std::cerr << "p3GxsChannels::getPostData Found Comment:" << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " Found Comment:" << std::endl; cmt.print(std::cerr," ", "cmt"); #endif cmts.push_back(cmt); @@ -504,18 +522,33 @@ bool p3GxsChannels::getPostData( } else { - RsGxsMsgItem* msg = (*vit); - //const uint16_t RS_SERVICE_GXS_TYPE_CHANNELS = 0x0217; - //const uint8_t RS_PKT_SUBTYPE_GXSCHANNEL_POST_ITEM = 0x03; - //const uint8_t RS_PKT_SUBTYPE_GXSCOMMENT_COMMENT_ITEM = 0xf1; - std::cerr << __PRETTY_FUNCTION__ - << " Not a GxsChannelPostItem neither a " - << "RsGxsCommentItem PacketService=" << std::hex - << (int)msg->PacketService() << std::dec - << " PacketSubType=" << std::hex - << (int)msg->PacketSubType() << std::dec - << " , deleting!" << std::endl; - delete *vit; + RsGxsVoteItem* votItem = + dynamic_cast(*vit); + if(votItem) + { + RsGxsVote vot; + RsGxsMsgItem *mi = (*vit); + vot = votItem->mMsg; + vot.mMeta = mi->meta; + vots.push_back(vot); + delete votItem; + } + else + { + RsGxsMsgItem* msg = (*vit); + //const uint16_t RS_SERVICE_GXS_TYPE_CHANNELS = 0x0217; + //const uint8_t RS_PKT_SUBTYPE_GXSCHANNEL_POST_ITEM = 0x03; + //const uint8_t RS_PKT_SUBTYPE_GXSCOMMENT_COMMENT_ITEM = 0xf1; + //const uint8_t RS_PKT_SUBTYPE_GXSCOMMENT_VOTE_ITEM = 0xf2; + RsErr() << __PRETTY_FUNCTION__ + << " Not a GxsChannelPostItem neither a " + << "RsGxsCommentItem neither a RsGxsVoteItem" + << " PacketService=" << std::hex << (int)msg->PacketService() << std::dec + << " PacketSubType=" << std::hex << (int)msg->PacketSubType() << std::dec + << " type name =" << typeid(*msg).name() + << " , deleting!" << std::endl; + delete *vit; + } } } } @@ -524,11 +557,19 @@ bool p3GxsChannels::getPostData( return true; } +bool p3GxsChannels::getPostData( + const uint32_t& token, std::vector& posts, std::vector& cmts ) +{ + std::vector vots; + return getPostData(token, posts, cmts, vots); +} + bool p3GxsChannels::getPostData( const uint32_t& token, std::vector& posts ) { std::vector cmts; - return getPostData(token, posts, cmts); + std::vector vots; + return getPostData(token, posts, cmts, vots); } //Not currently used @@ -706,8 +747,7 @@ void p3GxsChannels::request_AllSubscribedGroups() } -void p3GxsChannels::request_SpecificSubscribedGroups( - const std::list &groups ) +void p3GxsChannels::request_SpecificSubscribedGroups( const std::list &groups ) { #ifdef GXSCHANNELS_DEBUG std::cerr << "p3GxsChannels::request_SpecificSubscribedGroups()"; @@ -720,8 +760,7 @@ void p3GxsChannels::request_SpecificSubscribedGroups( uint32_t token = 0; - if(!RsGenExchange::getTokenService()-> - requestGroupInfo(token, ansType, opts, groups)) + if(!RsGenExchange::getTokenService()-> requestGroupInfo(token, ansType, opts, groups)) { std::cerr << __PRETTY_FUNCTION__ << " Failed requesting groups info!" << std::endl; @@ -1033,18 +1072,44 @@ bool p3GxsChannels::getChannelsSummaries( return getGroupSummary(token, channels); } -bool p3GxsChannels::getChannelsInfo( - const std::list& chanIds, - std::vector& channelsInfo ) +bool p3GxsChannels::getChannelsInfo( const std::list& chanIds, std::vector& channelsInfo ) { uint32_t token; RsTokReqOptions opts; opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; - if( !requestGroupInfo(token, opts, chanIds) - || waitToken(token) != RsTokenService::COMPLETE ) return false; + + if(chanIds.empty()) + { + if( !requestGroupInfo(token, opts) || waitToken(token) != RsTokenService::COMPLETE ) + return false; + } + else + { + if( !requestGroupInfo(token, opts, chanIds) || waitToken(token) != RsTokenService::COMPLETE ) + return false; + } + return getGroupData(token, channelsInfo) && !channelsInfo.empty(); } +bool p3GxsChannels::getChannelStatistics(const RsGxsGroupId& channelId,GxsGroupStatistic& stat) +{ + uint32_t token; + if(!RsGxsIfaceHelper::requestGroupStatistic(token, channelId) || waitToken(token) != RsTokenService::COMPLETE) + return false; + + return RsGenExchange::getGroupStatistic(token,stat); +} + +bool p3GxsChannels::getChannelServiceStatistics(GxsServiceStatistic& stat) +{ + uint32_t token; + if(!RsGxsIfaceHelper::requestServiceStatistic(token) || waitToken(token) != RsTokenService::COMPLETE) + return false; + + return RsGenExchange::getServiceStatistic(token,stat); +} + bool p3GxsChannels::getContentSummaries( const RsGxsGroupId& channelId, std::vector& summaries ) { @@ -1066,22 +1131,57 @@ bool p3GxsChannels::getContentSummaries( return res; } +bool p3GxsChannels::getChannelAllContent( const RsGxsGroupId& channelId, + std::vector& posts, + std::vector& comments, + std::vector& votes ) +{ + uint32_t token; + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; + + if( !requestMsgInfo(token, opts,std::list({channelId})) || waitToken(token) != RsTokenService::COMPLETE ) + return false; + + return getPostData(token, posts, comments,votes); +} + bool p3GxsChannels::getChannelContent( const RsGxsGroupId& channelId, - const std::set& contentsIds, - std::vector& posts, - std::vector& comments ) + const std::set& contentIds, + std::vector& posts, + std::vector& comments, + std::vector& votes ) { uint32_t token; RsTokReqOptions opts; opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; GxsMsgReq msgIds; - msgIds[channelId] = contentsIds; + msgIds[channelId] = contentIds; - if( !requestMsgInfo(token, opts, msgIds) || - waitToken(token) != RsTokenService::COMPLETE ) return false; + if( !requestMsgInfo(token, opts, msgIds) || waitToken(token) != RsTokenService::COMPLETE ) + return false; - return getPostData(token, posts, comments); + return getPostData(token, posts, comments, votes); +} + +bool p3GxsChannels::getChannelComments(const RsGxsGroupId &channelId, + const std::set &contentIds, + std::vector &comments) +{ + std::vector msgIds; + for (auto& msg:contentIds) + msgIds.push_back(RsGxsGrpMsgIdPair(channelId,msg)); + + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_MSG_RELATED_DATA; + opts.mOptions = RS_TOKREQOPT_MSG_THREAD | RS_TOKREQOPT_MSG_LATEST; + + uint32_t token; + if( !requestMsgRelatedInfo(token, opts, msgIds) || waitToken(token) != RsTokenService::COMPLETE ) + return false; + + return getRelatedComments(token,comments); } bool p3GxsChannels::createChannelV2( @@ -1237,8 +1337,9 @@ bool p3GxsChannels::createVoteV2( std::set s({commentId}); std::vector posts; std::vector comments; + std::vector votes; - if(!getChannelContent(channelId, s, posts, comments)) + if(!getChannelContent(channelId, s, posts, comments, votes)) { errorMessage = "You cannot vote on comment " + commentId.toStdString() + " of channel with Id " @@ -1398,8 +1499,9 @@ bool p3GxsChannels::createPostV2( std::set s({origPostId}); std::vector posts; std::vector comments; + std::vector votes; - if(!getChannelContent(channelId,s,posts,comments)) + if(!getChannelContent(channelId,s,posts,comments,votes)) { errorMessage = "You cannot edit post " + origPostId.toStdString() + " of channel with Id " + channelId.toStdString() @@ -1469,9 +1571,11 @@ bool p3GxsChannels::createCommentV2( std::vector posts; std::vector comments; + std::vector votes; if(!getChannelContent( // does the post thread exist? - channelId,std::set({threadId}), posts, comments )) + channelId, std::set({threadId}), + posts, comments, votes) ) return failure( "You cannot comment post " + threadId.toStdString() + " of channel with Id " + channelId.toStdString() + ": this post does not exists locally!" ); @@ -1483,19 +1587,18 @@ bool p3GxsChannels::createCommentV2( ": supplied threadId is not a thread, or parentMsgId is" " not a comment!"); - if(!getChannelContent( // does the post thread exist? + if(!getChannelContent( // does the post parent exist? channelId, std::set({parentId}), - posts, comments )) + posts, comments, votes) ) return failure( "You cannot comment post " + parentId.toStdString() + ": supplied parent doesn't exists locally!" ); if(!origCommentId.isNull()) { std::set s({origCommentId}); - std::vector posts; - std::vector comments; + std::vector cmts; - if( !getChannelContent(channelId, s, posts, comments) || + if( !getChannelContent(channelId, s, posts, cmts, votes) || comments.size() != 1 ) return failure( "You cannot edit comment " + origCommentId.toStdString() + @@ -2125,7 +2228,9 @@ void p3GxsChannels::dummy_tick() } +#ifdef TO_REMOVE cleanTimedOutCallbacks(); +#endif } @@ -2304,18 +2409,18 @@ bool p3GxsChannels::clearDistantSearchResults(TurtleRequestId req) { return netService()->clearDistantSearchResults(req); } -bool p3GxsChannels::retrieveDistantSearchResults(TurtleRequestId req,std::map& results) +bool p3GxsChannels::retrieveDistantSearchResults(TurtleRequestId req,std::map& results) { return netService()->retrieveDistantSearchResults(req,results); } -bool p3GxsChannels::retrieveDistantGroup(const RsGxsGroupId& group_id,RsGxsChannelGroup& distant_group) +bool p3GxsChannels::getDistantSearchResultGroupData(const RsGxsGroupId& group_id,RsGxsChannelGroup& distant_group) { - RsGxsGroupSummary gs; + RsGxsGroupSearchResults gs; if(netService()->retrieveDistantGroupSummary(group_id,gs)) { - // This is a placeholder information by the time we receive the full group meta data. + // This is a placeholder information by the time we receive the full group meta data and check the signature. distant_group.mMeta.mGroupId = gs.mGroupId ; distant_group.mMeta.mGroupName = gs.mGroupName; distant_group.mMeta.mGroupFlags = GXS_SERV::FLAG_PRIVACY_PUBLIC ; @@ -2341,6 +2446,7 @@ bool p3GxsChannels::retrieveDistantGroup(const RsGxsGroupId& group_id,RsGxsChann return false ; } +#ifdef TO_REMOVE bool p3GxsChannels::turtleSearchRequest( const std::string& matchString, const std::function& multiCallback, @@ -2420,17 +2526,24 @@ bool p3GxsChannels::localSearchRequest( return true; } +#endif -void p3GxsChannels::receiveDistantSearchResults( - TurtleRequestId id, const RsGxsGroupId& grpId ) +void p3GxsChannels::receiveDistantSearchResults( TurtleRequestId id, const RsGxsGroupId& grpId ) { - std::cerr << __PRETTY_FUNCTION__ << "(" << id << ", " << grpId << ")" - << std::endl; + if(!rsEvents) + return; + + // We temporise here, in order to avoid notifying clients with many events + // So we put some data in there and will send an event with all of them at once every 1 sec at most. + + mSearchResultsToNotify[id].insert(grpId); + +#ifdef TO_REMOVE + std::cerr << __PRETTY_FUNCTION__ << "(" << id << ", " << grpId << ")" << std::endl; { RsGenExchange::receiveDistantSearchResults(id, grpId); - RsGxsGroupSummary gs; - gs.mGroupId = grpId; + RsGxsGroupSearchResults gs; netService()->retrieveDistantGroupSummary(grpId, gs); { @@ -2471,8 +2584,10 @@ void p3GxsChannels::receiveDistantSearchResults( return; } } // RS_STACK_MUTEX(mDistantChannelsCallbacksMapMutex); +#endif } +#ifdef TO_REMOVE void p3GxsChannels::cleanTimedOutCallbacks() { auto now = std::chrono::system_clock::now(); @@ -2501,6 +2616,7 @@ void p3GxsChannels::cleanTimedOutCallbacks() else ++cbpt; } // RS_STACK_MUTEX(mDistantChannelsCallbacksMapMutex) } +#endif bool p3GxsChannels::exportChannelLink( std::string& link, const RsGxsGroupId& chanId, bool includeGxsData, diff --git a/libretroshare/src/services/p3gxschannels.h b/libretroshare/src/services/p3gxschannels.h index 40ca193e6..da274690b 100644 --- a/libretroshare/src/services/p3gxschannels.h +++ b/libretroshare/src/services/p3gxschannels.h @@ -68,9 +68,9 @@ protected: virtual TurtleRequestId turtleGroupRequest(const RsGxsGroupId& group_id); virtual TurtleRequestId turtleSearchRequest(const std::string& match_string); - virtual bool retrieveDistantSearchResults(TurtleRequestId req, std::map &results) ; + virtual bool retrieveDistantSearchResults(TurtleRequestId req, std::map &results) ; virtual bool clearDistantSearchResults(TurtleRequestId req); - virtual bool retrieveDistantGroup(const RsGxsGroupId& group_id,RsGxsChannelGroup& distant_group); + virtual bool getDistantSearchResultGroupData(const RsGxsGroupId& group_id,RsGxsChannelGroup& distant_group); // Overloaded to cache new groups. virtual RsGenExchange::ServiceCreate_Return service_CreateGroup(RsGxsGrpItem* grpItem, RsTlvSecurityKeySet& keySet); @@ -82,9 +82,10 @@ virtual void handle_event(uint32_t event_type, const std::string &elabel); public: -virtual bool getGroupData(const uint32_t &token, std::vector &groups); -virtual bool getPostData(const uint32_t &token, std::vector &posts, std::vector &cmts); -virtual bool getPostData(const uint32_t &token, std::vector &posts); +virtual bool getGroupData(const uint32_t &token, std::vector &groups ) override; +virtual bool getPostData(const uint32_t &token, std::vector &posts, std::vector &cmts, std::vector &vots) override; +virtual bool getPostData(const uint32_t &token, std::vector &posts, std::vector &cmts) override; +virtual bool getPostData(const uint32_t &token, std::vector &posts) override; //Not currently used //virtual bool getRelatedPosts(const uint32_t &token, std::vector &posts); @@ -108,6 +109,7 @@ virtual bool getChannelAutoDownload(const RsGxsGroupId &groupid, bool& enabled); virtual bool setChannelDownloadDirectory(const RsGxsGroupId &groupId, const std::string& directory); virtual bool getChannelDownloadDirectory(const RsGxsGroupId &groupId, std::string& directory); +#ifdef TO_REMOVE /// @see RsGxsChannels::turtleSearchRequest virtual bool turtleSearchRequest(const std::string& matchString, const std::function& multiCallback, @@ -123,6 +125,7 @@ virtual bool getChannelDownloadDirectory(const RsGxsGroupId &groupId, std::strin virtual bool localSearchRequest(const std::string& matchString, const std::function& multiCallback, rstime_t maxWait = 30 ) override; +#endif /** * Receive results from turtle search @see RsGenExchange @see RsNxsObserver @@ -140,7 +143,7 @@ virtual bool getChannelDownloadDirectory(const RsGxsGroupId &groupId, std::strin std::vector &msgs ) override { return mCommentService->getGxsRelatedComments(token, msgs); } - virtual bool createNewComment(uint32_t &token, RsGxsComment &msg) override + virtual bool createNewComment(uint32_t &token, const RsGxsComment &msg) override { return mCommentService->createGxsComment(token, msg); } @@ -185,17 +188,35 @@ virtual bool ExtraFileRemove(const RsFileHash &hash); const std::list& chanIds, std::vector& channelsInfo ) override; - /// Implementation of @see RsGxsChannels::getChannelContent - bool getChannelContent( const RsGxsGroupId& channelId, - const std::set& contentsIds, + /// Implementation of @see RsGxsChannels::getChannelAllMessages + bool getChannelAllContent(const RsGxsGroupId& channelId, + std::vector& posts, + std::vector& comments, + std::vector& votes ) override; + + /// Implementation of @see RsGxsChannels::getChannelMessages + bool getChannelContent(const RsGxsGroupId& channelId, + const std::set& contentIds, std::vector& posts, - std::vector& comments ) override; + std::vector& comments, + std::vector& votes ) override; + + /// Implementation of @see RsGxsChannels::getChannelComments + virtual bool getChannelComments(const RsGxsGroupId &channelId, + const std::set &contentIds, + std::vector &comments) override; /// Implementation of @see RsGxsChannels::getContentSummaries bool getContentSummaries( const RsGxsGroupId& channelId, std::vector& summaries ) override; + /// Implementation of @see RsGxsChannels::getChannelStatistics + bool getChannelStatistics(const RsGxsGroupId& channelId,GxsGroupStatistic& stat) override; + + /// Iplementation of @see RsGxsChannels::getChannelServiceStatistics + bool getChannelServiceStatistics(GxsServiceStatistic& stat) override; + /// Implementation of @see RsGxsChannels::createChannelV2 bool createChannelV2( const std::string& name, const std::string& description, @@ -277,7 +298,7 @@ virtual bool ExtraFileRemove(const RsFileHash &hash); /// @deprecated Implementation of @see RsGxsChannels::createComment RS_DEPRECATED_FOR(createCommentV2) - bool createComment(RsGxsComment& comment) override; + bool createComment(RsGxsComment &comment) override; /// @deprecated Implementation of @see RsGxsChannels::createVote RS_DEPRECATED_FOR(createVoteV2) @@ -355,6 +376,9 @@ bool generateGroup(uint32_t &token, std::string groupName); std::map mKnownChannels; RsMutex mKnownChannelsMutex; + rstime_t mLastDistantSearchNotificationTS; + std::map > mSearchResultsToNotify; +#ifdef TO_REMOVE /** Store search callbacks with timeout*/ std::map< TurtleRequestId, @@ -375,4 +399,5 @@ bool generateGroup(uint32_t &token, std::string groupName); /// Cleanup mSearchCallbacksMap and mDistantChannelsCallbacksMap void cleanTimedOutCallbacks(); +#endif }; diff --git a/libretroshare/src/services/p3gxscircles.cc b/libretroshare/src/services/p3gxscircles.cc index cb3b4a3ff..80f5fa290 100644 --- a/libretroshare/src/services/p3gxscircles.cc +++ b/libretroshare/src/services/p3gxscircles.cc @@ -43,6 +43,18 @@ /*extern*/ RsGxsCircles* rsGxsCircles = nullptr; +/*static*/ const std::string RsGxsCircles::DEFAULT_CIRCLE_BASE_URL = + "retroshare:///circles"; +/*static*/ const std::string RsGxsCircles::CIRCLE_URL_NAME_FIELD = "circleName"; +/*static*/ const std::string RsGxsCircles::CIRCLE_URL_ID_FIELD = "circleId"; +/*static*/ const std::string RsGxsCircles::CIRCLE_URL_DATA_FIELD = "circleData"; + +RsGxsCircles::~RsGxsCircles() = default; +RsGxsCircleMsg::~RsGxsCircleMsg() = default; +RsGxsCircleDetails::~RsGxsCircleDetails() = default; +RsGxsCircleGroup::~RsGxsCircleGroup() = default; +RsGxsCircleEvent::~RsGxsCircleEvent() = default; + /****** * * GxsCircles are used to limit the spread of Gxs Groups and Messages. @@ -73,8 +85,7 @@ * The data-types for the full system, however, will be in-place. */ - -#define CIRCLEREQ_CACHELOAD 0x0001 +#define CIRCLEREQ_CACHELOAD 0x0001 #define CIRCLEREQ_CIRCLE_LIST 0x0002 #define CIRCLEREQ_MESSAGE_DATA 0x0003 @@ -103,30 +114,28 @@ #define MIN_CIRCLE_LOAD_GAP 5 #define GXS_CIRCLE_DELAY_TO_FORCE_MEMBERSHIP_UPDATE 60 // re-check every 1 mins. Normally this shouldn't be necessary since notifications inform abotu new messages. #define GXS_CIRCLE_DELAY_TO_CHECK_MEMBERSHIP_UPDATE 60 // re-check every 1 mins. Normally this shouldn't be necessary since notifications inform abotu new messages. +#define GXS_CIRCLE_DELAY_TO_SEND_CACHE_UPDATED_EVENT 2 // do not send cache update events more often than every 2 secs. -/********************************************************************************/ -/******************* Startup / Tick ******************************************/ -/********************************************************************************/ +//====================================================================================// +// Startup // +//====================================================================================// -p3GxsCircles::p3GxsCircles( - RsGeneralDataService *gds, RsNetworkExchangeService *nes, - p3IdService *identities, PgpAuxUtils *pgpUtils) : - RsGxsCircleExchange( - gds, nes, new RsGxsCircleSerialiser(), RS_SERVICE_GXS_TYPE_GXSCIRCLE, - identities, circleAuthenPolicy() ), - RsGxsCircles(static_cast(*this)), GxsTokenQueue(this), - RsTickEvent(), mIdentities(identities), mPgpUtils(pgpUtils), - mCircleMtx("p3GxsCircles"), - mCircleCache(DEFAULT_MEM_CACHE_SIZE, "GxsCircleCache" ) +p3GxsCircles::p3GxsCircles( RsGeneralDataService *gds, RsNetworkExchangeService *nes, p3IdService *identities, PgpAuxUtils *pgpUtils) + : RsGxsCircleExchange( gds, nes, new RsGxsCircleSerialiser(), RS_SERVICE_GXS_TYPE_GXSCIRCLE, identities, circleAuthenPolicy() ), + RsGxsCircles(static_cast(*this)), GxsTokenQueue(this), + RsTickEvent(), mIdentities(identities), mPgpUtils(pgpUtils), + mCircleMtx("p3GxsCircles"), +// mCircleCache(DEFAULT_MEM_CACHE_SIZE, "GxsCircleCache" ), + mShouldSendCacheUpdateNotification(false) { // Kick off Cache Testing, + Others. //RsTickEvent::schedule_in(CIRCLE_EVENT_CACHETEST, CACHETEST_PERIOD); - mLastCacheMembershipUpdateTS = 0 ; - + mLastCacheMembershipUpdateTS = 0 ; + mLastCacheUpdateEvent = 0; + mLastDebugPrintTS = 0; + RsTickEvent::schedule_now(CIRCLE_EVENT_LOADIDS); - // Dummy Circles. - // RsTickEvent::schedule_in(CIRCLE_EVENT_DUMMYSTART, CIRCLE_DUMMY_STARTPERIOD); mDummyIdToken = 0; } @@ -154,12 +163,18 @@ RsServiceInfo p3GxsCircles::getServiceInfo() GXS_CIRCLES_MIN_MINOR_VERSION); } +//====================================================================================// +// Synchroneous API from rsGxsCircles // +//====================================================================================// + bool p3GxsCircles::createCircle( const std::string& circleName, RsGxsCircleType circleType, RsGxsCircleId& circleId, const RsGxsCircleId& restrictedId, const RsGxsId& authorId, const std::set& gxsIdMembers, const std::set& localMembers ) { + // 1 - Check consistency of the request data + if(circleName.empty()) { RsErr() << __PRETTY_FUNCTION__ << " Circle name is empty" << std::endl; @@ -218,6 +233,8 @@ bool p3GxsCircles::createCircle( return false; } + // 2 - Create the actual request + RsGxsCircleGroup cData; cData.mMeta.mGroupName = circleName; cData.mMeta.mAuthorId = authorId; @@ -227,20 +244,20 @@ bool p3GxsCircles::createCircle( cData.mLocalFriends = localMembers; cData.mInvitedMembers = gxsIdMembers; + // 3 - Send it and wait, for a sync response. + uint32_t token; createGroup(token, cData); if(waitToken(token) != RsTokenService::COMPLETE) { - std::cerr << __PRETTY_FUNCTION__ << "Error! GXS operation failed." - << std::endl; + std::cerr << __PRETTY_FUNCTION__ << "Error! GXS operation failed." << std::endl; return false; } if(!RsGenExchange::getPublishedGroupMeta(token, cData.mMeta)) { - std::cerr << __PRETTY_FUNCTION__ << "Error! Failure getting created" - << " group data." << std::endl; + std::cerr << __PRETTY_FUNCTION__ << "Error! Failure getting created" << " group data." << std::endl; return false; } @@ -275,9 +292,13 @@ bool p3GxsCircles::getCirclesSummaries(std::list& circles) uint32_t token; RsTokReqOptions opts; opts.mReqType = GXS_REQUEST_TYPE_GROUP_META; - if( !requestGroupInfo(token, opts) - || waitToken(token) != RsTokenService::COMPLETE ) return false; - return getGroupSummary(token, circles); + if( !requestGroupInfo(token, opts) || waitToken(token) != RsTokenService::COMPLETE ) + { + std::cerr << "Cannot get circles summary. Token queue is overloaded?" << std::endl; + return false; + } + else + return getGroupSummary(token, circles); } bool p3GxsCircles::getCirclesInfo( const std::list& circlesIds, @@ -286,9 +307,13 @@ bool p3GxsCircles::getCirclesInfo( const std::list& circlesIds, uint32_t token; RsTokReqOptions opts; opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; - if( !requestGroupInfo(token, opts, circlesIds) - || waitToken(token) != RsTokenService::COMPLETE ) return false; - return getGroupData(token, circlesInfo); + if( !requestGroupInfo(token, opts, circlesIds) || waitToken(token) != RsTokenService::COMPLETE ) + { + std::cerr << "Cannot get circle info. Token queue is overloaded?" << std::endl; + return false; + } + else + return getGroupData(token, circlesInfo); } bool p3GxsCircles::getCircleRequests( const RsGxsGroupId& circleId, @@ -365,6 +390,44 @@ bool p3GxsCircles::inviteIdsToCircle( const std::set& identities, return editCircle(circleGrp); } +bool p3GxsCircles::revokeIdsFromCircle( const std::set& identities, const RsGxsCircleId& circleId ) +{ + const std::list circlesIds{ RsGxsGroupId(circleId) }; + std::vector circlesInfo; + + if(!getCirclesInfo(circlesIds, circlesInfo)) + { + std::cerr << __PRETTY_FUNCTION__ << "Error! Failure getting group data." << std::endl; + return false; + } + + if(circlesInfo.empty()) + { + std::cerr << __PRETTY_FUNCTION__ << "Error! Circle: " << circleId.toStdString() << " not found!" << std::endl; + return false; + } + + RsGxsCircleGroup& circleGrp = circlesInfo[0]; + + if(!(circleGrp.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN)) + { + std::cerr << __PRETTY_FUNCTION__ << "Error! Attempt to edit non-own " << "circle: " << circleId.toStdString() << std::endl; + return false; + } + + // /!\ AVOID calling circleGrp.mInvitedMembers.erase(identities.begin(),identities.end()), because it is not the same set. Consequently + // STL code would corrupt the structure of mInvitedMembers. + + std::set new_invited_members; + for(auto& gxs_id: circleGrp.mInvitedMembers) + if(identities.find(gxs_id) == identities.end()) + new_invited_members.insert(gxs_id); + + circleGrp.mInvitedMembers = new_invited_members; + + return editCircle(circleGrp); +} + bool p3GxsCircles::exportCircleLink( std::string& link, const RsGxsCircleId& circleId, bool includeGxsData, const std::string& baseUrl, std::string& errMsg ) @@ -453,6 +516,9 @@ uint32_t p3GxsCircles::circleAuthenPolicy() return policy; } +//====================================================================================// +// Tick // +//====================================================================================// void p3GxsCircles::service_tick() { @@ -460,14 +526,38 @@ void p3GxsCircles::service_tick() GxsTokenQueue::checkRequests(); // GxsTokenQueue handles all requests. rstime_t now = time(NULL); - if(now > mLastCacheMembershipUpdateTS + GXS_CIRCLE_DELAY_TO_CHECK_MEMBERSHIP_UPDATE) + + if(mShouldSendCacheUpdateNotification && now > mLastCacheUpdateEvent + GXS_CIRCLE_DELAY_TO_SEND_CACHE_UPDATED_EVENT) + { + if(rsEvents) + { + auto ev = std::make_shared(); + ev->mCircleEventType = RsGxsCircleEventCode::CACHE_DATA_UPDATED; + rsEvents->postEvent(ev); + } + + mLastCacheUpdateEvent = now; + mShouldSendCacheUpdateNotification = false; + } + + if(now > mLastCacheMembershipUpdateTS + GXS_CIRCLE_DELAY_TO_CHECK_MEMBERSHIP_UPDATE) { checkCircleCache(); mLastCacheMembershipUpdateTS = now ; } - return; + +#ifdef DEBUG_CIRCLES + if(now > mLastDebugPrintTS) + { + mLastDebugPrintTS = now; + debug_dumpCache(); + } +#endif } +//====================================================================================// +// Handling of GXS changes // +//====================================================================================// void p3GxsCircles::notifyChanges(std::vector &changes) { @@ -477,210 +567,230 @@ void p3GxsCircles::notifyChanges(std::vector &changes) #endif p3Notify *notify = RsServer::notify(); + std::set circles_to_reload; for(auto it = changes.begin(); it != changes.end(); ++it) { - RsGxsGroupChange *groupChange = dynamic_cast(*it); - RsGxsMsgChange *msgChange = dynamic_cast(*it); RsGxsNotify *c = *it; + RsGxsMsgChange *msgChange = dynamic_cast(c); - if (msgChange && !msgChange->metaChange()) + if (msgChange) { #ifdef DEBUG_CIRCLES - std::cerr << " Found circle Message Change Notification" << std::endl; + std::cerr << " Found circle Message Change Notification for group " << msgChange->mGroupId << ", msg ID " << msgChange->mMsgId << std::endl; + std::cerr << " Msgs for Group: " << msgChange->mGroupId << std::endl; #endif - for(auto mit = msgChange->msgChangeMap.begin(); mit != msgChange->msgChangeMap.end(); ++mit) + RsGxsCircleId circle_id(msgChange->mGroupId); + + if(rsEvents && (c->getType() == RsGxsNotify::TYPE_RECEIVED_NEW)) { -#ifdef DEBUG_CIRCLES - std::cerr << " Msgs for Group: " << mit->first << std::endl; -#endif - RsGxsCircleId circle_id(mit->first); + const RsGxsCircleSubscriptionRequestItem *item = dynamic_cast(msgChange->mNewMsgItem); - force_cache_reload(circle_id); + if(item) + { + auto ev = std::make_shared(); + ev->mCircleId = circle_id; + ev->mGxsId = msgChange->mNewMsgItem->meta.mAuthorId; - RsGxsCircleDetails details; - getCircleDetails(circle_id,details); - - if(rsEvents && (c->getType() == RsGxsNotify::TYPE_RECEIVED_NEW|| c->getType() == RsGxsNotify::TYPE_PUBLISHED) ) - for (auto msgIdIt(mit->second.begin()), end(mit->second.end()); msgIdIt != end; ++msgIdIt) + if (item->subscription_type == RsGxsCircleSubscriptionType::UNSUBSCRIBE) { - RsGxsCircleMsg msg; - getCircleRequest(RsGxsGroupId(circle_id),*msgIdIt,msg); - - auto ev = std::make_shared(); - ev->mCircleId = circle_id; - ev->mGxsId = msg.mMeta.mAuthorId; - - if (msg.stuff == "SUBSCRIPTION_REQUEST_UNSUBSCRIBE") - ev->mCircleEventType = RsGxsCircleEventCode::CIRCLE_MEMBERSHIP_LEAVE; - else if(details.mAllowedGxsIds.find(msg.mMeta.mAuthorId) != details.mAllowedGxsIds.end()) - ev->mCircleEventType = RsGxsCircleEventCode::CIRCLE_MEMBERSHIP_JOIN; - else - ev->mCircleEventType = RsGxsCircleEventCode::CIRCLE_MEMBERSHIP_REQUEST; - + ev->mCircleEventType = RsGxsCircleEventCode::CIRCLE_MEMBERSHIP_LEAVE; rsEvents->postEvent(ev); } - + else if(item->subscription_type == RsGxsCircleSubscriptionType::SUBSCRIBE) + { + ev->mCircleEventType = RsGxsCircleEventCode::CIRCLE_MEMBERSHIP_REQUEST; + rsEvents->postEvent(ev); + } + else + RsErr() << __PRETTY_FUNCTION__ << " Unknown subscription request type " << static_cast(item->subscription_type) << " in msg item" << std::endl; + } + else + RsErr() << __PRETTY_FUNCTION__ << ": missing SubscriptionRequestItem in msg notification for msg " << msgChange->mMsgId << std::endl; } + + circles_to_reload.insert(circle_id); } + RsGxsGroupChange *groupChange = dynamic_cast(c); + /* add groups to ExternalIdList (Might get Personal Circles here until NetChecks in place) */ - if (groupChange && !groupChange->metaChange()) - { -#ifdef DEBUG_CIRCLES - std::cerr << " Found Group Change Notification" << std::endl; -#endif - for(std::list::iterator git = groupChange->mGrpIdList.begin(); git != groupChange->mGrpIdList.end(); ++git) - { -#ifdef DEBUG_CIRCLES - std::cerr << " Incoming Group: " << *git << ". Forcing cache load." << std::endl; -#endif - - // for new circles we need to add them to the list. - // we don't know the type of this circle here - // original behavior was to add all ids to the external ids list - - addCircleIdToList(RsGxsCircleId(*git), 0); - - // reset the cached circle data for this id - { - RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ - mCircleCache.erase(RsGxsCircleId(*git)); - } - } - } - - if(groupChange) + if (groupChange) { - std::list own_ids; - rsIdentity->getOwnIds(own_ids); + const RsGxsGroupId *git(&groupChange->mGroupId); - for(std::list::const_iterator git(groupChange->mGrpIdList.begin());git!=groupChange->mGrpIdList.end();++git) +#ifdef DEBUG_CIRCLES + std::cerr << " Found Group Change Notification of type " << c->getType() << std::endl; +#endif + switch(c->getType()) + { + case RsGxsNotify::TYPE_RECEIVED_NEW: + case RsGxsNotify::TYPE_UPDATED: + case RsGxsNotify::TYPE_PUBLISHED: { #ifdef DEBUG_CIRCLES - std::cerr << " forcing cache loading for circle " << *git << " in order to trigger subscribe update." << std::endl; + std::cerr << " Incoming/created/updated Group: " << *git << ". Forcing cache load." << std::endl; #endif -#ifdef TODO - // This code will not work: we would like to detect changes in the circle data that reflects the fact that one of the - // owned GXS ids is invited. But there's no way to compare the old circle data to the new if cache has to be updated. - // For this we need to add the old metadata and group data in the RsGxsGroupChange structure and account for it. + // for new circles we need to add them to the list. + // we don't know the type of this circle here + // original behavior was to add all ids to the external ids list - if(rsEvents && (c->getType() == RsGxsNotify::TYPE_RECEIVED_NEW) ) - { - RsGxsCircleId circle_id(*git); - force_cache_reload(circle_id); + addCircleIdToList(RsGxsCircleId(*git), 0); - RsGxsCircleDetails details; - getCircleDetails(circle_id,details); - - // We check that the change corresponds to one of our own ids. Since we do not know what the change is, we notify - // for whatever is different from what is currently known. Other ids, that get invited only trigger a notification when the - // ID also accepts the invitation, so it becomes a member of the circle. - - for(auto own_id: own_ids) - { - auto it = details.mSubscriptionFlags.find(own_id); - - if(it == details.mSubscriptionFlags.end()) - continue; - - bool invited ( it->second & GXS_EXTERNAL_CIRCLE_FLAGS_IN_ADMIN_LIST ); - bool subscrb ( it->second & GXS_EXTERNAL_CIRCLE_FLAGS_SUBSCRIBED ); - - if(std::find(details.mAllowedGxsIds.begin(),details.mAllowedGxsIds.end(),id) != details.mAllowedGxsIds.end() && !me_in_circle) - { - auto ev = std::make_shared(); - - ev->mType = RsGxsCircleEvent::CIRCLE_MEMBERSHIP_INVITE; - ev->mCircleId = circle_id; - ev->mGxsId = ; - - rsEvents->sendEvent(ev); - } - } - - } + circles_to_reload.insert(RsGxsCircleId(*git)); + } + break; + default: +#ifdef DEBUG_CIRCLES + std::cerr << " Type: " << c->getType() << " is ignored" << std::endl; #endif + break; + } - if(rsEvents && (c->getType() == RsGxsNotify::TYPE_RECEIVED_NEW|| c->getType() == RsGxsNotify::TYPE_PUBLISHED) ) + // Now compute which events should be sent. + + if(rsEvents) + { + if(c->getType() == RsGxsNotify::TYPE_RECEIVED_NEW|| c->getType() == RsGxsNotify::TYPE_PUBLISHED) { auto ev = std::make_shared(); ev->mCircleId = RsGxsCircleId(*git); ev->mCircleEventType = RsGxsCircleEventCode::NEW_CIRCLE; + rsEvents->postEvent(ev); + + // we also need to look into invitee list here! + + RsGxsCircleGroupItem *new_circle_grp_item = dynamic_cast(groupChange->mNewGroupItem); + + if(new_circle_grp_item) // groups published by us do not come in the mNewGroupItem field. It's possible to add them, in rsgenexchange.cc:2806 + for(auto& gxs_id: new_circle_grp_item->gxsIdSet.ids) + { + auto ev = std::make_shared(); + + ev->mCircleEventType = RsGxsCircleEventCode::CIRCLE_MEMBERSHIP_ID_ADDED_TO_INVITEE_LIST; + ev->mCircleId = RsGxsCircleId(*git); + ev->mGxsId = gxs_id; + + rsEvents->sendEvent(ev); + } } - - // reset circle from cache since the number of invitee may have changed. + else if(c->getType()==RsGxsNotify::TYPE_UPDATED) { - RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ - mCircleCache.erase(RsGxsCircleId(*git)); - } + // Happens when the group data has changed. In this case we need to analyse the old and new group in order to detect possible notifications for clients + RsGxsCircleGroupItem *old_circle_grp_item = dynamic_cast(groupChange->mOldGroupItem); + RsGxsCircleGroupItem *new_circle_grp_item = dynamic_cast(groupChange->mNewGroupItem); + + const RsGxsCircleId circle_id ( old_circle_grp_item->meta.mGroupId ); + + if(old_circle_grp_item == nullptr || new_circle_grp_item == nullptr) + { + RsErr() << __PRETTY_FUNCTION__ << " received GxsGroupUpdate item with mOldGroup and mNewGroup not of type RsGxsCircleGroupItem. This is inconsistent!" << std::endl; + delete groupChange; + continue; + } + + // First of all, we check if there is a difference between the old and new list of invited members + + for(auto& gxs_id: new_circle_grp_item->gxsIdSet.ids) + if(old_circle_grp_item->gxsIdSet.ids.find(gxs_id) == old_circle_grp_item->gxsIdSet.ids.end()) + { + auto ev = std::make_shared(); + + ev->mCircleEventType = RsGxsCircleEventCode::CIRCLE_MEMBERSHIP_ID_ADDED_TO_INVITEE_LIST; + ev->mCircleId = circle_id; + ev->mGxsId = gxs_id; + + rsEvents->sendEvent(ev); + } + + for(auto& gxs_id: old_circle_grp_item->gxsIdSet.ids) + if(new_circle_grp_item->gxsIdSet.ids.find(gxs_id) == old_circle_grp_item->gxsIdSet.ids.end()) + { + auto ev = std::make_shared(); + + ev->mCircleEventType = RsGxsCircleEventCode::CIRCLE_MEMBERSHIP_ID_REMOVED_FROM_INVITEE_LIST; + ev->mCircleId = circle_id; + ev->mGxsId = gxs_id; + + rsEvents->sendEvent(ev); + } + + } } } - delete *it; + delete c; } + + for(auto& circle_id:circles_to_reload) + force_cache_reload(circle_id); } -/********************************************************************************/ -/******************* RsCircles Interface ***************************************/ -/********************************************************************************/ +//====================================================================================// +// Synchroneous API using cache storage // +//====================================================================================// -bool p3GxsCircles::getCircleDetails( - const RsGxsCircleId& id, RsGxsCircleDetails& details) +bool p3GxsCircles::getCircleDetails(const RsGxsCircleId& id, RsGxsCircleDetails& details) { #ifdef DEBUG_CIRCLES - std::cerr << "p3GxsCircles::getCircleDetails(" << id << ")"; - std::cerr << std::endl; + std::cerr << "p3GxsCircles::getCircleDetails(" << id << ")"; + std::cerr << std::endl; #endif // DEBUG_CIRCLES - { - RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ - if (mCircleCache.is_cached(id)) - { - RsGxsCircleCache &data = mCircleCache.ref(id); + { + bool should_reload = false; - // should also have meta data.... - details.mCircleId = id; - details.mCircleName = data.mCircleName; + RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ + RsGxsCircleCache& data(mCircleCache[id]); - details.mCircleType = data.mCircleType; - details.mRestrictedCircleId = data.mRestrictedCircleId; + if(data.mStatus < CircleEntryCacheStatus::LOADING) + should_reload = true; - details.mAllowedNodes = data.mAllowedNodes; - details.mSubscriptionFlags.clear(); - details.mAllowedGxsIds.clear(); - details.mAmIAllowed = false ; - details.mAmIAdmin = bool(data.mGroupSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN); + if(data.mStatus == CircleEntryCacheStatus::LOADING) + return false; - for(std::map::const_iterator it(data.mMembershipStatus.begin());it!=data.mMembershipStatus.end();++it) - { - details.mSubscriptionFlags[it->first] = it->second.subscription_flags ; + // should also have meta data.... - if(it->second.subscription_flags == GXS_EXTERNAL_CIRCLE_FLAGS_ALLOWED) - { - details.mAllowedGxsIds.insert(it->first) ; - - if(rsIdentity->isOwnId(it->first)) - details.mAmIAllowed = true ; - } - } + if(!should_reload) + { + details.mCircleId = id; + details.mCircleName = data.mCircleName; + details.mCircleType = data.mCircleType; + details.mRestrictedCircleId = data.mRestrictedCircleId; - return true; - } - } + details.mAllowedNodes = data.mAllowedNodes; + details.mSubscriptionFlags.clear(); + details.mAllowedGxsIds.clear(); + details.mAmIAllowed = false ; + details.mAmIAdmin = bool(data.mGroupSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN); - /* it isn't there - add to public requests */ - cache_request_load(id); + for(std::map::const_iterator it(data.mMembershipStatus.begin());it!=data.mMembershipStatus.end();++it) + { + details.mSubscriptionFlags[it->first] = it->second.subscription_flags ; - return false; + if(it->second.subscription_flags == GXS_EXTERNAL_CIRCLE_FLAGS_ALLOWED) + { + details.mAllowedGxsIds.insert(it->first) ; + + if(rsIdentity->isOwnId(it->first)) + details.mAmIAllowed = true ; + } + } + + return true; + } + } + + cache_request_load(id); + return false; } -bool p3GxsCircles:: getCircleExternalIdList(std::list &circleIds) +bool p3GxsCircles::getCircleExternalIdList(std::list &circleIds) { #ifdef DEBUG_CIRCLES std::cerr << "p3GxsCircles::getCircleIdList()"; @@ -689,30 +799,22 @@ bool p3GxsCircles:: getCircleExternalIdList(std::list &circleIds) RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ if (circleIds.empty()) - { circleIds = mCircleExternalIdList; - } else { std::list::const_iterator it; for(it = mCircleExternalIdList.begin(); it != mCircleExternalIdList.begin(); ++it) - { circleIds.push_back(*it); - } } return true; } - -/********************************************************************************/ -/******************* RsGcxs Interface ***************************************/ -/********************************************************************************/ - bool p3GxsCircles::isLoaded(const RsGxsCircleId &circleId) { RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ - return mCircleCache.is_cached(circleId); + + return mCircleCache.is_cached(circleId) && (mCircleCache[circleId].mStatus >= CircleEntryCacheStatus::UPDATING); } bool p3GxsCircles::loadCircle(const RsGxsCircleId &circleId) @@ -720,14 +822,17 @@ bool p3GxsCircles::loadCircle(const RsGxsCircleId &circleId) return cache_request_load(circleId); } - int p3GxsCircles::canSend(const RsGxsCircleId &circleId, const RsPgpId &id, bool& should_encrypt) { RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ if (mCircleCache.is_cached(circleId)) { - RsGxsCircleCache &data = mCircleCache.ref(circleId); - should_encrypt = (data.mCircleType == GXS_CIRCLE_TYPE_EXTERNAL); + RsGxsCircleCache& data = mCircleCache.ref(circleId); + + if(data.mStatus < CircleEntryCacheStatus::UPDATING) + return 0; + + should_encrypt = (data.mCircleType == RsGxsCircleType::EXTERNAL); if (data.isAllowedPeer(id)) return 1; @@ -743,6 +848,10 @@ int p3GxsCircles::canReceive(const RsGxsCircleId &circleId, const RsPgpId &id) if (mCircleCache.is_cached(circleId)) { RsGxsCircleCache &data = mCircleCache.ref(circleId); + + if(data.mStatus < CircleEntryCacheStatus::UPDATING) + return 0; + if (data.isAllowedPeer(id)) { return 1; @@ -758,6 +867,10 @@ bool p3GxsCircles::recipients(const RsGxsCircleId &circleId, std::list& if (mCircleCache.is_cached(circleId)) { RsGxsCircleCache &data = mCircleCache.ref(circleId); + + if(data.mStatus < CircleEntryCacheStatus::UPDATING) + return 0; + data.getAllowedPeersList(friendlist); return true; } @@ -770,6 +883,10 @@ bool p3GxsCircles::isRecipient(const RsGxsCircleId &circleId, const RsGxsGroupId if (mCircleCache.is_cached(circleId)) { const RsGxsCircleCache &data = mCircleCache.ref(circleId); + + if(data.mStatus < CircleEntryCacheStatus::UPDATING) + return 0; + return data.isAllowedPeer(id,destination_group); } return false; @@ -789,16 +906,20 @@ bool p3GxsCircles::recipients(const RsGxsCircleId& circleId, const RsGxsGroupId& const RsGxsCircleCache& cache = mCircleCache.ref(circleId); + if(cache.mStatus < CircleEntryCacheStatus::UPDATING) + return 0; + for(std::map::const_iterator it(cache.mMembershipStatus.begin());it!=cache.mMembershipStatus.end();++it) - if(allowedGxsIdFlagTest(it->second.subscription_flags, RsGxsCircleId(dest_group) == circleId)) + if(allowedGxsIdFlagTest(it->second.subscription_flags, RsGxsCircleId(dest_group) == circleId)) gxs_ids.push_back(it->first) ; return true; } -/********************************************************************************/ -/******************* Get/Set Data ******************************************/ -/********************************************************************************/ +//====================================================================================// +// Asynchroneous API using token system // +//====================================================================================// + bool p3GxsCircles::getGroupData(const uint32_t &token, std::vector &groups) { @@ -835,10 +956,6 @@ bool p3GxsCircles::getGroupData(const uint32_t &token, std::vector &msgs) { GxsMsgDataMap msgData; @@ -855,8 +972,9 @@ bool p3GxsCircles::getMsgData(const uint32_t &token, std::vector for(; vit != msgItems.end(); ++vit) { +#ifdef TO_REMOVE RsGxsCircleMsgItem* item = dynamic_cast(*vit); - RsGxsCircleSubscriptionRequestItem* rsItem = dynamic_cast(*vit); + if(item) { RsGxsCircleMsg msg = item->mMsg; @@ -864,22 +982,16 @@ bool p3GxsCircles::getMsgData(const uint32_t &token, std::vector msgs.push_back(msg); delete item; } - else if (rsItem) +#endif + + RsGxsCircleSubscriptionRequestItem* rsItem = dynamic_cast(*vit); + + if (rsItem) { RsGxsCircleMsg msg ;//= rsItem->mMsg; msg.mMeta = rsItem->meta; - switch (rsItem->subscription_type) - { - case RsGxsCircleSubscriptionRequestItem::SUBSCRIPTION_REQUEST_UNKNOWN: - msg.stuff.clear(); - break; - case RsGxsCircleSubscriptionRequestItem::SUBSCRIPTION_REQUEST_SUBSCRIBE: - msg.stuff="SUBSCRIPTION_REQUEST_SUBSCRIBE"; - break; - case RsGxsCircleSubscriptionRequestItem::SUBSCRIPTION_REQUEST_UNSUBSCRIBE: - msg.stuff="SUBSCRIPTION_REQUEST_UNSUBSCRIBE"; - break; - } + msg.mSubscriptionType = rsItem->subscription_type; + msgs.push_back(msg); delete rsItem; } @@ -948,59 +1060,71 @@ RsGenExchange::ServiceCreate_Return p3GxsCircles::service_CreateGroup(RsGxsGrpIt return SERVICE_CREATE_SUCCESS; } - - -/************************************************************************************/ -/************************************************************************************/ -/************************************************************************************/ - -/* - * Cache of recently used circles. - */ +//====================================================================================// +// Cache system management // +//====================================================================================// RsGxsCircleCache::RsGxsCircleCache() { - mCircleType = GXS_CIRCLE_TYPE_EXTERNAL; + mCircleType = RsGxsCircleType::EXTERNAL; mIsExternal = true; - mUpdateTime = 0; + mLastUpdateTime = 0; mGroupStatus = 0; mGroupSubscribeFlags = 0; mLastUpdatedMembershipTS = 0 ; + mStatus = CircleEntryCacheStatus::NO_DATA_YET; + mAllIdsHere = false; return; } - -bool RsGxsCircleCache::loadBaseCircle(const RsGxsCircleGroup &circle) +bool RsGxsCircleCache::loadBaseCircle(const RsGxsCircleGroup& circle) { +#ifdef DEBUG_CIRCLES + std::cerr << "RsGxsCircleCache::loadBaseCircle(" << mCircleId << "):" << std::endl; +#endif // DEBUG_CIRCLES mCircleId = RsGxsCircleId(circle.mMeta.mGroupId); mCircleName = circle.mMeta.mGroupName; - mUpdateTime = time(NULL); -// mProcessedCircles.insert(mCircleId); + // mProcessedCircles.insert(mCircleId); - mCircleType = circle.mMeta.mCircleType; - mIsExternal = (mCircleType != GXS_CIRCLE_TYPE_LOCAL); + mCircleType = static_cast(circle.mMeta.mCircleType); + mIsExternal = (mCircleType != RsGxsCircleType::LOCAL); mGroupStatus = circle.mMeta.mGroupStatus; mGroupSubscribeFlags = circle.mMeta.mSubscribeFlags; mOriginator = circle.mMeta.mOriginator ; - - mAllowedNodes = circle.mLocalFriends ; - mRestrictedCircleId = circle.mMeta.mCircleId ; - - mMembershipStatus.clear() ; - - for(std::set::const_iterator it(circle.mInvitedMembers.begin());it!=circle.mInvitedMembers.end();++it) - { - RsGxsCircleMembershipStatus& s(mMembershipStatus[*it]) ; - s.last_subscription_TS = 0 ; - s.subscription_flags = GXS_EXTERNAL_CIRCLE_FLAGS_IN_ADMIN_LIST ; - } + + mAllowedNodes = circle.mLocalFriends ; + mRestrictedCircleId = circle.mMeta.mCircleId ; + + // We do not clear mMembershipStatus because this might be an update and if we do, it will clear membership requests + // that are not in the invited list! + + for(std::set::const_iterator it(circle.mInvitedMembers.begin());it!=circle.mInvitedMembers.end();++it) + { + RsGxsCircleMembershipStatus& s(mMembershipStatus[*it]) ; + s.subscription_flags |= GXS_EXTERNAL_CIRCLE_FLAGS_IN_ADMIN_LIST ; + + // This one can be cleared because it will anyway be updated to the latest when loading subscription request messages and it wil only + // be used there as well. + + s.last_subscription_TS = 0 ; #ifdef DEBUG_CIRCLES - std::cerr << "RsGxsCircleCache::loadBaseCircle(" << mCircleId << ")"; - std::cerr << std::endl; + std::cerr << " Invited member " << *it << " Initializing/updating membership status to " << std::hex << s.subscription_flags << std::dec << std::endl; #endif // DEBUG_CIRCLES + } + + // also sweep through the list of subscribed members and remove the membership to those who are not in the invitee list anymore + + for(auto& m:mMembershipStatus) + if(circle.mInvitedMembers.find(m.first) == circle.mInvitedMembers.end()) + { + m.second.subscription_flags &= ~GXS_EXTERNAL_CIRCLE_FLAGS_IN_ADMIN_LIST; +#ifdef DEBUG_CIRCLES + std::cerr << " member " << m.first << " is not in invitee list. Updating flags to " << std::hex << m.second.subscription_flags << std::dec << std::endl; +#endif // DEBUG_CIRCLES + } return true; } @@ -1023,7 +1147,7 @@ bool RsGxsCircleCache::getAllowedPeersList(std::list& friendlist) const { friendlist.clear() ; - for(std::set::const_iterator it = mAllowedNodes.begin(); it != mAllowedNodes.end(); ++it) + for(auto it = mAllowedNodes.begin(); it != mAllowedNodes.end(); ++it) friendlist.push_back(*it) ; return true; @@ -1031,12 +1155,12 @@ bool RsGxsCircleCache::getAllowedPeersList(std::list& friendlist) const bool RsGxsCircleCache::isAllowedPeer(const RsGxsId& id,const RsGxsGroupId& destination_group) const { - std::map::const_iterator it = mMembershipStatus.find(id) ; - - if(it == mMembershipStatus.end()) - return false ; - - return allowedGxsIdFlagTest(it->second.subscription_flags, RsGxsGroupId(mCircleId) == destination_group) ; + auto it = mMembershipStatus.find(id) ; + + if(it == mMembershipStatus.end()) + return false ; + + return allowedGxsIdFlagTest(it->second.subscription_flags, RsGxsGroupId(mCircleId) == destination_group) ; } bool RsGxsCircleCache::isAllowedPeer(const RsPgpId &id) const @@ -1047,11 +1171,10 @@ bool RsGxsCircleCache::isAllowedPeer(const RsPgpId &id) const bool RsGxsCircleCache::addLocalFriend(const RsPgpId &pgpId) { /* empty list as no GxsID associated */ - mAllowedNodes.insert(pgpId) ; + mAllowedNodes.insert(pgpId) ; return true; } - /************************************************************************************/ /************************************************************************************/ @@ -1111,6 +1234,987 @@ bool p3GxsCircles::load_CircleIdList(uint32_t token) + + +/************************************************************************************/ +/************************************************************************************/ +/************************************************************************************/ +// Complicated deal of loading Circles. + +bool p3GxsCircles::force_cache_reload(const RsGxsCircleId& id) +{ +#ifdef DEBUG_CIRCLES + std::cerr << "p3GxsCircles::force_cache_reload(): Forcing cache reload of Circle ID " << id << std::endl; +#endif + + cache_request_load(id) ; + return true ; +} + +bool p3GxsCircles::cache_request_load(const RsGxsCircleId &id) +{ +#ifdef DEBUG_CIRCLES + std::cerr << "p3GxsCircles::cache_request_load(" << id << ")"; + std::cerr << std::endl; +#endif // DEBUG_CIRCLES + + { + RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ + + /* check it is not already being loaded */ + + RsGxsCircleCache& cache(mCircleCache[id]); + + if(cache.mStatus < CircleEntryCacheStatus::LOADING) + cache.mCircleId = id; + + if(cache.mStatus == CircleEntryCacheStatus::LOADING || cache.mStatus == CircleEntryCacheStatus::UPDATING) + return false; + + // Put it into the Loading Cache - so we will detect it later. + + if(cache.mLastUpdateTime > 0) + cache.mStatus = CircleEntryCacheStatus::UPDATING; + else + cache.mStatus = CircleEntryCacheStatus::LOADING; + + mCirclesToLoad.insert(id); + } + + if (RsTickEvent::event_count(CIRCLE_EVENT_CACHELOAD) > 0) /* its already scheduled */ + return true; + + int32_t age = 0; + if (RsTickEvent::prev_event_ago(CIRCLE_EVENT_CACHELOAD, age) && age groupIds; + { + RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ + + /* now we process the modGroupList -> a map so we can use it easily later, and create id list too */ + + for(auto& circle_id:mCirclesToLoad) + { +#ifdef DEBUG_CIRCLES + std::cerr << "p3GxsCircles::cache_start_load() GroupId: " << circle_id << std::endl; +#endif // DEBUG_CIRCLES + + groupIds.push_back(RsGxsGroupId(circle_id.toStdString())); // might need conversion? + } + + mCirclesToLoad.clear(); + } + + if (groupIds.size() > 0) + { +#ifdef DEBUG_CIRCLES + std::cerr << "p3GxsCircles::cache_start_load() #Groups: " << groupIds.size(); + std::cerr << std::endl; +#endif // DEBUG_CIRCLES + + uint32_t ansType = RS_TOKREQ_ANSTYPE_DATA; + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; + uint32_t token = 0; + + RsGenExchange::getTokenService()->requestGroupInfo(token, ansType, opts, groupIds); + GxsTokenQueue::queueRequest(token, CIRCLEREQ_CACHELOAD); + } + return true; +} + + +bool p3GxsCircles::cache_load_for_token(uint32_t token) +{ +#ifdef DEBUG_CIRCLES + std::cerr << "p3GxsCircles::cache_load_for_token() : " << token << std::endl; +#endif // DEBUG_CIRCLES + + std::vector grpData; + + if(!RsGenExchange::getGroupData(token, grpData)) + { + std::cerr << "p3GxsCircles::cache_load_for_token() ERROR no data"; + std::cerr << std::endl; + + return false; + } + + for(auto vit = grpData.begin(); vit != grpData.end(); ++vit) + { + RsGxsCircleGroupItem *item = dynamic_cast(*vit); + + if (!item) + { + std::cerr << " Not a RsGxsCircleGroupItem Item, deleting!" << std::endl; + delete(*vit); + continue; + } + RsGxsCircleGroup group; + item->convertTo(group); + +#ifdef DEBUG_CIRCLES + std::cerr << " Loaded Id with Meta: " << item->meta << std::endl; +#endif // DEBUG_CIRCLES + + RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ + + /* should already have a LoadingCache entry */ + RsGxsCircleId id = RsGxsCircleId(item->meta.mGroupId) ; + RsGxsCircleCache& cache(mCircleCache[id]); + + cache.loadBaseCircle(group); + delete item; + + if(locked_processLoadingCacheEntry(cache)) + { +#ifdef DEBUG_CIRCLES + std::cerr << " All peers available. Moving to cache..." << std::endl; +#endif + cache.mAllIdsHere = true; + + // We can check for self inclusion in the circle right away, since own ids are always loaded. + // that allows to subscribe/unsubscribe uncomplete circles + + cache.mStatus = CircleEntryCacheStatus::CHECKING_MEMBERSHIP; + cache.mLastUpdatedMembershipTS = 0; // force processing of membership request + locked_checkCircleCacheForMembershipUpdate(cache); + } + else + { +#ifdef DEBUG_CIRCLES + std::cerr << " Unprocessed peers. Requesting reload..." << std::endl; +#endif + cache.mAllIdsHere = false; + + /* schedule event to try reload gxsIds */ + RsTickEvent::schedule_in(CIRCLE_EVENT_RELOADIDS, GXSID_LOAD_CYCLE, id.toStdString()); + } + mShouldSendCacheUpdateNotification = true; + + locked_checkCircleCacheForAutoSubscribe(cache); + } + + return true; +} + +// This method parses the cache entry and makes sure that all ids are known. If not, requests the missing ids +// when done, the entry is removed from mLoadingCache + +bool p3GxsCircles::locked_processLoadingCacheEntry(RsGxsCircleCache& cache) +{ + //bool isUnprocessedPeers = false; + + if (!cache.mIsExternal) + return true; + +#ifdef DEBUG_CIRCLES + std::cerr << "Processing External Circle " << cache.mCircleId << std::endl; +#endif + bool all_ids_here = true; + + // Do we actually need to retrieve the missing keys for all members of a circle??? + // These keys are needed for subscribtion request signature checking. But this is only + // when a subscription msg is posted, which would trigger retrieval of the key anyway + // Maybe this can be made an option of p3GxsCircles, or of rsIdentity. + + // need to trigger the searches. + for(std::map::iterator pit = cache.mMembershipStatus.begin(); pit != cache.mMembershipStatus.end(); ++pit) + { +#ifdef DEBUG_CIRCLES + std::cerr << " Member status: " << pit->first << " : " << pit->second.subscription_flags; +#endif + + /* check cache */ + if(!(pit->second.subscription_flags & GXS_EXTERNAL_CIRCLE_FLAGS_KEY_AVAILABLE)) + { + if(mIdentities->haveKey(pit->first)) + { + pit->second.subscription_flags |= GXS_EXTERNAL_CIRCLE_FLAGS_KEY_AVAILABLE; + +#ifdef DEBUG_CIRCLES + std::cerr << " Key is now available!"<< std::endl; +#endif + } + else + { + std::list peers; + + if(!cache.mOriginator.isNull()) + { + peers.push_back(cache.mOriginator) ; +#ifdef DEBUG_CIRCLES + std::cerr << " Requesting unknown/unloaded identity: " << pit->first << " to originator " << cache.mOriginator << std::endl; +#endif + } + else + { +#ifdef DEBUG_CIRCLES + std::cerr << " (WW) cache entry for circle " << cache.mCircleId << " has empty originator. Asking info for GXS id " << pit->first << " to all connected friends." << std::endl; +#endif + + rsPeers->getOnlineList(peers) ; + } + + mIdentities->requestKey(pit->first, peers,RsIdentityUsage(RsServiceType::GXSCIRCLE,RsIdentityUsage::CIRCLE_MEMBERSHIP_CHECK,RsGxsGroupId(cache.mCircleId))); + + all_ids_here = false; + } + } +#ifdef DEBUG_CIRCLES + else + std::cerr << " Key is available. Nothing to process." << std::endl; +#endif + } + + return all_ids_here; +} + +bool p3GxsCircles::cache_reloadids(const RsGxsCircleId &circleId) +{ +#ifdef DEBUG_CIRCLES + std::cerr << "p3GxsCircles::cache_reloadids()"; + std::cerr << std::endl; +#endif // DEBUG_CIRCLES + + RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ + + RsGxsCircleCache& cache(mCircleCache[circleId]); + + /* fetch from loadMap */ + + if(locked_processLoadingCacheEntry(cache)) + { + cache.mAllIdsHere = true; + + // We can check for self inclusion in the circle right away, since own ids are always loaded. + // that allows to subscribe/unsubscribe uncomplete circles + + locked_checkCircleCacheForAutoSubscribe(cache); + + cache.mStatus = CircleEntryCacheStatus::CHECKING_MEMBERSHIP; + locked_checkCircleCacheForMembershipUpdate(cache); + + std::cerr << " Loading complete." << std::endl; + + return true ; + } + + else + { + cache.mAllIdsHere = false; + +#ifdef DEBUG_CIRCLES + std::cerr << " Unprocessed peers. Requesting reload for circle " << circleId << std::endl; +#endif + + /* schedule event to try reload gxsIds */ + RsTickEvent::schedule_in(CIRCLE_EVENT_RELOADIDS, GXSID_LOAD_CYCLE, circleId.toStdString()); + } + + return true; +} + +bool p3GxsCircles::checkCircleCache() +{ +#ifdef DEBUG_CIRCLES + std::cerr << "checkCircleCache(): calling auto-subscribe check and membership update check." << std::endl; +#endif + RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ + + mCircleCache.applyToAllCachedEntries(*this,&p3GxsCircles::locked_checkCircleCacheForMembershipUpdate) ; + mCircleCache.applyToAllCachedEntries(*this,&p3GxsCircles::locked_checkCircleCacheForAutoSubscribe) ; + + return true ; +} + +bool p3GxsCircles::locked_checkCircleCacheForMembershipUpdate(RsGxsCircleCache& cache) +{ + rstime_t now = time(NULL) ; + + if(cache.mStatus < CircleEntryCacheStatus::UPDATING) + return false; + + if(cache.mLastUpdatedMembershipTS + GXS_CIRCLE_DELAY_TO_FORCE_MEMBERSHIP_UPDATE < now) + { +#ifdef DEBUG_CIRCLES + std::cerr << "Cache entry for circle " << cache.mCircleId << " needs a swab over membership requests. Re-scheduling it." << std::endl; +#endif + + // this should be called regularly + + uint32_t token ; + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; + std::list grpIds ; + + grpIds.push_back(RsGxsGroupId(cache.mCircleId)) ; + + RsGenExchange::getTokenService()->requestMsgInfo(token, RS_TOKREQ_ANSTYPE_SUMMARY, opts, grpIds); + GxsTokenQueue::queueRequest(token, CIRCLEREQ_MESSAGE_DATA); + } + return true ; +} + +/* We need to AutoSubscribe if the Circle is relevent to us */ + +bool p3GxsCircles::locked_checkCircleCacheForAutoSubscribe(RsGxsCircleCache &cache) +{ +#ifdef DEBUG_CIRCLES + std::cerr << "p3GxsCircles::locked_checkCircleCacheForAutoSubscribe() : "<< cache.mCircleId << std::endl; +#endif + + /* if processed already - ignore */ + if (!(cache.mGroupStatus & GXS_SERV::GXS_GRP_STATUS_UNPROCESSED)) + { +#ifdef DEBUG_CIRCLES + std::cerr << " Already Processed" << std::endl; +#endif + + return false; + } + + /* if personal - we created ... is subscribed already */ + if (!cache.mIsExternal) + { +#ifdef DEBUG_CIRCLES + std::cerr << " Personal Circle. Nothing to do." << std::endl; +#endif + + return false; + } + + if(cache.mStatus < CircleEntryCacheStatus::UPDATING) + return false; + + /* if we appear in the group - then autosubscribe, and mark as processed. This also applies if we're the group admin */ + + std::list myOwnIds; + + if(!rsIdentity->getOwnIds(myOwnIds)) + { + std::cerr << " own ids not loaded yet." << std::endl; + + /* schedule event to try reload gxsIds */ + RsTickEvent::schedule_in(CIRCLE_EVENT_RELOADIDS, GXSID_LOAD_CYCLE, cache.mCircleId.toStdString()); + return false ; + } + + bool in_admin_list = false ; + bool member_request = false ; + + for(std::list::const_iterator it(myOwnIds.begin());it!=myOwnIds.end() && (!in_admin_list) && (!member_request);++it) + { + std::map::const_iterator it2 = cache.mMembershipStatus.find(*it) ; + + if(it2 != cache.mMembershipStatus.end()) + { + in_admin_list = in_admin_list || bool(it2->second.subscription_flags & GXS_EXTERNAL_CIRCLE_FLAGS_IN_ADMIN_LIST) ; + member_request= member_request|| bool(it2->second.subscription_flags & GXS_EXTERNAL_CIRCLE_FLAGS_SUBSCRIBED) ; + } + } + + bool am_I_admin( cache.mGroupSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN) ; + +#ifdef DEBUG_CIRCLES + std::cerr << " own ID in circle: " << in_admin_list << ", own subscribe request: " << member_request << ", am I admin?: " << am_I_admin << std::endl; +#endif + if(in_admin_list || member_request || am_I_admin) + { + uint32_t token, token2; + + if(! (cache.mGroupSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_SUBSCRIBED)) + { +#ifdef DEBUG_CIRCLES + /* we are part of this group - subscribe, clear unprocessed flag */ + std::cerr << " I'm allowed in this circle => AutoSubscribing!" << std::endl; +#endif + RsGenExchange::subscribeToGroup(token, RsGxsGroupId(cache.mCircleId), true); + } +#ifdef DEBUG_CIRCLES + else + std::cerr << " I'm allowed in this circle, and already subscribed." << std::endl; +#endif + + RsGenExchange::setGroupStatusFlags(token2, RsGxsGroupId(cache.mCircleId), 0, GXS_SERV::GXS_GRP_STATUS_UNPROCESSED); + + cache.mGroupStatus &= ~GXS_SERV::GXS_GRP_STATUS_UNPROCESSED; + + mShouldSendCacheUpdateNotification = true; + + return true; + } + else + { + /* we know all the peers - we are not part - we can flag as PROCESSED. */ + uint32_t token,token2; + RsGenExchange::setGroupStatusFlags(token, RsGxsGroupId(cache.mCircleId.toStdString()), 0, GXS_SERV::GXS_GRP_STATUS_UNPROCESSED); + + if(cache.mGroupSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_SUBSCRIBED) + { + RsGenExchange::subscribeToGroup(token2, RsGxsGroupId(cache.mCircleId), false); +#ifdef DEBUG_CIRCLES + std::cerr << " Not part of the group! Let's unsubscribe this circle of unfriendly Napoleons!" << std::endl; +#endif + } +#ifdef DEBUG_CIRCLES + else + std::cerr << " Not part of the group, and not subscribed either." << std::endl; +#endif + + cache.mGroupStatus &= ~GXS_SERV::GXS_GRP_STATUS_UNPROCESSED; + + mShouldSendCacheUpdateNotification = true; + return true ; + } +} + +void p3GxsCircles::addCircleIdToList(const RsGxsCircleId &circleId, uint32_t circleType) +{ + RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ + + if (circleType == GXS_CIRCLE_TYPE_LOCAL) + { + if(mCirclePersonalIdList.end() == std::find(mCirclePersonalIdList.begin(), mCirclePersonalIdList.end(), circleId)){ + mCirclePersonalIdList.push_back(circleId); + } + } + else + { + if(mCircleExternalIdList.end() == std::find(mCircleExternalIdList.begin(), mCircleExternalIdList.end(), circleId)){ + mCircleExternalIdList.push_back(circleId); + } + } +} + +//====================================================================================// +// Event handling // +//====================================================================================// + + // Overloaded from GxsTokenQueue for Request callbacks. +void p3GxsCircles::handleResponse(uint32_t token, uint32_t req_type) +{ +#ifdef DEBUG_CIRCLES + std::cerr << "p3GxsCircles::handleResponse(" << token << "," << req_type << ")"; + std::cerr << std::endl; +#endif // DEBUG_CIRCLES + + // stuff. + switch(req_type) + { + case CIRCLEREQ_CIRCLE_LIST: + load_CircleIdList(token); + break; + + case CIRCLEREQ_MESSAGE_DATA: + processMembershipRequests(token); + break; + + case CIRCLEREQ_CACHELOAD: + cache_load_for_token(token); + break; + default: + RsErr() << __PRETTY_FUNCTION__ << " Unknown Request Type: " + << req_type << std::endl; + break; + } +} + + // Overloaded from RsTickEvent for Event callbacks. +void p3GxsCircles::handle_event(uint32_t event_type, const std::string &elabel) +{ +#ifdef DEBUG_CIRCLES + std::cerr << "p3GxsCircles::handle_event(" << event_type << ")"; + std::cerr << std::endl; +#endif // DEBUG_CIRCLES + + // stuff. + switch(event_type) + { + case CIRCLE_EVENT_LOADIDS: + request_CircleIdList(); + break; + + case CIRCLE_EVENT_CACHELOAD: + cache_start_load(); + break; + + case CIRCLE_EVENT_RELOADIDS: + cache_reloadids(RsGxsCircleId(elabel)); + break; + + case CIRCLE_EVENT_DUMMYSTART: + generateDummyData(); + break; + + case CIRCLE_EVENT_DUMMYLOAD: + checkDummyIdData(); + break; + + case CIRCLE_EVENT_DUMMYGEN: + generateDummyCircle(); + break; + + default: + RsErr() << __PRETTY_FUNCTION__ << " Unknown Event Type: " << event_type + << std::endl; + break; + } +} + +//====================================================================================// +// Membership request handling // +//====================================================================================// + +// Circle membership is requested/denied by posting a message into the cicle group, according to the following rules: +// +// - a subscription request is a RsItem (which serialises into a radix64 message, that is further signed by the group message publishing system) +// The item contains: +// * subscribe order (yes/no), boolean +// * circle ID (this is important, otherwise people can copy subscribe messages from one circle to another) +// * subscribe date +// * subscribe timeout (how long is the message kept. When timed out, the message is removed and subscription cancelled) +// +// - subscribe messages follow the following rules, which are enforced by a timer-based method: +// * subscription requests from a given user are always replaced by the last subscription request +// * a complete list of who's subscribed to a given group is kept, saved, and regularly updated when new subscribe messages are received, or when admin list is changed. +// * getGroupDetails reads this list in order to respond who's subscribed to a group. The list of +// +// - compatibility with self-restricted circles: +// * subscription should be based on admin list, so that non subscribed peers still receive the invitation +// +// - two possible subscription models for circle member list (Restricted forums only propagate to members): +// 1 - list of admin who have not opposed subscription +// - solves propagation issue. Only admin see data. They can however unsubscribe using a negative req. Admin needs to remove them. +// - bad for security. Admin can refuse to remove them => back to square one +// 2 - list of admin who have also requested membership +// - propagation is ok. On restricted circle, the circle msgs/group should be sent to admin list, instead of member list. +// - solves membership issue since people need to actively be in the group. +// => choose 2 +// - forum group : encrypted for Member list +// - circle group : clear / encrypted for admin list (for self-restricted) +// We decide between the two by comparing the group we're sending and the circle id it is restricted to. +// +// - Use cases +// * user sees group (not self restricted) and requests to subscribe => RS subscribes the group and the user can propagate the response +// * user is invited to self-restricted circle. He will see it and can subscribe, so he will be in admin list and receive e.g. forum posts. +// * +// +// - Threat model +// * a malicious user forges a new subscription request: NP-hard as it needs to break the RSA key of the GXS id. +// * a malicious corrupts a subscription request: NP-hard. Messages are signed. +// * a malicious user copies an old subscription of someone else and inserts it in the system. +// => not possible. Either this existing old susbscription already exists, or it has been replaced by a more recent one, which +// will always replace the old one because of the date. +// * a malicious user removes someone's subscription messages. This is possible, but the mesh nature of the network will allow the message to propagate anyway. +// * a malicious user creates a circle with an incriminating name/content and adds everyone in it +// => people can oppose their membership in the circle using a msg +// +// +// - the table below summarizes the various choices: forum and circle propagation when restricted to a circle, and group subscribe to the circle +// +// +------------------------------+-----------------------------+ +// | User in admin list | User not in admin list | +// +-------------+------------------------------+-----------------------------+ +// | User request| Forum Grp/Msg: YES | Forum Grp/Msg: NO | +// | Subscription| Circle Grp/Msg: YES/YES | Circle Grp/Msg: YES/NO | +// | | Grp Subscribed: YES | Grp Subscribed: YES | +// +-------------+------------------------------+-----------------------------+ +// | No request | Forum Grp/Msg: NO | Forum Grp/Msg: NO | +// | Subscription| Circle Grp/Msg: YES/YES | Circle Grp/Msg: YES/NO | +// | | Grp Subscribed: NO | Grp Subscribed: NO | +// +-------------+------------------------------+-----------------------------+ + +bool p3GxsCircles::pushCircleMembershipRequest( + const RsGxsId& own_gxsid, const RsGxsCircleId& circle_id, + RsGxsCircleSubscriptionType request_type ) +{ + Dbg3() << __PRETTY_FUNCTION__ << "own_gxsid = " << own_gxsid + << ", circle=" << circle_id << ", req type=" << request_type + << std::endl; + + if( request_type != RsGxsCircleSubscriptionType::SUBSCRIBE && request_type != RsGxsCircleSubscriptionType::UNSUBSCRIBE ) + { + RsErr() << __PRETTY_FUNCTION__ << " Unknown request type: " << static_cast(request_type) << std::endl; + return false; + } + + if(!rsIdentity->isOwnId(own_gxsid)) + { + RsErr() << __PRETTY_FUNCTION__ << " Cannot generate membership request " + << "from not-own id: " << own_gxsid << std::endl; + return false; + } + + if(!getCirclesInfo( + std::list{static_cast(circle_id)}, + RS_DEFAULT_STORAGE_PARAM(std::vector) )) + { + RsErr() << __PRETTY_FUNCTION__ << " Cannot generate membership request " + << "from unknown circle: " << circle_id << std::endl; + return false; + } + + // Create a subscribe item + + RsGxsCircleSubscriptionRequestItem *s = new RsGxsCircleSubscriptionRequestItem ; + + s->time_stamp = time(NULL) ; + s->time_out = 0 ; // means never + s->subscription_type = request_type ; + + RsTemporaryMemory tmpmem(circle_id.serial_size() + own_gxsid.serial_size()) ; + + uint32_t off = 0 ; + circle_id.serialise(tmpmem,tmpmem.size(),off) ; + own_gxsid.serialise(tmpmem,tmpmem.size(),off) ; + + s->meta.mGroupId = RsGxsGroupId(circle_id) ; + s->meta.mMsgId.clear(); + s->meta.mThreadId = RsGxsMessageId(RsDirUtil::sha1sum(tmpmem,tmpmem.size())); // make the ID from the hash of the cirle ID and the author ID + s->meta.mAuthorId = own_gxsid; + + // msgItem->meta.mParentId = ; // leave these blank + // msgItem->meta.mOrigMsgId= ; + +#ifdef DEBUG_CIRCLES + std::cerr << "p3GxsCircles::publishSubscribeRequest()" << std::endl; + std::cerr << " GroupId : " << circle_id << std::endl; + std::cerr << " AuthorId : " << s->meta.mAuthorId << std::endl; + std::cerr << " ThreadId : " << s->meta.mThreadId << std::endl; +#endif + uint32_t token ; + + if(request_type == RsGxsCircleSubscriptionType::SUBSCRIBE) + RsGenExchange::subscribeToGroup(token, RsGxsGroupId(circle_id), true); + + RsGenExchange::publishMsg(token, s); + + // update the cache. + force_cache_reload(circle_id); + + return true; +} + +bool p3GxsCircles::requestCircleMembership(const RsGxsId& own_gxsid,const RsGxsCircleId& circle_id) +{ + return pushCircleMembershipRequest(own_gxsid,circle_id,RsGxsCircleSubscriptionType::SUBSCRIBE) ; +} +bool p3GxsCircles::cancelCircleMembership(const RsGxsId& own_gxsid,const RsGxsCircleId& circle_id) +{ + return pushCircleMembershipRequest(own_gxsid,circle_id,RsGxsCircleSubscriptionType::UNSUBSCRIBE) ; +} + +bool p3GxsCircles::processMembershipRequests(uint32_t token) +{ + // Go through membership request messages and process them according to the following rule: + // * for each ID only keep the latest membership request. Delete the older ones. + // * for each circle, keep a list of IDs sorted into membership categories (e.g. keep updated flags for each IDs) + // Because msg loading is async-ed, the job in split in two methods: one calls the loading, the other one handles the loaded data. + +#ifdef DEBUG_CIRCLES + std::cerr << "Processing circle membership requests." << std::endl; +#endif + t_RsGxsGenericDataTemporaryMapVector msgItems; + + if(!RsGenExchange::getMsgData(token, msgItems)) + { + std::cerr << "(EE) Cannot get msg data for circle. Something's weird." << std::endl; + return false; + } + + GxsMsgReq messages_to_delete ; + + for(GxsMsgDataMap::const_iterator it(msgItems.begin());it!=msgItems.end();++it) + { + RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ +#ifdef DEBUG_CIRCLES + std::cerr << " Circle ID: " << it->first << std::endl; +#endif + // Find the circle ID in cache and process the list of messages to keep the latest order in time. + + RsGxsCircleId circle_id(it->first); + RsGxsCircleCache& cache( mCircleCache[circle_id] ); + +#ifdef DEBUG_CIRCLES + std::cerr << " Circle found in cache!" << std::endl; + std::cerr << " Retrieving messages..." << std::endl; +#endif + + for(uint32_t i=0;isecond.size();++i) + { +#ifdef DEBUG_CIRCLES + std::cerr << " Group ID: " << it->second[i]->meta.mGroupId << ", Message ID: " << it->second[i]->meta.mMsgId << ", thread ID: " << it->second[i]->meta.mThreadId << ", author: " << it->second[i]->meta.mAuthorId << ": " ; +#endif + + RsGxsCircleSubscriptionRequestItem *item = dynamic_cast(it->second[i]) ; + + if(item == NULL) + { + std::cerr << " (EE) item is not a RsGxsCircleSubscriptionRequestItem. Weird. Scheduling for deletion." << std::endl; + + messages_to_delete[RsGxsGroupId(circle_id)].insert(it->second[i]->meta.mMsgId); + continue ; + } + + RsGxsCircleMembershipStatus& info(cache.mMembershipStatus[item->meta.mAuthorId]) ; + +#ifdef DEBUG_CIRCLES + std::cerr << " " << time(NULL) - item->time_stamp << " seconds ago, " ; +#endif + + if(info.last_subscription_TS <= item->time_stamp) // the <= here allows to make sure we update the flags is something happenned + { + info.last_subscription_TS = item->time_stamp ; + + if(item->subscription_type == RsGxsCircleSubscriptionType::SUBSCRIBE) + info.subscription_flags |= GXS_EXTERNAL_CIRCLE_FLAGS_SUBSCRIBED; + else if(item->subscription_type == RsGxsCircleSubscriptionType::UNSUBSCRIBE) + info.subscription_flags &= ~GXS_EXTERNAL_CIRCLE_FLAGS_SUBSCRIBED; + else + std::cerr << " (EE) unknown subscription order type: " << static_cast(item->subscription_type) ; + + mShouldSendCacheUpdateNotification = true; +#ifdef DEBUG_CIRCLES + std::cerr << " UPDATING status to " << std::hex << info.subscription_flags << std::dec << std::endl; +#endif + } + else if(info.last_subscription_TS > item->time_stamp) + std::cerr << " Too old: item->TS=" << item->time_stamp << ", last_subscription_TS=" << info.last_subscription_TS << ". IGNORING." << std::endl; + } + + // now do another sweep and remove all msgs that are older than the latest + +#ifdef DEBUG_CIRCLES + std::cerr << " Cleaning old messages..." << std::endl; +#endif + + for(uint32_t i=0;isecond.size();++i) + { + RsGxsCircleMembershipStatus& info(cache.mMembershipStatus[it->second[i]->meta.mAuthorId]) ; + RsGxsCircleSubscriptionRequestItem *item = dynamic_cast(it->second[i]) ; + + if(item && info.last_subscription_TS > item->time_stamp) + { +#ifdef DEBUG_CIRCLES + std::cerr << " " << item->meta.mMsgId << ": Older than last known (" << (long int)info.last_subscription_TS - (long int)item->time_stamp << " seconds before): deleting." << std::endl; +#endif + messages_to_delete[RsGxsGroupId(circle_id)].insert(item->meta.mMsgId) ; + } + } + + cache.mLastUpdatedMembershipTS = time(NULL) ; + cache.mStatus = CircleEntryCacheStatus::UP_TO_DATE; + cache.mLastUpdateTime = time(NULL); + mShouldSendCacheUpdateNotification = true; + } + + RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ + uint32_t token2; + RsGenExchange::deleteMsgs(token2,messages_to_delete); + + return true ; +} + +//====================================================================================// +// DEBUG STUFF // +//====================================================================================// + +bool p3GxsCircles::debug_dumpCacheEntry(RsGxsCircleCache& cache) +{ + std::cerr << " Circle: " << cache.mCircleId << " status: " << cache.mStatus << " MembershipTS: " << cache.mLastUpdatedMembershipTS + << " UpdateTS: " << cache.mLastUpdateTime << " All Ids here: " << cache.mAllIdsHere << std::endl; + + return true; +} + +void p3GxsCircles::debug_dumpCache() +{ + std::cerr << "Debug dump of CircleCache:" << std::endl; + + mCircleCache.printStats(); + mCircleCache.applyToAllCachedEntries(*this,&p3GxsCircles::debug_dumpCacheEntry); +} + +std::string p3GxsCircles::genRandomId() +{ + std::string randomId; + for(int i = 0; i < 20; i++) + { + randomId += (char) ('a' + (RSRandom::random_u32() % 26)); + } + + return randomId; +} + +void p3GxsCircles::generateDummyData() +{ + // request Id Data... +#ifdef DEBUG_CIRCLES + std::cerr << "p3GxsCircles::generateDummyData() getting Id List"; + std::cerr << std::endl; +#endif // DEBUG_CIRCLES + + uint32_t ansType = RS_TOKREQ_ANSTYPE_DATA; + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; + + uint32_t token; + rsIdentity->getTokenService()->requestGroupInfo(token, ansType, opts); + + { + RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ + mDummyIdToken = token; + } + + RsTickEvent::schedule_in(CIRCLE_EVENT_DUMMYLOAD, CIRCLE_DUMMY_GENPERIOD); +} + + +void p3GxsCircles::checkDummyIdData() +{ +#ifdef DEBUG_CIRCLES + std::cerr << "p3GxsCircles::checkDummyIdData()"; + std::cerr << std::endl; +#endif // DEBUG_CIRCLES + + // check the token. + uint32_t status = rsIdentity->getTokenService()->requestStatus(mDummyIdToken); + if ( (RsTokenService::FAILED == status) || + (RsTokenService::COMPLETE == status) ) + { + std::vector ids; + if (!rsIdentity->getGroupData(mDummyIdToken, ids)) + { + std::cerr << "p3GxsCircles::checkDummyIdData() ERROR getting data"; + std::cerr << std::endl; + /* error */ + return; + } + + std::vector::iterator it; + for(it = ids.begin(); it != ids.end(); ++it) + { + if (it->mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID_kept_for_compatibility) + { +#ifdef DEBUG_CIRCLES + std::cerr << "p3GxsCircles::checkDummyIdData() PgpLinkedId: " << it->mMeta.mGroupId; + std::cerr << std::endl; +#endif // DEBUG_CIRCLES + mDummyPgpLinkedIds.push_back(RsGxsId(it->mMeta.mGroupId.toStdString())); + + if (it->mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN) + { +#ifdef DEBUG_CIRCLES + std::cerr << "p3GxsCircles::checkDummyIdData() OwnId: " << it->mMeta.mGroupId; + std::cerr << std::endl; +#endif // DEBUG_CIRCLES + mDummyOwnIds.push_back(RsGxsId(it->mMeta.mGroupId.toStdString())); + } + } + else + { +#ifdef DEBUG_CIRCLES + std::cerr << "p3GxsCircles::checkDummyIdData() Other Id: " << it->mMeta.mGroupId; + std::cerr << std::endl; +#endif // DEBUG_CIRCLES + } + } + + /* schedule the generate events */ +#define MAX_CIRCLES 10 + for(int i = 0; i < MAX_CIRCLES; i++) + { + RsTickEvent::schedule_in(CIRCLE_EVENT_DUMMYGEN, i * CIRCLE_DUMMY_GENPERIOD); + } + return; + } + + // Otherwise - reschedule to come back here. + RsTickEvent::schedule_in(CIRCLE_EVENT_DUMMYLOAD, CIRCLE_DUMMY_GENPERIOD); + return; +} + + +void p3GxsCircles::generateDummyCircle() +{ +#ifdef DEBUG_CIRCLES + std::cerr << "p3GxsCircles::generateDummyCircle()"; + std::cerr << std::endl; +#endif // DEBUG_CIRCLES + + int npgps = mDummyPgpLinkedIds.size(); + + if(npgps == 0) + return ; + + RsGxsCircleGroup group; + + std::set idset; + // select a random number of them. +#define MAX_PEERS_PER_CIRCLE_GROUP 20 + int nIds = 1 + (RSRandom::random_u32() % MAX_PEERS_PER_CIRCLE_GROUP); + for(int i = 0; i < nIds; i++) + { + + int selection = (RSRandom::random_u32() % npgps); + std::list::iterator it = mDummyPgpLinkedIds.begin(); + for(int j = 0; (it != mDummyPgpLinkedIds.end()) && (j < selection); j++, ++it) ; + if (it != mDummyPgpLinkedIds.end()) + { + idset.insert(*it); + } + } + + /* be sure to add one of our IDs too (otherwise we wouldn't get the group) + */ + { + + int selection = (RSRandom::random_u32() % mDummyOwnIds.size()); + std::list::iterator it = mDummyOwnIds.begin(); + mDummyOwnIds.push_back(*it); + for(int j = 0; (it != mDummyOwnIds.end()) && (j < selection); j++, ++it) ; + if (it != mDummyOwnIds.end()) + { + idset.insert(*it); + } + } + + group.mMeta.mGroupName = genRandomId(); +#ifdef DEBUG_CIRCLES + std::cerr << "p3GxsCircles::generateDummyCircle() Name: " << group.mMeta.mGroupName; + std::cerr << std::endl; +#endif // DEBUG_CIRCLES + + std::set::iterator it; + for(it = idset.begin(); it != idset.end(); ++it) + { + group.mInvitedMembers.insert(*it); +#ifdef DEBUG_CIRCLES + std::cerr << "p3GxsCircles::generateDummyCircle() Adding: " << *it; + std::cerr << std::endl; +#endif // DEBUG_CIRCLES + } + + uint32_t dummyToken; + createGroup(dummyToken, group); +} + + /****************************************************************************/ // ID STUFF. \/ \/ \/ \/ \/ \/ \/ :) /****************************************************************************/ @@ -1124,13 +2228,13 @@ bool p3GxsCircles::cachetest_getlist() std::cerr << "p3GxsCircles::cachetest_getlist() making request"; std::cerr << std::endl; - uint32_t ansType = RS_TOKREQ_ANSTYPE_LIST; + uint32_t ansType = RS_TOKREQ_ANSTYPE_LIST; RsTokReqOptions opts; opts.mReqType = GXS_REQUEST_TYPE_GROUP_IDS; uint32_t token = 0; - + RsGenExchange::getTokenService()->requestGroupInfo(token, ansType, opts); - GxsTokenQueue::queueRequest(token, CIRCLEREQ_CACHETEST); + GxsTokenQueue::queueRequest(token, CIRCLEREQ_CACHETEST); // Schedule Next Event. RsTickEvent::schedule_in(CIRCLE_EVENT_CACHETEST, CACHETEST_PERIOD); @@ -1226,544 +2330,6 @@ bool p3GxsCircles::cachetest_handlerequest(uint32_t token) /****************************************************************************/ #endif - - -/************************************************************************************/ -/************************************************************************************/ -/************************************************************************************/ -// Complicated deal of loading Circles. - -bool p3GxsCircles::force_cache_reload(const RsGxsCircleId& id) -{ -#ifdef DEBUG_CIRCLES - std::cerr << "p3GxsCircles::force_cache_reload(): Forcing cache reload of Circle ID " << id << std::endl; -#endif - - { - RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ - -#ifdef DEBUG_CIRCLES - std::cerr << " clearing from existing cache entries..." << std::endl; -#endif - - std::map::iterator it = mLoadingCache.find(id); - if (it != mLoadingCache.end()) - { - mLoadingCache.erase(it) ; -#ifdef DEBUG_CIRCLES - std::cerr << " removed item from currently loading cache entries..." << std::endl; -#endif - } - mCircleCache.erase(id) ; - } - cache_request_load(id) ; - - return true ; -} - -bool p3GxsCircles::cache_request_load(const RsGxsCircleId &id) -{ -#ifdef DEBUG_CIRCLES - std::cerr << "p3GxsCircles::cache_request_load(" << id << ")"; - std::cerr << std::endl; -#endif // DEBUG_CIRCLES - - { - RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ - - /* check its not loaded */ - if (mCircleCache.is_cached(id)) - return true; - - /* check it is not already being loaded */ - std::map::iterator it; - it = mLoadingCache.find(id); - if (it != mLoadingCache.end()) - { - // Already loading. - return true; - } - // Put it into the Loading Cache - so we will detect it later. - mLoadingCache[id] = RsGxsCircleCache(); - mCacheLoad_ToCache.push_back(id); - } - - if (RsTickEvent::event_count(CIRCLE_EVENT_CACHELOAD) > 0) - { - /* its already scheduled */ - return true; - } - - int32_t age = 0; - if (RsTickEvent::prev_event_ago(CIRCLE_EVENT_CACHELOAD, age)) - { - if (age < MIN_CIRCLE_LOAD_GAP) - { - RsTickEvent::schedule_in(CIRCLE_EVENT_CACHELOAD, MIN_CIRCLE_LOAD_GAP - age); - return true; - } - } - - RsTickEvent::schedule_now(CIRCLE_EVENT_CACHELOAD); - return true; -} - - -bool p3GxsCircles::cache_start_load() -{ -#ifdef DEBUG_CIRCLES - std::cerr << "p3GxsCircles::cache_start_load()"; - std::cerr << std::endl; -#endif // DEBUG_CIRCLESmatch - - /* trigger request to load missing ids into cache */ - std::list groupIds; - { - RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ - - /* now we process the modGroupList -> a map so we can use it easily later, and create id list too */ - std::list::iterator it; - for(it = mCacheLoad_ToCache.begin(); it != mCacheLoad_ToCache.end(); ++it) - { -#ifdef DEBUG_CIRCLES - std::cerr << "p3GxsCircles::cache_start_load() GroupId: " << *it; - std::cerr << std::endl; -#endif // DEBUG_CIRCLES - - groupIds.push_back(RsGxsGroupId(it->toStdString())); // might need conversion? - } - - mCacheLoad_ToCache.clear(); - } - - if (groupIds.size() > 0) - { -#ifdef DEBUG_CIRCLES - std::cerr << "p3GxsCircles::cache_start_load() #Groups: " << groupIds.size(); - std::cerr << std::endl; -#endif // DEBUG_CIRCLES - - uint32_t ansType = RS_TOKREQ_ANSTYPE_DATA; - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; - uint32_t token = 0; - - RsGenExchange::getTokenService()->requestGroupInfo(token, ansType, opts, groupIds); - GxsTokenQueue::queueRequest(token, CIRCLEREQ_CACHELOAD); - } - return 1; -} - - -bool p3GxsCircles::cache_load_for_token(uint32_t token) -{ -#ifdef DEBUG_CIRCLES - std::cerr << "p3GxsCircles::cache_load_for_token() : " << token << std::endl; -#endif // DEBUG_CIRCLES - - std::vector grpData; - - if(!RsGenExchange::getGroupData(token, grpData)) - { - std::cerr << "p3GxsCircles::cache_load_for_token() ERROR no data"; - std::cerr << std::endl; - - return false; - } - - std::vector::iterator vit = grpData.begin(); - - for(; vit != grpData.end(); ++vit) - { - RsGxsCircleGroupItem *item = dynamic_cast(*vit); - if (!item) - { - std::cerr << " Not a RsGxsCircleGroupItem Item, deleting!" << std::endl; - delete(*vit); - continue; - } - RsGxsCircleGroup group; - item->convertTo(group); - -#ifdef DEBUG_CIRCLES - std::cerr << " Loaded Id with Meta: " << item->meta << std::endl; -#endif // DEBUG_CIRCLES - - - RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ - - /* should already have a LoadingCache entry */ - RsGxsCircleId id = RsGxsCircleId(item->meta.mGroupId) ; - - // (cyril) I'm not sure this logic is needed. The token system would avoid duplicates normally. - - std::map::iterator it = mLoadingCache.find(id); - - if (it == mLoadingCache.end()) - { - std::cerr << "p3GxsCircles::cache_load_for_token() Load ERROR: "; - std::cerr << item->meta; - std::cerr << std::endl; - delete(item); - // ERROR. - continue; - } - - RsGxsCircleCache& cache = it->second; - - cache.loadBaseCircle(group); - delete item; - - if(locked_processLoadingCacheEntry(it->second)) - { -#ifdef DEBUG_CIRCLES - std::cerr << " All peers available. Moving to cache..." << std::endl; -#endif - mLoadingCache.erase(it); - } - else - { -#ifdef DEBUG_CIRCLES - std::cerr << " Unprocessed peers. Requesting reload..." << std::endl; -#endif - - /* schedule event to try reload gxsIds */ - RsTickEvent::schedule_in(CIRCLE_EVENT_RELOADIDS, GXSID_LOAD_CYCLE, id.toStdString()); - } - - } - - return true; -} - -// This method parses the cache entry and makes sure that all ids are known. If not, requests the missing ids -// when done, the entry is removed from mLoadingCache - -bool p3GxsCircles::locked_processLoadingCacheEntry(RsGxsCircleCache& cache) -{ - //bool isUnprocessedPeers = false; - - if (cache.mIsExternal) - { -#ifdef DEBUG_CIRCLES - std::cerr << "Processing External Circle " << cache.mCircleId << std::endl; -#endif - - // Do we actually need to retrieve the missing keys for all members of a circle??? - // These keys are needed for subscribtion request signature checking. But this is only - // when a subscription msg is posted, which would trigger retrieval of the key anyway - // Maybe this can be made an option of p3GxsCircles, or of rsIdentity. - - // need to trigger the searches. - for(std::map::iterator pit = cache.mMembershipStatus.begin(); pit != cache.mMembershipStatus.end(); ++pit) - { -#ifdef DEBUG_CIRCLES - std::cerr << " Member status: " << pit->first << " : " << pit->second.subscription_flags; -#endif - - /* check cache */ - if(!(pit->second.subscription_flags & GXS_EXTERNAL_CIRCLE_FLAGS_KEY_AVAILABLE)) - { - if(mIdentities->haveKey(pit->first)) - { - pit->second.subscription_flags |= GXS_EXTERNAL_CIRCLE_FLAGS_KEY_AVAILABLE; -#ifdef DEBUG_CIRCLES - std::cerr << " Key is now available!"<< std::endl; -#endif - } - else - { - std::list peers; - - if(!cache.mOriginator.isNull()) - { - peers.push_back(cache.mOriginator) ; -#ifdef DEBUG_CIRCLES - std::cerr << " Requesting unknown/unloaded identity: " << pit->first << " to originator " << cache.mOriginator << std::endl; -#endif - } - else - { - std::cerr << " (WW) cache entry for circle " << cache.mCircleId << " has empty originator. Asking info for GXS id " << pit->first << " to all connected friends." << std::endl; - - rsPeers->getOnlineList(peers) ; - } - - mIdentities->requestKey(pit->first, peers,RsIdentityUsage(serviceType(),RsIdentityUsage::CIRCLE_MEMBERSHIP_CHECK,RsGxsGroupId(cache.mCircleId))); - //isUnprocessedPeers = true; - } - } -#ifdef DEBUG_CIRCLES - else - std::cerr << " Key is available. Nothing to process." << std::endl; -#endif - } - -#ifdef HANDLE_SUBCIRCLES -#if 0 - std::list &circles = group.mSubCircles; - std::list::const_iterator cit; - for(cit = circles.begin(); cit != circles.end(); ++cit) - { - /* if its cached already -> then its complete. */ - if (mCircleCache.is_loaded(*cit)) - { - RsGxsCircleCache cachedCircle; - if (mCircleCache.fetch(&cit, cachedCircle)) - { - /* copy cached circle into circle */ - cache.loadSubCircle(cachedCircle); - } - else - { - /* error */ - continue; - } - } - else - { - /* push into secondary processing queues */ - std::list &proc_circles = mCacheLoad_SubCircle[*cit]; - proc_circles.push_back(id); - - subCirclesToLoad.push_back(*cit); - - isComplete = false; - isUnprocessedCircles = true; - } - } -#endif -#endif - } -#ifdef DEBUG_CIRCLES - else - std::cerr << " Loading Personal Circle" << std::endl; -#endif - - // We can check for self inclusion in the circle right away, since own ids are always loaded. - // that allows to subscribe/unsubscribe uncomplete circles - - locked_checkCircleCacheForAutoSubscribe(cache); - locked_checkCircleCacheForMembershipUpdate(cache); - - // always store in cache even if uncomplete. But do not remove the loading items so that they can be kept in loading state. -// if(isUnprocessedPeers) - // return false ; - - /* move straight into the cache */ - mCircleCache.store(cache.mCircleId, cache); - mCircleCache.resize(); - - std::cerr << " Loading complete." << std::endl; - - return true ; -} - -bool p3GxsCircles::cache_reloadids(const RsGxsCircleId &circleId) -{ -#ifdef DEBUG_CIRCLES - std::cerr << "p3GxsCircles::cache_reloadids()"; - std::cerr << std::endl; -#endif // DEBUG_CIRCLES - - RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ - - /* fetch from loadMap */ - std::map::iterator it; - it = mLoadingCache.find(circleId); - if (it == mLoadingCache.end()) - { - std::cerr << "p3GxsCircles::cache_reloadids() ERROR Id: " << circleId; - std::cerr << " Not in mLoadingCache Map"; - std::cerr << std::endl; - - // ERROR - return false; - } - - if(locked_processLoadingCacheEntry(it->second)) - { - /* remove from loading queue */ - mLoadingCache.erase(it); - } - else - { -#ifdef DEBUG_CIRCLES - std::cerr << " Unprocessed peers. Requesting reload for circle " << circleId << std::endl; -#endif - - /* schedule event to try reload gxsIds */ - RsTickEvent::schedule_in(CIRCLE_EVENT_RELOADIDS, GXSID_LOAD_CYCLE, circleId.toStdString()); - } - - return true; -} - -bool p3GxsCircles::checkCircleCache() -{ -#ifdef DEBUG_CIRCLES - std::cerr << "checkCircleCache(): calling auto-subscribe check and membership update check." << std::endl; -#endif - RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ - - mCircleCache.applyToAllCachedEntries(*this,&p3GxsCircles::locked_checkCircleCacheForAutoSubscribe) ; -// mCircleCache.applyToAllCachedEntries(*this,&p3GxsCircles::locked_checkCircleCacheForMembershipUpdate) ; - - return true ; -} - -bool p3GxsCircles::locked_checkCircleCacheForMembershipUpdate(RsGxsCircleCache& cache) -{ - rstime_t now = time(NULL) ; - - if(cache.mLastUpdatedMembershipTS + GXS_CIRCLE_DELAY_TO_FORCE_MEMBERSHIP_UPDATE < now) - { -#ifdef DEBUG_CIRCLES - std::cerr << "Cache entry for circle " << cache.mCircleId << " needs a swab over membership requests. Re-scheduling it." << std::endl; -#endif - - // this should be called regularly - - uint32_t token ; - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; - std::list grpIds ; - - grpIds.push_back(RsGxsGroupId(cache.mCircleId)) ; - - RsGenExchange::getTokenService()->requestMsgInfo(token, RS_TOKREQ_ANSTYPE_SUMMARY, opts, grpIds); - GxsTokenQueue::queueRequest(token, CIRCLEREQ_MESSAGE_DATA); - } - return true ; -} - -/* We need to AutoSubscribe if the Circle is relevent to us */ - -bool p3GxsCircles::locked_checkCircleCacheForAutoSubscribe(RsGxsCircleCache &cache) -{ -#ifdef DEBUG_CIRCLES - std::cerr << "p3GxsCircles::locked_checkCircleCacheForAutoSubscribe() : "<< cache.mCircleId << std::endl; -#endif - - /* if processed already - ignore */ - if (!(cache.mGroupStatus & GXS_SERV::GXS_GRP_STATUS_UNPROCESSED)) - { -#ifdef DEBUG_CIRCLES - std::cerr << " Already Processed" << std::endl; -#endif - - return false; - } - - /* if personal - we created ... is subscribed already */ - if (!cache.mIsExternal) - { -#ifdef DEBUG_CIRCLES - std::cerr << " Personal Circle. Nothing to do." << std::endl; -#endif - - return false; - } - - /* if we appear in the group - then autosubscribe, and mark as processed. This also applies if we're the group admin */ - - std::list myOwnIds; - - if(!rsIdentity->getOwnIds(myOwnIds)) - { - std::cerr << " own ids not loaded yet." << std::endl; - - /* schedule event to try reload gxsIds */ - RsTickEvent::schedule_in(CIRCLE_EVENT_RELOADIDS, GXSID_LOAD_CYCLE, cache.mCircleId.toStdString()); - return false ; - } - - bool in_admin_list = false ; - bool member_request = false ; - - for(std::list::const_iterator it(myOwnIds.begin());it!=myOwnIds.end() && (!in_admin_list) && (!member_request);++it) - { - std::map::const_iterator it2 = cache.mMembershipStatus.find(*it) ; - - if(it2 != cache.mMembershipStatus.end()) - { - in_admin_list = in_admin_list || bool(it2->second.subscription_flags & GXS_EXTERNAL_CIRCLE_FLAGS_IN_ADMIN_LIST) ; - member_request= member_request|| bool(it2->second.subscription_flags & GXS_EXTERNAL_CIRCLE_FLAGS_SUBSCRIBED) ; - } - } - - bool am_I_admin( cache.mGroupSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN) ; - -#ifdef DEBUG_CIRCLES - std::cerr << " own ID in circle: " << in_admin_list << ", own subscribe request: " << member_request << ", am I admin?: " << am_I_admin << std::endl; -#endif - if(in_admin_list || member_request || am_I_admin) - { - uint32_t token, token2; - - if(! (cache.mGroupSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_SUBSCRIBED)) - { -#ifdef DEBUG_CIRCLES - /* we are part of this group - subscribe, clear unprocessed flag */ - std::cerr << " I'm allowed in this circle => AutoSubscribing!" << std::endl; -#endif - RsGenExchange::subscribeToGroup(token, RsGxsGroupId(cache.mCircleId), true); - } -#ifdef DEBUG_CIRCLES - else - std::cerr << " I'm allowed in this circle, and already subscribed." << std::endl; -#endif - - RsGenExchange::setGroupStatusFlags(token2, RsGxsGroupId(cache.mCircleId), 0, GXS_SERV::GXS_GRP_STATUS_UNPROCESSED); - - cache.mGroupStatus &= ~GXS_SERV::GXS_GRP_STATUS_UNPROCESSED; - - return true; - } - else - { - /* we know all the peers - we are not part - we can flag as PROCESSED. */ - uint32_t token,token2; - RsGenExchange::setGroupStatusFlags(token, RsGxsGroupId(cache.mCircleId.toStdString()), 0, GXS_SERV::GXS_GRP_STATUS_UNPROCESSED); - - if(cache.mGroupSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_SUBSCRIBED) - { - RsGenExchange::subscribeToGroup(token2, RsGxsGroupId(cache.mCircleId), false); -#ifdef DEBUG_CIRCLES - std::cerr << " Not part of the group! Let's unsubscribe this circle of unfriendly Napoleons!" << std::endl; -#endif - } -#ifdef DEBUG_CIRCLES - else - std::cerr << " Not part of the group, and not subscribed either." << std::endl; -#endif - - cache.mGroupStatus &= ~GXS_SERV::GXS_GRP_STATUS_UNPROCESSED; - - return true ; - } -} - -void p3GxsCircles::addCircleIdToList(const RsGxsCircleId &circleId, uint32_t circleType) -{ - RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ - - if (circleType == GXS_CIRCLE_TYPE_LOCAL) - { - if(mCirclePersonalIdList.end() == std::find(mCirclePersonalIdList.begin(), mCirclePersonalIdList.end(), circleId)){ - mCirclePersonalIdList.push_back(circleId); - } - } - else - { - if(mCircleExternalIdList.end() == std::find(mCircleExternalIdList.begin(), mCircleExternalIdList.end(), circleId)){ - mCircleExternalIdList.push_back(circleId); - } - } -} - - - #ifdef HANDLE_SUBCIRCLES #if 0 /**** TODO BELOW ****/ @@ -1845,7 +2411,7 @@ bool p3GxsCircles::cache_load_subcircles(uint32_t token) std::map::iterator it; it = mLoadingCache.find(id); } - + } else { @@ -1938,495 +2504,38 @@ bool p3GxsCircles::cache_load_subcircles(uint32_t token) #endif #endif - -/************************************************************************************/ -/************************************************************************************/ -/************************************************************************************/ - -std::string p3GxsCircles::genRandomId() -{ - std::string randomId; - for(int i = 0; i < 20; i++) +#ifdef HANDLE_SUBCIRCLES +#if 0 + std::list &circles = group.mSubCircles; + std::list::const_iterator cit; + for(cit = circles.begin(); cit != circles.end(); ++cit) { - randomId += (char) ('a' + (RSRandom::random_u32() % 26)); - } - - return randomId; -} - -void p3GxsCircles::generateDummyData() -{ - // request Id Data... -#ifdef DEBUG_CIRCLES - std::cerr << "p3GxsCircles::generateDummyData() getting Id List"; - std::cerr << std::endl; -#endif // DEBUG_CIRCLES - - uint32_t ansType = RS_TOKREQ_ANSTYPE_DATA; - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; - - uint32_t token; - rsIdentity->getTokenService()->requestGroupInfo(token, ansType, opts); - - { - RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ - mDummyIdToken = token; - } - - RsTickEvent::schedule_in(CIRCLE_EVENT_DUMMYLOAD, CIRCLE_DUMMY_GENPERIOD); -} - - -void p3GxsCircles::checkDummyIdData() -{ -#ifdef DEBUG_CIRCLES - std::cerr << "p3GxsCircles::checkDummyIdData()"; - std::cerr << std::endl; -#endif // DEBUG_CIRCLES - - // check the token. - uint32_t status = rsIdentity->getTokenService()->requestStatus(mDummyIdToken); - if ( (RsTokenService::FAILED == status) || - (RsTokenService::COMPLETE == status) ) - { - std::vector ids; - if (!rsIdentity->getGroupData(mDummyIdToken, ids)) + /* if its cached already -> then its complete. */ + if (mCircleCache.is_loaded(*cit)) { - std::cerr << "p3GxsCircles::checkDummyIdData() ERROR getting data"; - std::cerr << std::endl; - /* error */ - return; - } - - std::vector::iterator it; - for(it = ids.begin(); it != ids.end(); ++it) - { - if (it->mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID_kept_for_compatibility) + RsGxsCircleCache cachedCircle; + if (mCircleCache.fetch(&cit, cachedCircle)) { -#ifdef DEBUG_CIRCLES - std::cerr << "p3GxsCircles::checkDummyIdData() PgpLinkedId: " << it->mMeta.mGroupId; - std::cerr << std::endl; -#endif // DEBUG_CIRCLES - mDummyPgpLinkedIds.push_back(RsGxsId(it->mMeta.mGroupId.toStdString())); - - if (it->mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN) - { -#ifdef DEBUG_CIRCLES - std::cerr << "p3GxsCircles::checkDummyIdData() OwnId: " << it->mMeta.mGroupId; - std::cerr << std::endl; -#endif // DEBUG_CIRCLES - mDummyOwnIds.push_back(RsGxsId(it->mMeta.mGroupId.toStdString())); - } + /* copy cached circle into circle */ + cache.loadSubCircle(cachedCircle); } else { -#ifdef DEBUG_CIRCLES - std::cerr << "p3GxsCircles::checkDummyIdData() Other Id: " << it->mMeta.mGroupId; - std::cerr << std::endl; -#endif // DEBUG_CIRCLES + /* error */ + continue; } } - - /* schedule the generate events */ -#define MAX_CIRCLES 10 - for(int i = 0; i < MAX_CIRCLES; i++) + else { - RsTickEvent::schedule_in(CIRCLE_EVENT_DUMMYGEN, i * CIRCLE_DUMMY_GENPERIOD); - } - return; - } + /* push into secondary processing queues */ + std::list &proc_circles = mCacheLoad_SubCircle[*cit]; + proc_circles.push_back(id); - // Otherwise - reschedule to come back here. - RsTickEvent::schedule_in(CIRCLE_EVENT_DUMMYLOAD, CIRCLE_DUMMY_GENPERIOD); - return; -} + subCirclesToLoad.push_back(*cit); - -void p3GxsCircles::generateDummyCircle() -{ -#ifdef DEBUG_CIRCLES - std::cerr << "p3GxsCircles::generateDummyCircle()"; - std::cerr << std::endl; -#endif // DEBUG_CIRCLES - - int npgps = mDummyPgpLinkedIds.size(); - - if(npgps == 0) - return ; - - RsGxsCircleGroup group; - - std::set idset; - // select a random number of them. -#define MAX_PEERS_PER_CIRCLE_GROUP 20 - int nIds = 1 + (RSRandom::random_u32() % MAX_PEERS_PER_CIRCLE_GROUP); - for(int i = 0; i < nIds; i++) - { - - int selection = (RSRandom::random_u32() % npgps); - std::list::iterator it = mDummyPgpLinkedIds.begin(); - for(int j = 0; (it != mDummyPgpLinkedIds.end()) && (j < selection); j++, ++it) ; - if (it != mDummyPgpLinkedIds.end()) - { - idset.insert(*it); + isComplete = false; + isUnprocessedCircles = true; } } - - /* be sure to add one of our IDs too (otherwise we wouldn't get the group) - */ - { - - int selection = (RSRandom::random_u32() % mDummyOwnIds.size()); - std::list::iterator it = mDummyOwnIds.begin(); - mDummyOwnIds.push_back(*it); - for(int j = 0; (it != mDummyOwnIds.end()) && (j < selection); j++, ++it) ; - if (it != mDummyOwnIds.end()) - { - idset.insert(*it); - } - } - - group.mMeta.mGroupName = genRandomId(); -#ifdef DEBUG_CIRCLES - std::cerr << "p3GxsCircles::generateDummyCircle() Name: " << group.mMeta.mGroupName; - std::cerr << std::endl; -#endif // DEBUG_CIRCLES - - std::set::iterator it; - for(it = idset.begin(); it != idset.end(); ++it) - { - group.mInvitedMembers.insert(*it); -#ifdef DEBUG_CIRCLES - std::cerr << "p3GxsCircles::generateDummyCircle() Adding: " << *it; - std::cerr << std::endl; -#endif // DEBUG_CIRCLES - } - - uint32_t dummyToken; - createGroup(dummyToken, group); -} - - // Overloaded from GxsTokenQueue for Request callbacks. -void p3GxsCircles::handleResponse(uint32_t token, uint32_t req_type) -{ -#ifdef DEBUG_CIRCLES - std::cerr << "p3GxsCircles::handleResponse(" << token << "," << req_type << ")"; - std::cerr << std::endl; -#endif // DEBUG_CIRCLES - - // stuff. - switch(req_type) - { - case CIRCLEREQ_CIRCLE_LIST: - load_CircleIdList(token); - break; - - case CIRCLEREQ_MESSAGE_DATA: - processMembershipRequests(token); - break; - - case CIRCLEREQ_CACHELOAD: - cache_load_for_token(token); - break; - default: - RsErr() << __PRETTY_FUNCTION__ << " Unknown Request Type: " - << req_type << std::endl; - break; - } -} - - // Overloaded from RsTickEvent for Event callbacks. -void p3GxsCircles::handle_event(uint32_t event_type, const std::string &elabel) -{ -#ifdef DEBUG_CIRCLES - std::cerr << "p3GxsCircles::handle_event(" << event_type << ")"; - std::cerr << std::endl; -#endif // DEBUG_CIRCLES - - // stuff. - switch(event_type) - { - case CIRCLE_EVENT_LOADIDS: - request_CircleIdList(); - break; - - case CIRCLE_EVENT_CACHELOAD: - cache_start_load(); - break; - - case CIRCLE_EVENT_RELOADIDS: - cache_reloadids(RsGxsCircleId(elabel)); - break; - - case CIRCLE_EVENT_DUMMYSTART: - generateDummyData(); - break; - - case CIRCLE_EVENT_DUMMYLOAD: - checkDummyIdData(); - break; - - case CIRCLE_EVENT_DUMMYGEN: - generateDummyCircle(); - break; - - default: - RsErr() << __PRETTY_FUNCTION__ << " Unknown Event Type: " << event_type - << std::endl; - break; - } -} - -// Circle membership is requested/denied by posting a message into the cicle group, according to the following rules: -// -// - a subscription request is a RsItem (which serialises into a radix64 message, that is further signed by the group message publishing system) -// The item contains: -// * subscribe order (yes/no), boolean -// * circle ID (this is important, otherwise people can copy subscribe messages from one circle to another) -// * subscribe date -// * subscribe timeout (how long is the message kept. When timed out, the message is removed and subscription cancelled) -// -// - subscribe messages follow the following rules, which are enforced by a timer-based method: -// * subscription requests from a given user are always replaced by the last subscription request -// * a complete list of who's subscribed to a given group is kept, saved, and regularly updated when new subscribe messages are received, or when admin list is changed. -// * getGroupDetails reads this list in order to respond who's subscribed to a group. The list of -// -// - compatibility with self-restricted circles: -// * subscription should be based on admin list, so that non subscribed peers still receive the invitation -// -// - two possible subscription models for circle member list (Restricted forums only propagate to members): -// 1 - list of admin who have not opposed subscription -// - solves propagation issue. Only admin see data. They can however unsubscribe using a negative req. Admin needs to remove them. -// - bad for security. Admin can refuse to remove them => back to square one -// 2 - list of admin who have also requested membership -// - propagation is ok. On restricted circle, the circle msgs/group should be sent to admin list, instead of member list. -// - solves membership issue since people need to actively be in the group. -// => choose 2 -// - forum group : encrypted for Member list -// - circle group : clear / encrypted for admin list (for self-restricted) -// We decide between the two by comparing the group we're sending and the circle id it is restricted to. -// -// - Use cases -// * user sees group (not self restricted) and requests to subscribe => RS subscribes the group and the user can propagate the response -// * user is invited to self-restricted circle. He will see it and can subscribe, so he will be in admin list and receive e.g. forum posts. -// * -// -// - Threat model -// * a malicious user forges a new subscription request: NP-hard as it needs to break the RSA key of the GXS id. -// * a malicious corrupts a subscription request: NP-hard. Messages are signed. -// * a malicious user copies an old subscription of someone else and inserts it in the system. -// => not possible. Either this existing old susbscription already exists, or it has been replaced by a more recent one, which -// will always replace the old one because of the date. -// * a malicious user removes someone's subscription messages. This is possible, but the mesh nature of the network will allow the message to propagate anyway. -// * a malicious user creates a circle with an incriminating name/content and adds everyone in it -// => people can oppose their membership in the circle using a msg -// -// -// - the table below summarizes the various choices: forum and circle propagation when restricted to a circle, and group subscribe to the circle -// -// +------------------------------+-----------------------------+ -// | User in admin list | User not in admin list | -// +-------------+------------------------------+-----------------------------+ -// | User request| Forum Grp/Msg: YES | Forum Grp/Msg: NO | -// | Subscription| Circle Grp/Msg: YES/YES | Circle Grp/Msg: YES/NO | -// | | Grp Subscribed: YES | Grp Subscribed: YES | -// +-------------+------------------------------+-----------------------------+ -// | No request | Forum Grp/Msg: NO | Forum Grp/Msg: NO | -// | Subscription| Circle Grp/Msg: YES/YES | Circle Grp/Msg: YES/NO | -// | | Grp Subscribed: NO | Grp Subscribed: NO | -// +-------------+------------------------------+-----------------------------+ - -bool p3GxsCircles::pushCircleMembershipRequest( - const RsGxsId& own_gxsid, const RsGxsCircleId& circle_id, - uint32_t request_type ) -{ - Dbg3() << __PRETTY_FUNCTION__ << "own_gxsid = " << own_gxsid - << ", circle=" << circle_id << ", req type=" << request_type - << std::endl; - - if( request_type != - RsGxsCircleSubscriptionRequestItem::SUBSCRIPTION_REQUEST_SUBSCRIBE && - request_type != - RsGxsCircleSubscriptionRequestItem::SUBSCRIPTION_REQUEST_UNSUBSCRIBE ) - { - RsErr() << __PRETTY_FUNCTION__ << " Unknown request type: " - << request_type << std::endl; - return false; - } - - if(!rsIdentity->isOwnId(own_gxsid)) - { - RsErr() << __PRETTY_FUNCTION__ << " Cannot generate membership request " - << "from not-own id: " << own_gxsid << std::endl; - return false; - } - - if(!getCirclesInfo( - std::list{static_cast(circle_id)}, - RS_DEFAULT_STORAGE_PARAM(std::vector) )) - { - RsErr() << __PRETTY_FUNCTION__ << " Cannot generate membership request " - << "from unknown circle: " << circle_id << std::endl; - return false; - } - - // Create a subscribe item - - RsGxsCircleSubscriptionRequestItem *s = new RsGxsCircleSubscriptionRequestItem ; - - s->time_stamp = time(NULL) ; - s->time_out = 0 ; // means never - s->subscription_type = request_type ; - - RsTemporaryMemory tmpmem(circle_id.serial_size() + own_gxsid.serial_size()) ; - - uint32_t off = 0 ; - circle_id.serialise(tmpmem,tmpmem.size(),off) ; - own_gxsid.serialise(tmpmem,tmpmem.size(),off) ; - - s->meta.mGroupId = RsGxsGroupId(circle_id) ; - s->meta.mMsgId.clear(); - s->meta.mThreadId = RsDirUtil::sha1sum(tmpmem,tmpmem.size()); // make the ID from the hash of the cirle ID and the author ID - s->meta.mAuthorId = own_gxsid; - - // msgItem->meta.mParentId = ; // leave these blank - // msgItem->meta.mOrigMsgId= ; - -#ifdef DEBUG_CIRCLES - std::cerr << "p3GxsCircles::publishSubscribeRequest()" << std::endl; - std::cerr << " GroupId : " << circle_id << std::endl; - std::cerr << " AuthorId : " << s->meta.mAuthorId << std::endl; - std::cerr << " ThreadId : " << s->meta.mThreadId << std::endl; #endif - uint32_t token ; - - if(request_type == RsGxsCircleSubscriptionRequestItem::SUBSCRIPTION_REQUEST_SUBSCRIBE) - RsGenExchange::subscribeToGroup(token, RsGxsGroupId(circle_id), true); - - RsGenExchange::publishMsg(token, s); - - // update the cache. - force_cache_reload(circle_id); - - return true; -} - -bool p3GxsCircles::requestCircleMembership(const RsGxsId& own_gxsid,const RsGxsCircleId& circle_id) -{ - return pushCircleMembershipRequest(own_gxsid,circle_id,RsGxsCircleSubscriptionRequestItem::SUBSCRIPTION_REQUEST_SUBSCRIBE) ; -} -bool p3GxsCircles::cancelCircleMembership(const RsGxsId& own_gxsid,const RsGxsCircleId& circle_id) -{ - return pushCircleMembershipRequest(own_gxsid,circle_id,RsGxsCircleSubscriptionRequestItem::SUBSCRIPTION_REQUEST_UNSUBSCRIBE) ; -} - - -bool p3GxsCircles::processMembershipRequests(uint32_t token) -{ - // Go through membership request messages and process them according to the following rule: - // * for each ID only keep the latest membership request. Delete the older ones. - // * for each circle, keep a list of IDs sorted into membership categories (e.g. keep updated flags for each IDs) - // Because msg loading is async-ed, the job in split in two methods: one calls the loading, the other one handles the loaded data. - -#ifdef DEBUG_CIRCLES - std::cerr << "Processing circle membership requests." << std::endl; #endif - t_RsGxsGenericDataTemporaryMapVector msgItems; - - if(!RsGenExchange::getMsgData(token, msgItems)) - { - std::cerr << "(EE) Cannot get msg data for circle. Something's weird." << std::endl; - return false; - } - - GxsMsgReq messages_to_delete ; - - for(GxsMsgDataMap::const_iterator it(msgItems.begin());it!=msgItems.end();++it) - { - RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ -#ifdef DEBUG_CIRCLES - std::cerr << " Circle ID: " << it->first << std::endl; -#endif - - RsGxsCircleId cid ( it->first ); - - if (!mCircleCache.is_cached(cid)) - { - std::cerr << " (EE) Circle is not in cache!" << std::endl; - continue ; - } - - // Find the circle ID in cache and process the list of messages to keep the latest order in time. - - RsGxsCircleCache& data = mCircleCache.ref(cid); -#ifdef DEBUG_CIRCLES - std::cerr << " Circle found in cache!" << std::endl; - std::cerr << " Retrieving messages..." << std::endl; -#endif - - for(uint32_t i=0;isecond.size();++i) - { -#ifdef DEBUG_CIRCLES - std::cerr << " Group ID: " << it->second[i]->meta.mGroupId << ", Message ID: " << it->second[i]->meta.mMsgId << ": " ; -#endif - - RsGxsCircleSubscriptionRequestItem *item = dynamic_cast(it->second[i]) ; - - if(item == NULL) - { - std::cerr << " (EE) item is not a RsGxsCircleSubscriptionRequestItem. Weird." << std::endl; - continue ; - } - - RsGxsCircleMembershipStatus& info(data.mMembershipStatus[item->meta.mAuthorId]) ; - -#ifdef DEBUG_CIRCLES - std::cerr << " is from id " << item->meta.mAuthorId << " " << time(NULL) - item->time_stamp << " seconds ago, " ; -#endif - - if(info.last_subscription_TS < item->time_stamp) - { - info.last_subscription_TS = item->time_stamp ; - - if(item->subscription_type == RsGxsCircleSubscriptionRequestItem::SUBSCRIPTION_REQUEST_SUBSCRIBE) - info.subscription_flags |= GXS_EXTERNAL_CIRCLE_FLAGS_SUBSCRIBED; - else if(item->subscription_type == RsGxsCircleSubscriptionRequestItem::SUBSCRIPTION_REQUEST_UNSUBSCRIBE) - info.subscription_flags &= ~GXS_EXTERNAL_CIRCLE_FLAGS_SUBSCRIBED; - else - std::cerr << " (EE) unknown subscription order type: " << item->subscription_type ; - -#ifdef DEBUG_CIRCLES - std::cerr << " UPDATING" << std::endl; -#endif - } - else if(info.last_subscription_TS > item->time_stamp) - { -#ifdef DEBUG_CIRCLES - std::cerr << " Older than last known (" << time(NULL)-info.last_subscription_TS << " seconds ago): deleting." << std::endl; -#endif - messages_to_delete[RsGxsGroupId(cid)].insert(it->second[i]->meta.mMsgId) ; - } - } - - data.mLastUpdatedMembershipTS = time(NULL) ; - } - - RsStackMutex stack(mCircleMtx); /********** STACK LOCKED MTX ******/ - uint32_t token2; - RsGenExchange::deleteMsgs(token2,messages_to_delete); - - return true ; -} - -/*static*/ const std::string RsGxsCircles::DEFAULT_CIRCLE_BASE_URL = - "retroshare:///circles"; -/*static*/ const std::string RsGxsCircles::CIRCLE_URL_NAME_FIELD = "circleName"; -/*static*/ const std::string RsGxsCircles::CIRCLE_URL_ID_FIELD = "circleId"; -/*static*/ const std::string RsGxsCircles::CIRCLE_URL_DATA_FIELD = "circleData"; - -RsGxsCircles::~RsGxsCircles() = default; -RsGxsCircleMsg::~RsGxsCircleMsg() = default; -RsGxsCircleDetails::~RsGxsCircleDetails() = default; -RsGxsCircleGroup::~RsGxsCircleGroup() = default; -RsGxsCircleEvent::~RsGxsCircleEvent() = default; diff --git a/libretroshare/src/services/p3gxscircles.h b/libretroshare/src/services/p3gxscircles.h index da93142cc..1b7986014 100644 --- a/libretroshare/src/services/p3gxscircles.h +++ b/libretroshare/src/services/p3gxscircles.h @@ -128,11 +128,20 @@ public: uint32_t subscription_flags ; // combination of GXS_EXTERNAL_CIRCLE_FLAGS_IN_ADMIN_LIST and GXS_EXTERNAL_CIRCLE_FLAGS_SUBSCRIBED }; +enum CircleEntryCacheStatus: uint8_t { + UNKNOWN = 0x00, // Used to detect uninitialized memory + NO_DATA_YET = 0x01, // Used in the constuctor + LOADING = 0x02, // When the token request to load cache has been sent and no data is present + UPDATING = 0x03, // Starting from this level the cache entry can be used + CHECKING_MEMBERSHIP = 0x04, // Means we're actually looking into msgs to update membership status + UP_TO_DATE = 0x05 // Everything should be loaded here. +}; + class RsGxsCircleCache { - public: - +public: RsGxsCircleCache(); + bool loadBaseCircle(const RsGxsCircleGroup &circle); bool loadSubCircle(const RsGxsCircleCache &subcircle); @@ -142,33 +151,62 @@ class RsGxsCircleCache bool addAllowedPeer(const RsPgpId &pgpid); bool addLocalFriend(const RsPgpId &pgpid); + // Cache related data + + rstime_t mLastUpdatedMembershipTS ; // Last time the subscribe messages have been requested. Should be reset when new messages arrive. + rstime_t mLastUpdateTime; // Last time the cache entry was loaded + CircleEntryCacheStatus mStatus; // Overall state of the cache entry + bool mAllIdsHere ; // True when all ids are knwon and available. + + // GxsCircle related data + RsGxsCircleId mCircleId; std::string mCircleName; - uint32_t mCircleType; + RsGxsCircleType mCircleType; bool mIsExternal; - RsGxsCircleId mRestrictedCircleId ; // circle ID that circle is restricted to. + RsGxsCircleId mRestrictedCircleId ; // circle ID that circle is restricted to. uint32_t mGroupStatus; uint32_t mGroupSubscribeFlags; - rstime_t mUpdateTime; #ifdef SUBSCIRCLES std::set mUnprocessedCircles; std::set mProcessedCircles; #endif std::map mMembershipStatus; - rstime_t mLastUpdatedMembershipTS ; // last time the subscribe messages have been requested. Should be reset when new messages arrive. - + std::set mAllowedGxsIds; // IDs that are allowed in the circle and have requested membership. This is the official members list. std::set mAllowedNodes; - - RsPeerId mOriginator ; // peer who sent the data, in case we need to ask for ids + + RsPeerId mOriginator ; // peer who sent the data, in case we need to ask for ids }; class PgpAuxUtils; +class RsCirclesMemCache : public std::map +{ +public: + RsCirclesMemCache() : std::map(){} + + bool is_cached(const RsGxsCircleId& id) { return end() != find(id) ; } + RsGxsCircleCache& ref(const RsGxsCircleId& id) { return operator[](id) ; } + + void printStats() { std::cerr << "CircleMemCache: " << size() << " elements." << std::endl; } + + template void applyToAllCachedEntries(ClientClass& c,bool (ClientClass::*method)(RsGxsCircleCache&)) + { + for(auto& it:*this) + (c.*method)(it.second); + } + template void applyToAllCachedEntries(ClientClass& c,bool (ClientClass::*method)(const RsGxsCircleCache&)) + { + for(const auto& it:*this) + (c.*method)(it.second); + } +}; + class p3GxsCircles: public RsGxsCircleExchange, public RsGxsCircles, public GxsTokenQueue, public RsTickEvent { @@ -208,6 +246,10 @@ public: bool inviteIdsToCircle( const std::set& identities, const RsGxsCircleId& circleId ) override; + /// @see RsGxsCircles + bool revokeIdsFromCircle( const std::set& identities, + const RsGxsCircleId& circleId ) override; + /// @see RsGxsCircles bool getCircleRequest(const RsGxsGroupId& circleId, const RsGxsMessageId& msgId, @@ -260,7 +302,7 @@ public: protected: - bool pushCircleMembershipRequest(const RsGxsId& own_gxsid,const RsGxsCircleId& circle_id,uint32_t request_type) ; + bool pushCircleMembershipRequest(const RsGxsId& own_gxsid, const RsGxsCircleId& circle_id, RsGxsCircleSubscriptionType request_type) ; static uint32_t circleAuthenPolicy(); /** Notifications **/ @@ -315,20 +357,18 @@ public: std::list mCirclePersonalIdList; /***** Caching Circle Info, *****/ - // initial load queue - std::list mCacheLoad_ToCache; // waiting for subcircle to load. (first is part of each of the second list) // TODO. //std::map > mCacheLoad_SubCircle; - // Circles that are being loaded. - std::map mLoadingCache; + std::set mCirclesToLoad; // list of circles to update/load, so that we can treat them by groups. + RsCirclesMemCache mCircleCache; + //RsMemCache mCircleCache; // actual cache data - // actual cache. - RsMemCache mCircleCache; - - private: + void debug_dumpCache(); // debug method to overview what's going on + bool debug_dumpCacheEntry(RsGxsCircleCache &cache); +private: std::string genRandomId(); @@ -341,6 +381,9 @@ public: uint32_t mDummyIdToken; std::list mDummyPgpLinkedIds; std::list mDummyOwnIds; + bool mShouldSendCacheUpdateNotification ; + rstime_t mLastCacheUpdateEvent; + rstime_t mLastDebugPrintTS; RS_SET_CONTEXT_DEBUG_LEVEL(2) }; diff --git a/libretroshare/src/services/p3gxscommon.cc b/libretroshare/src/services/p3gxscommon.cc index 28d94e28c..38f6f3cc0 100644 --- a/libretroshare/src/services/p3gxscommon.cc +++ b/libretroshare/src/services/p3gxscommon.cc @@ -79,6 +79,11 @@ RsGxsImage &RsGxsImage::operator=(const RsGxsImage &a) } +bool RsGxsImage::empty() const +{ + return ((mData == NULL) || (mSize == 0)); +} + void RsGxsImage::take(uint8_t *data, uint32_t size) { #ifdef DEBUG_GXSCOMMON @@ -420,7 +425,7 @@ double p3GxsCommentService::calculateBestScore(int upVotes, int downVotes) /********************************************************************************************/ -bool p3GxsCommentService::createGxsComment(uint32_t &token, RsGxsComment &msg) +bool p3GxsCommentService::createGxsComment(uint32_t &token, const RsGxsComment &msg) { #ifdef DEBUG_GXSCOMMON std::cerr << "p3GxsCommentService::createGxsComment() GroupId: " << msg.mMeta.mGroupId; diff --git a/libretroshare/src/services/p3gxscommon.h b/libretroshare/src/services/p3gxscommon.h index 469fa3426..4b25c826e 100644 --- a/libretroshare/src/services/p3gxscommon.h +++ b/libretroshare/src/services/p3gxscommon.h @@ -66,7 +66,7 @@ class p3GxsCommentService: public GxsTokenQueue bool getGxsCommentData(const uint32_t &token, std::vector &msgs); bool getGxsRelatedComments(const uint32_t &token, std::vector &msgs); - bool createGxsComment(uint32_t &token, RsGxsComment &msg); + bool createGxsComment(uint32_t &token, const RsGxsComment &msg); bool createGxsVote(uint32_t &token, RsGxsVote &msg); // Special Acknowledge. diff --git a/libretroshare/src/services/p3gxsforums.cc b/libretroshare/src/services/p3gxsforums.cc index b8c414df0..988abb667 100644 --- a/libretroshare/src/services/p3gxsforums.cc +++ b/libretroshare/src/services/p3gxsforums.cc @@ -4,7 +4,8 @@ * libretroshare: retroshare core library * * * * Copyright (C) 2012-2014 Robert Fernie * - * Copyright (C) 2018-2019 Gioacchino Mazzurco * + * Copyright (C) 2018-2020 Gioacchino Mazzurco * + * Copyright (C) 2019-2020 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -20,16 +21,20 @@ * along with this program. If not, see . * * * *******************************************************************************/ + +#include +#include + #include "services/p3gxsforums.h" #include "rsitems/rsgxsforumitems.h" #include "retroshare/rspeers.h" #include "retroshare/rsidentity.h" - +#include "util/rsdebug.h" #include "rsserver/p3face.h" #include "retroshare/rsnotify.h" - +#include "util/rsdebuglevel2.h" #include "retroshare/rsgxsflags.h" -#include + // For Dummy Msgs. #include "util/rsrandom.h" @@ -96,14 +101,14 @@ uint32_t p3GxsForums::forumsAuthenPolicy() static const uint32_t GXS_FORUMS_CONFIG_MAX_TIME_NOTIFY_STORAGE = 86400*30*2 ; // ignore notifications for 2 months static const uint8_t GXS_FORUMS_CONFIG_SUBTYPE_NOTIFY_RECORD = 0x01 ; -struct RsGxsGroupNotifyRecordsItem: public RsItem +struct RsGxsForumNotifyRecordsItem: public RsItem { - RsGxsGroupNotifyRecordsItem() + RsGxsForumNotifyRecordsItem() : RsItem(RS_PKT_VERSION_SERVICE,RS_SERVICE_GXS_TYPE_FORUMS_CONFIG,GXS_FORUMS_CONFIG_SUBTYPE_NOTIFY_RECORD) {} - virtual ~RsGxsGroupNotifyRecordsItem() {} + virtual ~RsGxsForumNotifyRecordsItem() {} void serial_process( RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx ) @@ -127,7 +132,7 @@ public: switch(item_sub_id) { - case GXS_FORUMS_CONFIG_SUBTYPE_NOTIFY_RECORD: return new RsGxsGroupNotifyRecordsItem(); + case GXS_FORUMS_CONFIG_SUBTYPE_NOTIFY_RECORD: return new RsGxsForumNotifyRecordsItem(); default: return NULL; } @@ -138,7 +143,7 @@ bool p3GxsForums::saveList(bool &cleanup, std::list&saveList) { cleanup = true ; - RsGxsGroupNotifyRecordsItem *item = new RsGxsGroupNotifyRecordsItem ; + RsGxsForumNotifyRecordsItem *item = new RsGxsForumNotifyRecordsItem ; item->records = mKnownForums ; @@ -155,14 +160,14 @@ bool p3GxsForums::loadList(std::list& loadList) rstime_t now = time(NULL); - RsGxsGroupNotifyRecordsItem *fnr = dynamic_cast(item) ; + RsGxsForumNotifyRecordsItem *fnr = dynamic_cast(item) ; if(fnr != NULL) { mKnownForums.clear(); for(auto it(fnr->records.begin());it!=fnr->records.end();++it) - if( it->second + GXS_FORUMS_CONFIG_MAX_TIME_NOTIFY_STORAGE < now) + if( now < it->second + GXS_FORUMS_CONFIG_MAX_TIME_NOTIFY_STORAGE) mKnownForums.insert(*it) ; } @@ -189,21 +194,17 @@ void p3GxsForums::notifyChanges(std::vector &changes) for(it = changes.begin(); it != changes.end(); ++it) { RsGxsMsgChange *msgChange = dynamic_cast(*it); + if (msgChange) { if (msgChange->getType() == RsGxsNotify::TYPE_RECEIVED_NEW || msgChange->getType() == RsGxsNotify::TYPE_PUBLISHED) /* message received */ if (rsEvents) { - std::map >& msgChangeMap = msgChange->msgChangeMap; - for (auto mit = msgChangeMap.begin(); mit != msgChangeMap.end(); ++mit) - for (auto mit1 = mit->second.begin(); mit1 != mit->second.end(); ++mit1) - { - auto ev = std::make_shared(); - ev->mForumMsgId = *mit1; - ev->mForumGroupId = mit->first; - ev->mForumEventCode = RsForumEventCode::NEW_MESSAGE; - rsEvents->postEvent(ev); - } + auto ev = std::make_shared(); + ev->mForumMsgId = msgChange->mMsgId; + ev->mForumGroupId = msgChange->mGroupId; + ev->mForumEventCode = RsForumEventCode::NEW_MESSAGE; + rsEvents->postEvent(ev); } #ifdef NOT_USED_YET @@ -246,69 +247,92 @@ void p3GxsForums::notifyChanges(std::vector &changes) { switch (grpChange->getType()) { - default: case RsGxsNotify::TYPE_PROCESSED: // happens when the group is subscribed { - std::list &grpList = grpChange->mGrpIdList; - std::list::iterator git; - for (git = grpList.begin(); git != grpList.end(); ++git) - { auto ev = std::make_shared(); - ev->mForumGroupId = *git; + ev->mForumGroupId = grpChange->mGroupId; ev->mForumEventCode = RsForumEventCode::SUBSCRIBE_STATUS_CHANGED; rsEvents->postEvent(ev); - } - } - break; + break; case RsGxsNotify::TYPE_PUBLISHED: case RsGxsNotify::TYPE_RECEIVED_NEW: { /* group received */ - std::list &grpList = grpChange->mGrpIdList; - std::list::iterator git; RS_STACK_MUTEX(mKnownForumsMutex); - for (git = grpList.begin(); git != grpList.end(); ++git) - { - if(mKnownForums.find(*git) == mKnownForums.end()) - { - mKnownForums.insert( - std::make_pair(*git, time(nullptr))); - IndicateConfigChanged(); - auto ev = std::make_shared(); - ev->mForumGroupId = *git; - ev->mForumEventCode = RsForumEventCode::NEW_FORUM; - rsEvents->postEvent(ev); - } - else - RsInfo() << __PRETTY_FUNCTION__ - << " Not notifying already known forum " - << *git << std::endl; + if(mKnownForums.find(grpChange->mGroupId) == mKnownForums.end()) + { + mKnownForums.insert( std::make_pair(grpChange->mGroupId, time(nullptr))); + IndicateConfigChanged(); + + auto ev = std::make_shared(); + ev->mForumGroupId = grpChange->mGroupId; + ev->mForumEventCode = RsForumEventCode::NEW_FORUM; + rsEvents->postEvent(ev); } + else + RsInfo() << __PRETTY_FUNCTION__ + << " Not notifying already known forum " + << grpChange->mGroupId << std::endl; + } break; - } -#ifdef NOT_USED_YET - case RsGxsNotify::TYPE_RECEIVED_PUBLISHKEY: + case RsGxsNotify::TYPE_STATISTICS_CHANGED: { - /* group received */ - std::list &grpList = grpChange->mGrpIdList; - std::list::iterator git; - for (git = grpList.begin(); git != grpList.end(); ++git) + auto ev = std::make_shared(); + ev->mForumGroupId = grpChange->mGroupId; + ev->mForumEventCode = RsForumEventCode::STATISTICS_CHANGED; + rsEvents->postEvent(ev); + } + break; + + case RsGxsNotify::TYPE_UPDATED: + { + // Happens when the group data has changed. In this case we need to analyse the old and new group in order to detect possible notifications for clients + + RsGxsForumGroupItem *old_forum_grp_item = dynamic_cast(grpChange->mOldGroupItem); + RsGxsForumGroupItem *new_forum_grp_item = dynamic_cast(grpChange->mNewGroupItem); + + if(old_forum_grp_item == nullptr || new_forum_grp_item == nullptr) { - auto ev = std::make_shared(); + RsErr() << __PRETTY_FUNCTION__ << " received GxsGroupUpdate item with mOldGroup and mNewGroup not of type RsGxsForumGroupItem. This is inconsistent!" << std::endl; + delete grpChange; + continue; + } - ev->mChannelGroupId = *git; - ev->mChannelEventCode = RsGxsChannelEvent::RECEIVED_PUBLISH_KEY; + // First of all, we check if there is a difference between the old and new list of moderators - rsEvents->sendEvent(ev); + std::list added_mods, removed_mods; + + for(auto& gxs_id: new_forum_grp_item->mGroup.mAdminList.ids) + if(old_forum_grp_item->mGroup.mAdminList.ids.find(gxs_id) == old_forum_grp_item->mGroup.mAdminList.ids.end()) + added_mods.push_back(gxs_id); + + for(auto& gxs_id: old_forum_grp_item->mGroup.mAdminList.ids) + if(new_forum_grp_item->mGroup.mAdminList.ids.find(gxs_id) == new_forum_grp_item->mGroup.mAdminList.ids.end()) + removed_mods.push_back(gxs_id); + + if(!added_mods.empty() || !removed_mods.empty()) + { + auto ev = std::make_shared(); + + ev->mForumGroupId = new_forum_grp_item->meta.mGroupId; + ev->mModeratorsAdded = added_mods; + ev->mModeratorsRemoved = removed_mods; + ev->mForumEventCode = RsForumEventCode::MODERATOR_LIST_CHANGED; + + rsEvents->postEvent(ev); } } - break; -#endif + break; + + + default: + RsErr() << " Got a GXS event of type " << grpChange->getType() << " Currently not handled." << std::endl; + break; } } } @@ -403,46 +427,6 @@ bool p3GxsForums::getMsgData(const uint32_t &token, std::vector & return ok; } -//Not currently used -/*bool p3GxsForums::getRelatedMessages(const uint32_t &token, std::vector &msgs) -{ - GxsMsgRelatedDataMap msgData; - bool ok = RsGenExchange::getMsgRelatedData(token, msgData); - - if(ok) - { - GxsMsgRelatedDataMap::iterator mit = msgData.begin(); - - for(; mit != msgData.end(); ++mit) - { - std::vector& msgItems = mit->second; - std::vector::iterator vit = msgItems.begin(); - - for(; vit != msgItems.end(); ++vit) - { - RsGxsForumMsgItem* item = dynamic_cast(*vit); - - if(item) - { - RsGxsForumMsg msg = item->mMsg; - msg.mMeta = item->meta; - msgs.push_back(msg); - delete item; - } - else - { - std::cerr << "Not a GxsForumMsgItem, deleting!" << std::endl; - delete *vit; - } - } - } - } - - return ok; -}*/ - -/********************************************************************************************/ - bool p3GxsForums::createForumV2( const std::string& name, const std::string& description, const RsGxsId& authorId, const std::set& moderatorsIds, @@ -666,15 +650,22 @@ bool p3GxsForums::getForumsSummaries( std::list& forums ) return getGroupSummary(token, forums); } -bool p3GxsForums::getForumsInfo( - const std::list& forumIds, - std::vector& forumsInfo ) +bool p3GxsForums::getForumsInfo( const std::list& forumIds, std::vector& forumsInfo ) { uint32_t token; RsTokReqOptions opts; opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; - if( !requestGroupInfo(token, opts, forumIds) - || waitToken(token,std::chrono::milliseconds(5000)) != RsTokenService::COMPLETE ) return false; + + if(forumIds.empty()) + { + if( !requestGroupInfo(token, opts) || waitToken(token,std::chrono::milliseconds(5000)) != RsTokenService::COMPLETE ) + return false; + } + else + { + if( !requestGroupInfo(token, opts, forumIds, forumIds.size()==1) || waitToken(token,std::chrono::milliseconds(5000)) != RsTokenService::COMPLETE ) + return false; + } return getGroupData(token, forumsInfo); } @@ -800,6 +791,51 @@ bool p3GxsForums::importForumLink( return true; } +std::error_condition p3GxsForums::getChildPosts( + const RsGxsGroupId& forumId, const RsGxsMessageId& parentId, + std::vector& childPosts ) +{ + RS_DBG3("forumId: ", forumId, " parentId: ", parentId); + + if(forumId.isNull() || parentId.isNull()) + return std::errc::invalid_argument; + + std::vector msgIds; + msgIds.push_back(RsGxsGrpMsgIdPair(forumId, parentId)); + + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_MSG_RELATED_DATA; + opts.mOptions = RS_TOKREQOPT_MSG_PARENT | RS_TOKREQOPT_MSG_LATEST; + + uint32_t token; + if( !requestMsgRelatedInfo(token, opts, msgIds) || + waitToken(token) != RsTokenService::COMPLETE ) + return std::errc::timed_out; + + GxsMsgRelatedDataMap msgData; + if(!getMsgRelatedData(token, msgData)) + return std::errc::no_message_available; + + for(auto& mit: msgData) + { + for(auto& vit: mit.second) + { + auto msgItem = dynamic_cast(vit); + if(msgItem) + { + RsGxsForumMsg post = msgItem->mMsg; + post.mMeta = msgItem->meta; + childPosts.push_back(post); + } + else RS_WARN("Got item of unexpected type: ", vit); + + delete vit; + } + } + + return std::error_condition(); +} + bool p3GxsForums::createGroup(uint32_t &token, RsGxsForumGroup &group) { std::cerr << "p3GxsForums::createGroup()" << std::endl; @@ -812,7 +848,25 @@ bool p3GxsForums::createGroup(uint32_t &token, RsGxsForumGroup &group) return true; } -bool p3GxsForums::updateGroup(uint32_t &token, RsGxsForumGroup &group) +bool p3GxsForums::getForumServiceStatistics(GxsServiceStatistic& stat) +{ + uint32_t token; + if(!RsGxsIfaceHelper::requestServiceStatistic(token) || waitToken(token) != RsTokenService::COMPLETE) + return false; + + return RsGenExchange::getServiceStatistic(token,stat); +} + +bool p3GxsForums::getForumStatistics(const RsGxsGroupId& ForumId,GxsGroupStatistic& stat) +{ + uint32_t token; + if(!RsGxsIfaceHelper::requestGroupStatistic(token, ForumId) || waitToken(token) != RsTokenService::COMPLETE) + return false; + + return RsGenExchange::getGroupStatistic(token,stat); +} + +bool p3GxsForums::updateGroup(uint32_t &token, const RsGxsForumGroup &group) { std::cerr << "p3GxsForums::updateGroup()" << std::endl; @@ -865,6 +919,9 @@ void p3GxsForums::setMessageReadStatus(uint32_t& token, const RsGxsGrpMsgIdPair& setMsgStatusFlags(token, msgId, status, mask); + /* WARNING: The event may be received before the operation is completed! + * TODO: move notification to blocking method markRead(...) which wait the + * operation to complete */ if (rsEvents) { auto ev = std::make_shared(); @@ -879,6 +936,37 @@ void p3GxsForums::setMessageReadStatus(uint32_t& token, const RsGxsGrpMsgIdPair& /********************************************************************************************/ /********************************************************************************************/ +std::error_condition p3GxsForums::setPostKeepForever( + const RsGxsGroupId& forumId, const RsGxsMessageId& postId, + bool keepForever ) +{ + if(forumId.isNull() || postId.isNull()) return std::errc::invalid_argument; + + uint32_t mask = GXS_SERV::GXS_MSG_STATUS_KEEP_FOREVER; + uint32_t status = keepForever ? GXS_SERV::GXS_MSG_STATUS_KEEP_FOREVER : 0; + + uint32_t token; + setMsgStatusFlags(token, RsGxsGrpMsgIdPair(forumId, postId), status, mask); + + switch(waitToken(token)) + { + case RsTokenService::PENDING: // [[fallthrough]]; + case RsTokenService::PARTIAL: return std::errc::timed_out; + case RsTokenService::COMPLETE: // [[fallthrough]]; + case RsTokenService::DONE: + { + auto ev = std::make_shared(); + ev->mForumGroupId = forumId; + ev->mForumMsgId = postId; + ev->mForumEventCode = RsForumEventCode::UPDATED_MESSAGE; + rsEvents->postEvent(ev); + return std::error_condition(); + } + case RsTokenService::CANCELLED: return std::errc::operation_canceled; + default: return std::errc::bad_message; + } +} + /* so we need the same tick idea as wiki for generating dummy forums */ diff --git a/libretroshare/src/services/p3gxsforums.h b/libretroshare/src/services/p3gxsforums.h index ae2bf74bf..5ddce2ea2 100644 --- a/libretroshare/src/services/p3gxsforums.h +++ b/libretroshare/src/services/p3gxsforums.h @@ -4,7 +4,8 @@ * libretroshare: retroshare core library * * * * Copyright (C) 2012-2014 Robert Fernie * - * Copyright (C) 2018-2019 Gioacchino Mazzurco * + * Copyright (C) 2018-2020 Gioacchino Mazzurco * + * Copyright (C) 2019-2020 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -94,6 +95,12 @@ public: const std::list& forumIds, std::vector& forumsInfo ); + /// Implementation of @see RsGxsForums::getForumStatistics + bool getForumStatistics(const RsGxsGroupId& ForumId,GxsGroupStatistic& stat) override; + + /// Implementation of @see RsGxsForums::getForumServiceStatistics + bool getForumServiceStatistics(GxsServiceStatistic& stat) override; + /// @see RsGxsForums::getForumMsgMetaData virtual bool getForumMsgMetaData(const RsGxsGroupId& forumId, std::vector& msg_metas) ; @@ -125,13 +132,26 @@ public: std::string& errMsg = RS_DEFAULT_STORAGE_PARAM(std::string) ) override; - virtual bool getGroupData(const uint32_t &token, std::vector &groups); - virtual bool getMsgData(const uint32_t &token, std::vector &msgs); - virtual bool getMsgMetaData(const uint32_t &token, GxsMsgMetaMap& msg_metas); - virtual void setMessageReadStatus(uint32_t& token, const RsGxsGrpMsgIdPair& msgId, bool read); - virtual bool createGroup(uint32_t &token, RsGxsForumGroup &group); - virtual bool createMsg(uint32_t &token, RsGxsForumMsg &msg); - virtual bool updateGroup(uint32_t &token, RsGxsForumGroup &group); + /// @see RsGxsForums + std::error_condition getChildPosts( + const RsGxsGroupId& forumId, const RsGxsMessageId& parentId, + std::vector& childPosts ) override; + + /// @see RsGxsForums + std::error_condition setPostKeepForever( + const RsGxsGroupId& forumId, const RsGxsMessageId& postId, + bool keepForever ); + + /// implementation of rsGxsGorums + /// + bool getGroupData(const uint32_t &token, std::vector &groups) override; + bool getMsgData(const uint32_t &token, std::vector &msgs) override; + void setMessageReadStatus(uint32_t& token, const RsGxsGrpMsgIdPair& msgId, bool read) override; + bool createGroup(uint32_t &token, RsGxsForumGroup &group) override; + bool createMsg(uint32_t &token, RsGxsForumMsg &msg) override; + bool updateGroup(uint32_t &token, const RsGxsForumGroup &group) override; + + bool getMsgMetaData(const uint32_t &token, GxsMsgMetaMap& msg_metas) ; private: diff --git a/libretroshare/src/services/p3idservice.cc b/libretroshare/src/services/p3idservice.cc index 861c5c224..609dd8ad6 100644 --- a/libretroshare/src/services/p3idservice.cc +++ b/libretroshare/src/services/p3idservice.cc @@ -55,7 +55,7 @@ #define ID_REQUEST_REPUTATION 0x0003 #define ID_REQUEST_OPINION 0x0004 -#define GXSID_MAX_CACHE_SIZE 5000 +#define GXSID_MAX_CACHE_SIZE 15000 // unused keys are deleted according to some heuristic that should favor known keys, signed keys etc. @@ -606,67 +606,72 @@ void p3IdService::notifyChanges(std::vector &changes) RsGxsMsgChange *msgChange = dynamic_cast(changes[i]); if (msgChange && !msgChange->metaChange()) - { -#ifdef DEBUG_IDS - std::cerr << "p3IdService::notifyChanges() Found Message Change Notification"; - std::cerr << std::endl; -#endif - - std::map > &msgChangeMap = msgChange->msgChangeMap; - - for(auto mit = msgChangeMap.begin(); mit != msgChangeMap.end(); ++mit) - { -#ifdef DEBUG_IDS - std::cerr << "p3IdService::notifyChanges() Msgs for Group: " << mit->first; - std::cerr << std::endl; -#endif - } - } + RsWarn() << __PRETTY_FUNCTION__ << " Found a Msg data change in p3IdService. This is quite unexpected." << std::endl; RsGxsGroupChange *groupChange = dynamic_cast(changes[i]); - if (groupChange && !groupChange->metaChange()) + if (groupChange) { #ifdef DEBUG_IDS std::cerr << "p3IdService::notifyChanges() Found Group Change Notification"; std::cerr << std::endl; #endif - std::list &groupList = groupChange->mGrpIdList; - - for(auto git = groupList.begin(); git != groupList.end();++git) - { + const RsGxsGroupId& gid(groupChange->mGroupId); #ifdef DEBUG_IDS - std::cerr << "p3IdService::notifyChanges() Auto Subscribe to Incoming Groups: " << *git; + std::cerr << "p3IdService::notifyChanges() Auto Subscribe to Incoming Groups: " << gid; std::cerr << std::endl; #endif - if(!rsReputations->isIdentityBanned(RsGxsId(*git))) + + if(!rsReputations->isIdentityBanned(RsGxsId(gid))) { - uint32_t token; - RsGenExchange::subscribeToGroup(token, *git, true); - - // also time_stamp the key that this group represents - - timeStampKey(RsGxsId(*git),RsIdentityUsage(serviceType(),RsIdentityUsage::IDENTITY_DATA_UPDATE)) ; - // notify that a new identity is received, if needed + bool should_subscribe = false; + switch(groupChange->getType()) { + case RsGxsNotify::TYPE_PROCESSED: break ; // Happens when the group is subscribed. This is triggered by RsGenExchange::subscribeToGroup, so better not + // call it again from here!! + case RsGxsNotify::TYPE_PUBLISHED: + { + auto ev = std::make_shared(); + ev->mIdentityId = gid; + ev->mIdentityEventCode = RsGxsIdentityEventCode::UPDATED_IDENTITY; + rsEvents->postEvent(ev); + + // also time_stamp the key that this group represents + timeStampKey(RsGxsId(gid),RsIdentityUsage(RsServiceType(serviceType()),RsIdentityUsage::IDENTITY_NEW_FROM_GXS_SYNC)) ; + should_subscribe = true; + } + break; + case RsGxsNotify::TYPE_RECEIVED_NEW: { auto ev = std::make_shared(); - ev->mIdentityId = *git; + ev->mIdentityId = gid; ev->mIdentityEventCode = RsGxsIdentityEventCode::NEW_IDENTITY; rsEvents->postEvent(ev); + + // also time_stamp the key that this group represents + timeStampKey(RsGxsId(gid),RsIdentityUsage(RsServiceType(serviceType()),RsIdentityUsage::IDENTITY_NEW_FROM_GXS_SYNC)) ; + should_subscribe = true; + + std::cerr << "Received new identity " << gid << " and subscribing to it" << std::endl; } break; default: break; } + + if(should_subscribe) + { + uint32_t token; + RsGenExchange::subscribeToGroup(token, gid, true); + } + } - } } delete changes[i]; @@ -1211,8 +1216,7 @@ bool p3IdService::requestIdentity( return false; } - RsIdentityUsage usageInfo( RsServiceType::GXSID, - RsIdentityUsage::IDENTITY_DATA_UPDATE ); + RsIdentityUsage usageInfo( RsServiceType::GXSID, RsIdentityUsage::IDENTITY_NEW_FROM_EXPLICIT_REQUEST ); return requestKey(id, askPeersList, usageInfo); } @@ -1247,8 +1251,9 @@ bool p3IdService::requestKey(const RsGxsId &id, const std::list& peers if( info.mOverallReputationLevel == RsReputationLevel::LOCALLY_NEGATIVE ) { - RsInfo() << __PRETTY_FUNCTION__ << " not requesting Key " << id - << " because it has been banned." << std::endl; +#ifdef DEBUG_IDS + RsInfo() << __PRETTY_FUNCTION__ << " not requesting Key " << id << " because it has been banned." << std::endl; +#endif RS_STACK_MUTEX(mIdMtx); mIdsNotPresent.erase(id); @@ -1357,7 +1362,7 @@ bool p3IdService::signData(const uint8_t *data,uint32_t data_size,const RsGxsId& return false ; } error_status = RS_GIXS_ERROR_NO_ERROR ; - timeStampKey(own_gxs_id,RsIdentityUsage(serviceType(),RsIdentityUsage::IDENTITY_GENERIC_SIGNATURE_CREATION)) ; + timeStampKey(own_gxs_id,RsIdentityUsage(RsServiceType(serviceType()),RsIdentityUsage::IDENTITY_GENERIC_SIGNATURE_CREATION)) ; return true ; } @@ -1430,7 +1435,7 @@ bool p3IdService::encryptData( const uint8_t *decrypted_data, return false ; } error_status = RS_GIXS_ERROR_NO_ERROR ; - timeStampKey(encryption_key_id,RsIdentityUsage(serviceType(),RsIdentityUsage::IDENTITY_GENERIC_ENCRYPTION)) ; + timeStampKey(encryption_key_id,RsIdentityUsage(RsServiceType::GXSID,RsIdentityUsage::IDENTITY_GENERIC_ENCRYPTION)) ; return true ; } @@ -1519,7 +1524,7 @@ bool p3IdService::encryptData( const uint8_t* decrypted_data, { timeStampKey( *it, RsIdentityUsage( - serviceType(), + RsServiceType::GXSID, RsIdentityUsage::IDENTITY_GENERIC_ENCRYPTION ) ); } @@ -1560,7 +1565,7 @@ bool p3IdService::decryptData( const uint8_t *encrypted_data, error_status = RS_GIXS_ERROR_NO_ERROR; timeStampKey( key_id, RsIdentityUsage( - serviceType(), + RsServiceType::GXSID, RsIdentityUsage::IDENTITY_GENERIC_DECRYPTION) ); return true ; @@ -1653,7 +1658,7 @@ bool p3IdService::decryptData( const uint8_t* encrypted_data, { timeStampKey( *it, RsIdentityUsage( - serviceType(), + RsServiceType::GXSID, RsIdentityUsage::IDENTITY_GENERIC_DECRYPTION ) ); } @@ -2289,7 +2294,7 @@ bool SSGxsIdGroup::load(const std::string &input) char scorestr[RSGXSID_MAX_SERVICE_STRING]; // split into parts. - if (3 != sscanf(input.c_str(), "v2 {P:%[^}]} {T:%[^}]} {R:%[^}]}", pgpstr, recognstr, scorestr)) + if (3 != sscanf(input.c_str(), "v2 {P:%[^}]}{T:%[^}]}{R:%[^}]}", pgpstr, recognstr, scorestr)) { #ifdef DEBUG_IDS std::cerr << "SSGxsIdGroup::load() Failed to extract 4 Parts"; @@ -2950,8 +2955,22 @@ void p3IdService::requestIdsFromNet() for(cit = mIdsNotPresent.begin(); cit != mIdsNotPresent.end();) { - Dbg2() << __PRETTY_FUNCTION__ << " Processing missing key RsGxsId: " - << cit->first << std::endl; +#ifdef DEBUG_IDS + Dbg2() << __PRETTY_FUNCTION__ << " Processing missing key RsGxsId: " << cit->first << std::endl; +#endif + RsGxsIdCache data; + + if(mKeyCache.fetch(cit->first,data)) + { +#ifdef DEBUG_IDS + std::cerr << __PRETTY_FUNCTION__ << ". Dropping request for ID " << cit->first << " at last minute, because it was found in cache"<< std::endl; +#endif + auto tmp(cit); + ++tmp; + mIdsNotPresent.erase(cit); + cit = tmp; + continue; + } const RsGxsId& gxsId = cit->first; const std::list& peers = cit->second; @@ -2970,9 +2989,11 @@ void p3IdService::requestIdsFromNet() requests[peer].push_back(cit->first); request_can_proceed = true ; +#ifdef DEBUG_IDS Dbg2() << __PRETTY_FUNCTION__ << " Moving missing key RsGxsId:" << gxsId << " to peer: " << peer << " requests queue" << std::endl; +#endif } } @@ -2990,9 +3011,11 @@ void p3IdService::requestIdsFromNet() } else { +#ifdef DEBUG_IDS RsInfo() << __PRETTY_FUNCTION__ << " no online peers among supplied" << " list in request for RsGxsId: " << gxsId << ". Keeping it until peers show up."<< std::endl; +#endif ++cit; } } @@ -3002,12 +3025,13 @@ void p3IdService::requestIdsFromNet() { const RsPeerId& peer = cit2->first; std::list grpIds; - for( std::list::const_iterator gxs_id_it = cit2->second.begin(); - gxs_id_it != cit2->second.end(); ++gxs_id_it ) + for( std::list::const_iterator gxs_id_it = cit2->second.begin(); gxs_id_it != cit2->second.end(); ++gxs_id_it ) { +#ifdef DEBUG_IDS Dbg2() << __PRETTY_FUNCTION__ << " passing RsGxsId: " << *gxs_id_it << " request for peer: " << peer << " to RsNetworkExchangeService " << std::endl; +#endif grpIds.push_back(RsGxsGroupId(*gxs_id_it)); } @@ -3212,7 +3236,7 @@ bool p3IdService::cachetest_handlerequest(uint32_t token) if (!haveKey(*vit)) { std::list nullpeers; - requestKey(*vit, nullpeers,RsIdentityUsage(serviceType(),RsIdentityUsage::UNKNOWN_USAGE)); + requestKey(*vit, nullpeers,RsIdentityUsage(RsServiceType::GXSID,RsIdentityUsage::UNKNOWN_USAGE)); #ifdef DEBUG_IDS std::cerr << "p3IdService::cachetest_request() Requested Key Id: " << *vit; @@ -3492,25 +3516,7 @@ RsGenExchange::ServiceCreate_Return p3IdService::service_CreateGroup( unsigned int sign_size = MAX_SIGN_SIZE; memset(signarray,0,MAX_SIGN_SIZE) ; // just in case. - /* -10 is never returned by askForDeferredSelfSignature therefore we can - * use it to properly detect and handle the case libretroshare is being - * used outside retroshare-gui */ - int result = -10; - - /* This method is DEPRECATED we call it only for retrocompatibility with - * retroshare-gui, when called from something different then - * retroshare-gui for example retroshare-service it miserably fail! */ - mPgpUtils->askForDeferredSelfSignature( - static_cast(hash.toByteArray()), - hash.SIZE_IN_BYTES, signarray, &sign_size, result, - __PRETTY_FUNCTION__ ); - - /* If askForDeferredSelfSignature left result untouched it means - * libretroshare is being used by something different then - * retroshare-gui so try calling AuthGPG::getAuthGPG()->SignDataBin - * directly */ - if( result == -10 ) - result = AuthGPG::getAuthGPG()->SignDataBin( + int result = AuthGPG::getAuthGPG()->SignDataBin( static_cast(hash.toByteArray()), hash.SIZE_IN_BYTES, signarray, &sign_size, __PRETTY_FUNCTION__ ) @@ -3572,7 +3578,9 @@ RsGenExchange::ServiceCreate_Return p3IdService::service_CreateGroup( } } +#ifdef DEBUG_IDS Dbg2() << __PRETTY_FUNCTION__ << " returns: " << createStatus << std::endl; +#endif return createStatus; } @@ -4724,6 +4732,8 @@ void p3IdService::handle_event(uint32_t event_type, const std::string &/*elabel* case GXSID_EVENT_REQUEST_IDS: requestIdsFromNet(); break; + case GXSID_EVENT_REPUTATION: + break; default: RsErr() << __PRETTY_FUNCTION__ << " Unknown Event Type: " << event_type << std::endl; @@ -4822,11 +4832,10 @@ void RsGxsIdGroup::serial_process( RS_SERIAL_PROCESS(mReputation); } -RsIdentityUsage::RsIdentityUsage( - RsServiceType service, RsIdentityUsage::UsageCode code, - const RsGxsGroupId& gid, const RsGxsMessageId& mid, +RsIdentityUsage::RsIdentityUsage(RsServiceType service, RsIdentityUsage::UsageCode code, + const RsGxsGroupId& gid, const RsGxsMessageId& mid, const RsGxsMessageId &pid, const RsGxsMessageId &tid, uint64_t additional_id, const std::string& comment ) : - mServiceId(service), mUsageCode(code), mGrpId(gid), mMsgId(mid), + mServiceId(service), mUsageCode(code), mGrpId(gid), mMsgId(mid),mParentId(pid),mThreadId(tid), mAdditionalId(additional_id), mComment(comment) { /* This is a hack, since it will hash also mHash, but because it is @@ -4844,42 +4853,6 @@ RsIdentityUsage::RsIdentityUsage( mHash = hs.hash(); } -RsIdentityUsage::RsIdentityUsage( - uint16_t service, const RsIdentityUsage::UsageCode& code, - const RsGxsGroupId& gid, const RsGxsMessageId& mid, - uint64_t additional_id,const std::string& comment ) : - mServiceId(static_cast(service)), mUsageCode(code), - mGrpId(gid), mMsgId(mid), mAdditionalId(additional_id), mComment(comment) -{ -#ifdef DEBUG_IDS - std::cerr << "New identity usage: " << std::endl; - std::cerr << " service=" << std::hex << service << std::endl; - std::cerr << " code =" << std::hex << code << std::endl; - std::cerr << " grpId =" << std::hex << gid << std::endl; - std::cerr << " msgId =" << std::hex << mid << std::endl; - std::cerr << " add id =" << std::hex << additional_id << std::endl; - std::cerr << " commnt =\"" << std::hex << comment << "\"" << std::endl; -#endif - - /* This is a hack, since it will hash also mHash, but because it is - * initialized to 0, and only computed in the constructor here, it should - * be ok. */ - librs::crypto::HashStream hs(librs::crypto::HashStream::SHA1) ; - - hs << (uint32_t)service ; // G10h4ck: Why uint32 if it's 16 bits? - hs << (uint8_t)code ; - hs << gid ; - hs << mid ; - hs << (uint64_t)additional_id ; - hs << comment ; - - mHash = hs.hash(); - -#ifdef DEBUG_IDS - std::cerr << " hash =\"" << std::hex << mHash << "\"" << std::endl; -#endif -} - RsIdentityUsage::RsIdentityUsage() : mServiceId(RsServiceType::NONE), mUsageCode(UNKNOWN_USAGE), mAdditionalId(0) {} diff --git a/libretroshare/src/services/p3msgservice.cc b/libretroshare/src/services/p3msgservice.cc index b1c1bf847..83c1c8e88 100644 --- a/libretroshare/src/services/p3msgservice.cc +++ b/libretroshare/src/services/p3msgservice.cc @@ -80,7 +80,9 @@ p3MsgService::p3MsgService( p3ServiceControl *sc, p3IdService *id_serv, recentlyReceivedMutex("p3MsgService recently received hash mutex"), mGxsTransServ(gxsMS) { - _serialiser = new RsMsgSerialiser(RsServiceSerializer::SERIALIZATION_FLAG_NONE); // this serialiser is used for services. It's not the same than the one returned by setupSerialiser(). We need both!! + /* this serialiser is used for services. It's not the same than the one + * returned by setupSerialiser(). We need both!! */ + _serialiser = new RsMsgSerialiser(); addSerialType(_serialiser); /* MsgIds are not transmitted, but only used locally as a storage index. @@ -509,7 +511,7 @@ RsSerialiser* p3MsgService::setupSerialiser() // this serialiser is used for con { RsSerialiser *rss = new RsSerialiser ; - rss->addSerialType(new RsMsgSerialiser(RsServiceSerializer::SERIALIZATION_FLAG_CONFIG)); + rss->addSerialType(new RsMsgSerialiser(RsSerializationFlags::CONFIG)); rss->addSerialType(new RsGeneralConfigSerialiser()); return rss; @@ -1185,11 +1187,15 @@ bool p3MsgService::MessageSend(MessageInfo &info) /* use processMsg to get the new msgId */ msg->recvTime = time(NULL); msg->msgId = getNewUniqueMsgId(); - + msg->msgFlags |= RS_MSG_OUTGOING; imsg[msg->msgId] = msg; + // Update info for caller + info.msgId = std::to_string(msg->msgId); + info .msgflags = msg->msgFlags; + RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGELIST,NOTIFY_TYPE_ADD); } @@ -1735,6 +1741,7 @@ void p3MsgService::initRsMI(RsMsgItem *msg, MessageInfo &mi) if (msg->msgFlags & RS_MSG_FLAGS_REPLIED) mi.msgflags |= RS_MSG_REPLIED; if (msg->msgFlags & RS_MSG_FLAGS_FORWARDED) mi.msgflags |= RS_MSG_FORWARDED; if (msg->msgFlags & RS_MSG_FLAGS_STAR) mi.msgflags |= RS_MSG_STAR; + if (msg->msgFlags & RS_MSG_FLAGS_SPAM) mi.msgflags |= RS_MSG_SPAM; if (msg->msgFlags & RS_MSG_FLAGS_USER_REQUEST) mi.msgflags |= RS_MSG_USER_REQUEST; if (msg->msgFlags & RS_MSG_FLAGS_FRIEND_RECOMMENDATION) mi.msgflags |= RS_MSG_FRIEND_RECOMMENDATION; if (msg->msgFlags & RS_MSG_FLAGS_PUBLISH_KEY) mi.msgflags |= RS_MSG_PUBLISH_KEY; @@ -1829,6 +1836,10 @@ void p3MsgService::initRsMIS(RsMsgItem *msg, MsgInfoSummary &mis) { mis.msgflags |= RS_MSG_STAR; } + if (msg->msgFlags & RS_MSG_FLAGS_SPAM) + { + mis.msgflags |= RS_MSG_SPAM; + } if (msg->msgFlags & RS_MSG_FLAGS_USER_REQUEST) { mis.msgflags |= RS_MSG_USER_REQUEST; @@ -2386,10 +2397,11 @@ void p3MsgService::sendDistantMsgItem(RsMsgItem *msgitem) /* The item is serialized and turned into a generic turtle item. Use use the * explicit serialiser to make sure that the msgId is not included */ - uint32_t msg_serialized_rssize = RsMsgSerialiser(RsServiceSerializer::SERIALIZATION_FLAG_NONE).size(msgitem) ; + uint32_t msg_serialized_rssize = RsMsgSerialiser().size(msgitem); RsTemporaryMemory msg_serialized_data(msg_serialized_rssize) ; - if(!RsMsgSerialiser(RsServiceSerializer::SERIALIZATION_FLAG_NONE).serialise(msgitem,msg_serialized_data,&msg_serialized_rssize)) + if( !RsMsgSerialiser(). + serialise(msgitem,msg_serialized_data,&msg_serialized_rssize) ) { std::cerr << "(EE) p3MsgService::sendTurtleData(): Serialization error." << std::endl; return ; diff --git a/libretroshare/src/services/p3photoservice.cc b/libretroshare/src/services/p3photoservice.cc index 608fddbc0..1547e5a7d 100644 --- a/libretroshare/src/services/p3photoservice.cc +++ b/libretroshare/src/services/p3photoservice.cc @@ -25,179 +25,118 @@ RsPhoto *rsPhoto = NULL; - -const uint32_t RsPhoto::FLAG_MSG_TYPE_MASK = 0x000f; -const uint32_t RsPhoto::FLAG_MSG_TYPE_PHOTO_POST = 0x0001; -const uint32_t RsPhoto::FLAG_MSG_TYPE_PHOTO_COMMENT = 0x0002; - - - - -bool RsPhotoThumbnail::copyFrom(const RsPhotoThumbnail &nail) -{ - if (data) - { - deleteImage(); - } - - if ((!nail.data) || (nail.size == 0)) - { - return false; - } - - size = nail.size; - type = nail.type; - data = (uint8_t *) rs_malloc(size); - - if(data == NULL) - return false ; - - memcpy(data, nail.data, size); - - return true; -} - -bool RsPhotoThumbnail::deleteImage() -{ - if (data) - { - free(data); - data = NULL; - size = 0; - type.clear(); - } - return true; -} - - RsPhotoPhoto::RsPhotoPhoto() - :mSetFlags(0), mOrder(0), mMode(0), mModFlags(0) + :mOrder(0) { - return; + return; } RsPhotoAlbum::RsPhotoAlbum() - :mMode(0), mSetFlags(0), mModFlags(0) + :mShareMode(RSPHOTO_SHAREMODE_LOWRESONLY), mAutoDownload(false) { - return; + return; } -RsPhotoComment::RsPhotoComment() - : mComment(""), mCommentFlag(0) { - -} - -RsPhotoComment::RsPhotoComment(const RsGxsPhotoCommentItem &comment) - : mComment(""), mCommentFlag(0) { - - *this = comment.comment; - (*this).mMeta = comment.meta; - -} std::ostream &operator<<(std::ostream &out, const RsPhotoPhoto &photo) { - out << "RsPhotoPhoto [ "; - out << "Title: " << photo.mMeta.mMsgName; - out << "]"; - return out; + out << "RsPhotoPhoto [ "; + out << "Title: " << photo.mMeta.mMsgName; + out << "]"; + return out; } - std::ostream &operator<<(std::ostream &out, const RsPhotoAlbum &album) { - out << "RsPhotoAlbum [ "; - out << "Title: " << album.mMeta.mGroupName; - out << "]"; - return out; + out << "RsPhotoAlbum [ "; + out << "Title: " << album.mMeta.mGroupName; + out << "]"; + return out; } p3PhotoService::p3PhotoService(RsGeneralDataService* gds, RsNetworkExchangeService* nes, RsGixs* gixs) - : RsGenExchange(gds, nes, new RsGxsPhotoSerialiser(), RS_SERVICE_GXS_TYPE_PHOTO, gixs, photoAuthenPolicy()), - mPhotoMutex(std::string("Photo Mutex")) + : RsGenExchange(gds, nes, new RsGxsPhotoSerialiser(), RS_SERVICE_GXS_TYPE_PHOTO, gixs, photoAuthenPolicy()), + RsPhoto(static_cast(*this)), + mPhotoMutex(std::string("Photo Mutex")) { + mCommentService = new p3GxsCommentService(this, RS_SERVICE_GXS_TYPE_PHOTO); } const std::string GXS_PHOTO_APP_NAME = "gxsphoto"; -const uint16_t GXS_PHOTO_APP_MAJOR_VERSION = 1; -const uint16_t GXS_PHOTO_APP_MINOR_VERSION = 0; -const uint16_t GXS_PHOTO_MIN_MAJOR_VERSION = 1; -const uint16_t GXS_PHOTO_MIN_MINOR_VERSION = 0; +const uint16_t GXS_PHOTO_APP_MAJOR_VERSION = 1; +const uint16_t GXS_PHOTO_APP_MINOR_VERSION = 0; +const uint16_t GXS_PHOTO_MIN_MAJOR_VERSION = 1; +const uint16_t GXS_PHOTO_MIN_MINOR_VERSION = 0; RsServiceInfo p3PhotoService::getServiceInfo() { - return RsServiceInfo(RS_SERVICE_GXS_TYPE_PHOTO, - GXS_PHOTO_APP_NAME, - GXS_PHOTO_APP_MAJOR_VERSION, - GXS_PHOTO_APP_MINOR_VERSION, - GXS_PHOTO_MIN_MAJOR_VERSION, - GXS_PHOTO_MIN_MINOR_VERSION); + return RsServiceInfo(RS_SERVICE_GXS_TYPE_PHOTO, + GXS_PHOTO_APP_NAME, + GXS_PHOTO_APP_MAJOR_VERSION, + GXS_PHOTO_APP_MINOR_VERSION, + GXS_PHOTO_MIN_MAJOR_VERSION, + GXS_PHOTO_MIN_MINOR_VERSION); } - - uint32_t p3PhotoService::photoAuthenPolicy() { - uint32_t policy = 0; - uint8_t flag = 0; + uint32_t policy = 0; + uint8_t flag = 0; - flag = GXS_SERV::MSG_AUTHEN_ROOT_PUBLISH_SIGN | GXS_SERV::MSG_AUTHEN_CHILD_AUTHOR_SIGN; - RsGenExchange::setAuthenPolicyFlag(flag, policy, RsGenExchange::PUBLIC_GRP_BITS); + flag = GXS_SERV::MSG_AUTHEN_ROOT_PUBLISH_SIGN | GXS_SERV::MSG_AUTHEN_CHILD_AUTHOR_SIGN; + RsGenExchange::setAuthenPolicyFlag(flag, policy, RsGenExchange::PUBLIC_GRP_BITS); - flag |= GXS_SERV::MSG_AUTHEN_CHILD_PUBLISH_SIGN; - RsGenExchange::setAuthenPolicyFlag(flag, policy, RsGenExchange::RESTRICTED_GRP_BITS); - RsGenExchange::setAuthenPolicyFlag(flag, policy, RsGenExchange::PRIVATE_GRP_BITS); + flag |= GXS_SERV::MSG_AUTHEN_CHILD_PUBLISH_SIGN; + RsGenExchange::setAuthenPolicyFlag(flag, policy, RsGenExchange::RESTRICTED_GRP_BITS); + RsGenExchange::setAuthenPolicyFlag(flag, policy, RsGenExchange::PRIVATE_GRP_BITS); - flag = GXS_SERV::GRP_OPTION_AUTHEN_AUTHOR_SIGN; - RsGenExchange::setAuthenPolicyFlag(flag, policy, RsGenExchange::GRP_OPTION_BITS); + flag = GXS_SERV::GRP_OPTION_AUTHEN_AUTHOR_SIGN; + RsGenExchange::setAuthenPolicyFlag(flag, policy, RsGenExchange::GRP_OPTION_BITS); - return policy; + return policy; } bool p3PhotoService::updated() { - RsStackMutex stack(mPhotoMutex); + RsStackMutex stack(mPhotoMutex); - bool changed = (!mGroupChange.empty() || !mMsgChange.empty()); + bool changed = (!mGroupChange.empty() || !mMsgChange.empty()); - return changed; + return changed; } void p3PhotoService::service_tick() { - + mCommentService->comment_tick(); } - void p3PhotoService::groupsChanged(std::list& grpIds) { - RsStackMutex stack(mPhotoMutex); + RsStackMutex stack(mPhotoMutex); - while(!mGroupChange.empty()) - { - RsGxsGroupChange* gc = mGroupChange.back(); - std::list& gList = gc->mGrpIdList; - std::list::iterator lit = gList.begin(); - for(; lit != gList.end(); ++lit) - grpIds.push_back(*lit); + while(!mGroupChange.empty()) + { + RsGxsGroupChange* gc = mGroupChange.back(); + grpIds.push_back(gc->mGroupId); - mGroupChange.pop_back(); - delete gc; - } + mGroupChange.pop_back(); + delete gc; + } } void p3PhotoService::msgsChanged(GxsMsgIdResult& msgs) { - RsStackMutex stack(mPhotoMutex); + RsStackMutex stack(mPhotoMutex); - while(!mMsgChange.empty()) - { - RsGxsMsgChange* mc = mMsgChange.back(); - msgs = mc->msgChangeMap; - mMsgChange.pop_back(); - delete mc; - } + while(!mMsgChange.empty()) + { + RsGxsMsgChange* mc = mMsgChange.back(); + + msgs[mc->mGroupId].insert(mc->mMsgId); + mMsgChange.pop_back(); + delete mc; + } } @@ -210,7 +149,8 @@ RsTokenService* p3PhotoService::getTokenService() { bool p3PhotoService::getGroupList(const uint32_t& token, std::list& groupIds) { - return RsGenExchange::getGroupList(token, groupIds); + bool okay = RsGenExchange::getGroupList(token, groupIds); + return okay; } @@ -225,7 +165,8 @@ bool p3PhotoService::getMsgList(const uint32_t& token, bool p3PhotoService::getGroupSummary(const uint32_t& token, std::list& groupInfo) { - return RsGenExchange::getGroupMeta(token, groupInfo); + bool okay = RsGenExchange::getGroupMeta(token, groupInfo); + return okay; } @@ -304,92 +245,40 @@ bool p3PhotoService::getPhoto(const uint32_t& token, PhotoResult& photos) return ok; } -bool p3PhotoService::getPhotoComment(const uint32_t &token, PhotoCommentResult &comments) -{ - GxsMsgDataMap msgData; - bool ok = RsGenExchange::getMsgData(token, msgData); - - if(ok) - { - GxsMsgDataMap::iterator mit = msgData.begin(); - - for(; mit != msgData.end(); ++mit) - { - RsGxsGroupId grpId = mit->first; - std::vector& msgItems = mit->second; - std::vector::iterator vit = msgItems.begin(); - - for(; vit != msgItems.end(); ++vit) - { - RsGxsPhotoCommentItem* item = dynamic_cast(*vit); - - if(item) - { - RsPhotoComment comment = item->comment; - comment.mMeta = item->meta; - comments[grpId].push_back(comment); - delete item; - }else - { - std::cerr << "Not a comment Item, deleting!" << std::endl; - delete *vit; - } - } - } - } - - return ok; -} - -RsPhotoComment& RsPhotoComment::operator=(const RsGxsPhotoCommentItem& comment) -{ - *this = comment.comment; - return *this; -} - -bool p3PhotoService::getPhotoRelatedComment(const uint32_t &token, PhotoRelatedCommentResult &comments) -{ - - return RsGenExchange::getMsgRelatedDataT(token, comments); - -} - bool p3PhotoService::submitAlbumDetails(uint32_t& token, RsPhotoAlbum& album) { - RsGxsPhotoAlbumItem* albumItem = new RsGxsPhotoAlbumItem(); - albumItem->album = album; - albumItem->meta = album.mMeta; - RsGenExchange::publishGroup(token, albumItem); - return true; + RsGxsPhotoAlbumItem* albumItem = new RsGxsPhotoAlbumItem(); + albumItem->album = album; + albumItem->meta = album.mMeta; + RsGenExchange::publishGroup(token, albumItem); + return true; } - - void p3PhotoService::notifyChanges(std::vector& changes) { - RsStackMutex stack(mPhotoMutex); + RsStackMutex stack(mPhotoMutex); - std::vector::iterator vit = changes.begin(); + std::vector::iterator vit = changes.begin(); - for(; vit != changes.end(); ++vit) - { - RsGxsNotify* n = *vit; - RsGxsGroupChange* gc; - RsGxsMsgChange* mc; - if((mc = dynamic_cast(n)) != NULL) - { - mMsgChange.push_back(mc); - } - else if((gc = dynamic_cast(n)) != NULL) - { - mGroupChange.push_back(gc); - } - else - { - delete n; - } - } + for(; vit != changes.end(); ++vit) + { + RsGxsNotify* n = *vit; + RsGxsGroupChange* gc; + RsGxsMsgChange* mc; + if((mc = dynamic_cast(n)) != NULL) + { + mMsgChange.push_back(mc); + } + else if((gc = dynamic_cast(n)) != NULL) + { + mGroupChange.push_back(gc); + } + else + { + delete n; + } + } } bool p3PhotoService::submitPhoto(uint32_t& token, RsPhotoPhoto& photo) @@ -397,21 +286,9 @@ bool p3PhotoService::submitPhoto(uint32_t& token, RsPhotoPhoto& photo) RsGxsPhotoPhotoItem* photoItem = new RsGxsPhotoPhotoItem(); photoItem->photo = photo; photoItem->meta = photo.mMeta; - photoItem->meta.mMsgFlags = FLAG_MSG_TYPE_PHOTO_POST; - RsGenExchange::publishMsg(token, photoItem); - return true; -} - -bool p3PhotoService::submitComment(uint32_t &token, RsPhotoComment &comment) -{ - RsGxsPhotoCommentItem* commentItem = new RsGxsPhotoCommentItem(); - commentItem->comment = comment; - commentItem->meta = comment.mMeta; - commentItem->meta.mMsgFlags = FLAG_MSG_TYPE_PHOTO_COMMENT; - - RsGenExchange::publishMsg(token, commentItem); - return true; + RsGenExchange::publishMsg(token, photoItem); + return true; } bool p3PhotoService::acknowledgeMsg(const uint32_t& token, @@ -429,12 +306,46 @@ bool p3PhotoService::acknowledgeGrp(const uint32_t& token, bool p3PhotoService::subscribeToAlbum(uint32_t &token, const RsGxsGroupId &grpId, bool subscribe) { - if(subscribe) - RsGenExchange::setGroupSubscribeFlags(token, grpId, GXS_SERV::GROUP_SUBSCRIBE_SUBSCRIBED, GXS_SERV::GROUP_SUBSCRIBE_MASK); - else - RsGenExchange::setGroupSubscribeFlags(token, grpId, 0, GXS_SERV::GROUP_SUBSCRIBE_MASK); + if(subscribe) + RsGenExchange::setGroupSubscribeFlags(token, grpId, GXS_SERV::GROUP_SUBSCRIBE_SUBSCRIBED, GXS_SERV::GROUP_SUBSCRIBE_MASK); + else + RsGenExchange::setGroupSubscribeFlags(token, grpId, 0, GXS_SERV::GROUP_SUBSCRIBE_MASK); - return true; + return true; } +// Blocking versions ============================================================= + +bool p3PhotoService::createAlbum(RsPhotoAlbum &album) +{ + uint32_t token; + return submitAlbumDetails(token, album) && waitToken(token) == RsTokenService::COMPLETE; +} + +bool p3PhotoService::updateAlbum(const RsPhotoAlbum &album) +{ + // TODO + return false; +} + +bool p3PhotoService::getAlbums(const std::list &groupIds, + std::vector &albums) +{ + uint32_t token; + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; + + if (groupIds.empty()) + { + if (!requestGroupInfo(token, opts) || waitToken(token) != RsTokenService::COMPLETE ) + return false; + } + else + { + if (!requestGroupInfo(token, opts, groupIds) || waitToken(token) != RsTokenService::COMPLETE ) + return false; + } + + return getAlbum(token, albums) && !albums.empty(); +} diff --git a/libretroshare/src/services/p3photoservice.h b/libretroshare/src/services/p3photoservice.h index d5a5ba661..c8056e46a 100644 --- a/libretroshare/src/services/p3photoservice.h +++ b/libretroshare/src/services/p3photoservice.h @@ -24,122 +24,184 @@ #include "gxs/rsgenexchange.h" #include "retroshare/rsphoto.h" +#include "services/p3gxscommon.h" -class p3PhotoService : public RsPhoto, public RsGenExchange +class p3PhotoService : public RsGenExchange, public RsPhoto { public: - p3PhotoService(RsGeneralDataService* gds, RsNetworkExchangeService* nes, RsGixs* gixs); -virtual RsServiceInfo getServiceInfo(); + p3PhotoService(RsGeneralDataService* gds, RsNetworkExchangeService* nes, RsGixs* gixs); + virtual RsServiceInfo getServiceInfo(); -static uint32_t photoAuthenPolicy(); + static uint32_t photoAuthenPolicy(); public: - /*! - * @return true if a change has occured - */ - bool updated(); + /*! + * @return true if a change has occured + */ + bool updated(); - /*! - * - */ - void service_tick(); + /*! + * + */ + void service_tick(); protected: - void notifyChanges(std::vector& changes); + void notifyChanges(std::vector& changes); public: - /** Requests **/ + /** Requests **/ - void groupsChanged(std::list& grpIds); + void groupsChanged(std::list& grpIds); - void msgsChanged(GxsMsgIdResult& msgs); + void msgsChanged(GxsMsgIdResult& msgs); - RsTokenService* getTokenService(); + RsTokenService* getTokenService(); - bool getGroupList(const uint32_t &token, - std::list &groupIds); - bool getMsgList(const uint32_t &token, - GxsMsgIdResult& msgIds); + bool getGroupList(const uint32_t &token, std::list &groupIds); + bool getMsgList(const uint32_t &token, GxsMsgIdResult& msgIds); - /* Generic Summary */ - bool getGroupSummary(const uint32_t &token, - std::list &groupInfo); + /* Generic Summary */ + bool getGroupSummary(const uint32_t &token, std::list &groupInfo); - bool getMsgSummary(const uint32_t &token, - MsgMetaResult &msgInfo); + bool getMsgSummary(const uint32_t &token, MsgMetaResult &msgInfo); - /* Specific Service Data */ - bool getAlbum(const uint32_t &token, std::vector &albums); - bool getPhoto(const uint32_t &token, PhotoResult &photos); - bool getPhotoComment(const uint32_t &token, PhotoCommentResult &comments); - bool getPhotoRelatedComment(const uint32_t &token, PhotoRelatedCommentResult &comments); + /* Specific Service Data */ + bool getAlbum(const uint32_t &token, std::vector &albums); + bool getPhoto(const uint32_t &token, PhotoResult &photos); + +public: + /* Comment service - Provide RsGxsCommentService - redirect to p3GxsCommentService */ + virtual bool getCommentData(uint32_t token, std::vector &msgs) override + { + return mCommentService->getGxsCommentData(token, msgs); + } + + virtual bool getRelatedComments( uint32_t token, std::vector &msgs ) override + { + return mCommentService->getGxsRelatedComments(token, msgs); + } + + virtual bool createNewComment(uint32_t &token, const RsGxsComment &msg) override + { + return mCommentService->createGxsComment(token, msg); + } + + virtual bool createNewVote(uint32_t &token, RsGxsVote &msg) override + { + return mCommentService->createGxsVote(token, msg); + } + + virtual bool acknowledgeComment(uint32_t token, std::pair& msgId) override + { + return acknowledgeMsg(token, msgId); + } + + virtual bool acknowledgeVote(uint32_t token, std::pair& msgId) override + { + if (mCommentService->acknowledgeVote(token, msgId)) + { + return true; + } + return acknowledgeMsg(token, msgId); + } + + // Blocking versions. + virtual bool createComment(RsGxsComment &msg) override + { + uint32_t token; + return mCommentService->createGxsComment(token, msg) && waitToken(token) == RsTokenService::COMPLETE; + } public: - /** Modifications **/ + /** Modifications **/ - /*! - * submits album, which returns a token that needs - * to be acknowledge to get album grp id - * @param token token to redeem for acknowledgement - * @param album album to be submitted - */ - bool submitAlbumDetails(uint32_t& token, RsPhotoAlbum &album); + /*! + * submits album, which returns a token that needs + * to be acknowledge to get album grp id + * @param token token to redeem for acknowledgement + * @param album album to be submitted + */ + bool submitAlbumDetails(uint32_t& token, RsPhotoAlbum &album); - /*! - * submits photo, which returns a token that needs - * to be acknowledge to get photo msg-grp id pair - * @param token token to redeem for acknowledgement - * @param photo photo to be submitted - */ - bool submitPhoto(uint32_t& token, RsPhotoPhoto &photo); + /*! + * submits photo, which returns a token that needs + * to be acknowledge to get photo msg-grp id pair + * @param token token to redeem for acknowledgement + * @param photo photo to be submitted + */ + bool submitPhoto(uint32_t& token, RsPhotoPhoto &photo); - /*! - * submits photo comment, which returns a token that needs - * to be acknowledged to get photo msg-grp id pair - * The mParentId needs to be set to an existing msg for which - * commenting is enabled - * @param token token to redeem for acknowledgement - * @param comment comment to be submitted - */ - bool submitComment(uint32_t& token, RsPhotoComment &photo); + /*! + * submits photo comment, which returns a token that needs + * to be acknowledged to get photo msg-grp id pair + * The mParentId needs to be set to an existing msg for which + * commenting is enabled + * @param token token to redeem for acknowledgement + * @param comment comment to be submitted + */ + // bool submitComment(uint32_t& token, RsPhotoComment &photo); - /*! - * subscribes to group, and returns token which can be used - * to be acknowledged to get group Id - * @param token token to redeem for acknowledgement - * @param grpId the id of the group to subscribe to - */ - bool subscribeToAlbum(uint32_t& token, const RsGxsGroupId& grpId, bool subscribe); + /*! + * subscribes to group, and returns token which can be used + * to be acknowledged to get group Id + * @param token token to redeem for acknowledgement + * @param grpId the id of the group to subscribe to + */ + bool subscribeToAlbum(uint32_t& token, const RsGxsGroupId& grpId, bool subscribe); - /*! - * This allows the client service to acknowledge that their msgs has - * been created/modified and retrieve the create/modified msg ids - * @param token the token related to modification/create request - * @param msgIds map of grpid->msgIds of message created/modified - * @return true if token exists false otherwise - */ - bool acknowledgeMsg(const uint32_t& token, std::pair& msgId); + /*! + * This allows the client service to acknowledge that their msgs has + * been created/modified and retrieve the create/modified msg ids + * @param token the token related to modification/create request + * @param msgIds map of grpid->msgIds of message created/modified + * @return true if token exists false otherwise + */ + bool acknowledgeMsg(const uint32_t& token, std::pair& msgId); - /*! + /*! * This allows the client service to acknowledge that their grps has * been created/modified and retrieve the create/modified grp ids * @param token the token related to modification/create request * @param msgIds vector of ids of groups created/modified * @return true if token exists false otherwise */ - bool acknowledgeGrp(const uint32_t& token, RsGxsGroupId& grpId); + bool acknowledgeGrp(const uint32_t& token, RsGxsGroupId& grpId); + // Blocking versions. + /*! + * request to create a new album. Blocks until process completes. + * @param album album to be submitted + * @return true if created false otherwise + */ + virtual bool createAlbum(RsPhotoAlbum &album) override; + + /*! + * request to update an existing album. Blocks until process completes. + * @param album album to be submitted + * @return true if created false otherwise + */ + virtual bool updateAlbum(const RsPhotoAlbum &album) override; + + /*! + * retrieve albums based in groupIds. + * @param groupIds the ids to fetch. + * @param albums vector to be filled by request. + * @return true is successful, false otherwise. + */ + virtual bool getAlbums(const std::list &groupIds, + std::vector &albums) override; private: + p3GxsCommentService* mCommentService; - std::vector mGroupChange; - std::vector mMsgChange; + std::vector mGroupChange; + std::vector mMsgChange; - RsMutex mPhotoMutex; + RsMutex mPhotoMutex; }; #endif // P3PHOTOSERVICEV2_H diff --git a/libretroshare/src/services/p3postbase.cc b/libretroshare/src/services/p3postbase.cc index 309c3ee16..c4c96f3b5 100644 --- a/libretroshare/src/services/p3postbase.cc +++ b/libretroshare/src/services/p3postbase.cc @@ -98,27 +98,22 @@ void p3PostBase::notifyChanges(std::vector &changes) std::cerr << std::endl; #endif - std::map > &msgChangeMap = msgChange->msgChangeMap; - for(auto mit = msgChangeMap.begin(); mit != msgChangeMap.end(); ++mit) - { #ifdef POSTBASE_DEBUG - std::cerr << "p3PostBase::notifyChanges() Msgs for Group: " << mit->first; - std::cerr << std::endl; + std::cerr << "p3PostBase::notifyChanges() Msgs for Group: " << mit->first; + std::cerr << std::endl; #endif - // To start with we are just going to trigger updates on these groups. - // FUTURE OPTIMISATION. - // It could be taken a step further and directly request these msgs for an update. - addGroupForProcessing(mit->first); + // To start with we are just going to trigger updates on these groups. + // FUTURE OPTIMISATION. + // It could be taken a step further and directly request these msgs for an update. + addGroupForProcessing(msgChange->mGroupId); - if (rsEvents && (msgChange->getType() == RsGxsNotify::TYPE_RECEIVED_NEW || msgChange->getType() == RsGxsNotify::TYPE_PUBLISHED)) - for (auto mit1 = mit->second.begin(); mit1 != mit->second.end(); ++mit1) - { - auto ev = std::make_shared(); - ev->mPostedMsgId = *mit1; - ev->mPostedGroupId = mit->first; - ev->mPostedEventCode = RsPostedEventCode::NEW_MESSAGE; - rsEvents->postEvent(ev); - } + if (rsEvents && (msgChange->getType() == RsGxsNotify::TYPE_RECEIVED_NEW || msgChange->getType() == RsGxsNotify::TYPE_PUBLISHED)) + { + auto ev = std::make_shared(); + ev->mPostedMsgId = msgChange->mMsgId; + ev->mPostedGroupId = msgChange->mGroupId; + ev->mPostedEventCode = RsPostedEventCode::NEW_MESSAGE; + rsEvents->postEvent(ev); } } @@ -131,54 +126,63 @@ void p3PostBase::notifyChanges(std::vector &changes) std::cerr << "p3PostBase::notifyChanges() Found Group Change Notification"; std::cerr << std::endl; #endif + const RsGxsGroupId& group_id(grpChange->mGroupId); switch(grpChange->getType()) - { - default: + { case RsGxsNotify::TYPE_PROCESSED: // happens when the group is subscribed { - std::list &grpList = grpChange->mGrpIdList; - std::list::iterator git; - for (git = grpList.begin(); git != grpList.end(); ++git) - { - auto ev = std::make_shared(); - ev->mPostedGroupId = *git; - ev->mPostedEventCode = RsPostedEventCode::SUBSCRIBE_STATUS_CHANGED; - rsEvents->postEvent(ev); - } - + auto ev = std::make_shared(); + ev->mPostedGroupId = group_id; + ev->mPostedEventCode = RsPostedEventCode::SUBSCRIBE_STATUS_CHANGED; + rsEvents->postEvent(ev); } - break; + break; - case RsGxsNotify::TYPE_PUBLISHED: - case RsGxsNotify::TYPE_RECEIVED_NEW: - { - /* group received */ - const std::list& grpList = grpChange->mGrpIdList; + case RsGxsNotify::TYPE_STATISTICS_CHANGED: + { + auto ev = std::make_shared(); + ev->mPostedGroupId = group_id; + ev->mPostedEventCode = RsPostedEventCode::STATISTICS_CHANGED; + rsEvents->postEvent(ev); + } + break; + + case RsGxsNotify::TYPE_PUBLISHED: + case RsGxsNotify::TYPE_RECEIVED_NEW: + { + /* group received */ + + if(mKnownPosted.find(group_id) == mKnownPosted.end()) + { + mKnownPosted.insert(std::make_pair(group_id, time(nullptr))); + IndicateConfigChanged(); - for (auto git = grpList.begin(); git != grpList.end(); ++git) - { -#ifdef POSTBASE_DEBUG - std::cerr << "p3PostBase::notifyChanges() Incoming Group: " << *git; - std::cerr << std::endl; -#endif auto ev = std::make_shared(); - ev->mPostedGroupId = *git; + ev->mPostedGroupId = group_id; ev->mPostedEventCode = RsPostedEventCode::NEW_POSTED_GROUP; rsEvents->postEvent(ev); + +#ifdef POSTBASE_DEBUG + std::cerr << "p3PostBase::notifyChanges() Incoming Group: " << group_id; + std::cerr << std::endl; +#endif } - } + else + RsInfo() << __PRETTY_FUNCTION__ + << " Not notifying already known forum " + << group_id << std::endl; + } break; - } + + default: + RsErr() << " Got a GXS event of type " << grpChange->getType() << " Currently not handled." << std::endl; + break; + } } delete *it; } - -#ifdef POSTBASE_DEBUG - std::cerr << "p3PostBase::notifyChanges() -> receiveChanges()"; - std::cerr << std::endl; -#endif } void p3PostBase::service_tick() @@ -327,11 +331,7 @@ void p3PostBase::addGroupForProcessing(RsGxsGroupId grpId) { RsStackMutex stack(mPostBaseMtx); /********** STACK LOCKED MTX ******/ // no point having multiple lookups queued. - if (mBgGroupList.end() == std::find(mBgGroupList.begin(), - mBgGroupList.end(), grpId)) - { - mBgGroupList.push_back(grpId); - } + mBgGroupList.insert(grpId); } } @@ -364,8 +364,8 @@ void p3PostBase::background_requestUnprocessedGroup() return; } - grpId = mBgGroupList.front(); - mBgGroupList.pop_front(); + grpId = *mBgGroupList.begin(); + mBgGroupList.erase(grpId); mBgProcessing = true; } @@ -459,8 +459,6 @@ void p3PostBase::background_loadMsgs(const uint32_t &token, bool unprocessed) // generate vector of changes to push to the GUI. std::vector changes; - RsGxsMsgChange *msgChanges = new RsGxsMsgChange(RsGxsNotify::TYPE_PROCESSED, false); - RsGxsGroupId groupId; std::map >::iterator mit; @@ -511,7 +509,7 @@ void p3PostBase::background_loadMsgs(const uint32_t &token, bool unprocessed) #endif /* but we need to notify GUI about them */ - msgChanges->msgChangeMap[mit->first].insert((*vit)->meta.mMsgId); + changes.push_back(new RsGxsMsgChange(RsGxsNotify::TYPE_PROCESSED, mit->first,(*vit)->meta.mMsgId, false)); } else if (NULL != (commentItem = dynamic_cast(*vit))) { @@ -607,20 +605,7 @@ void p3PostBase::background_loadMsgs(const uint32_t &token, bool unprocessed) } /* push updates of new Posts */ - if (msgChanges->msgChangeMap.size() > 0) - { -#ifdef POSTBASE_DEBUG - std::cerr << "p3PostBase::background_processNewMessages() -> receiveChanges()"; - std::cerr << std::endl; -#endif - - changes.push_back(msgChanges); - receiveHelperChanges(changes); - } - else - { - delete(msgChanges); - } + notifyChanges(changes); /* request the summary info from the parents */ uint32_t token_b; @@ -685,7 +670,6 @@ void p3PostBase::background_updateVoteCounts(const uint32_t &token) // generate vector of changes to push to the GUI. std::vector changes; - RsGxsMsgChange *msgChanges = new RsGxsMsgChange(RsGxsNotify::TYPE_PROCESSED, false); for(mit = parentMsgList.begin(); mit != parentMsgList.end(); ++mit) { @@ -728,7 +712,8 @@ void p3PostBase::background_updateVoteCounts(const uint32_t &token) #endif stats.increment(it->second); - msgChanges->msgChangeMap[mit->first].insert(vit->mMsgId); + + changes.push_back(new RsGxsMsgChange(RsGxsNotify::TYPE_PROCESSED,mit->first,vit->mMsgId, false)); } else { @@ -760,20 +745,7 @@ void p3PostBase::background_updateVoteCounts(const uint32_t &token) } } - if (msgChanges->msgChangeMap.size() > 0) - { -#ifdef POSTBASE_DEBUG - std::cerr << "p3PostBase::background_updateVoteCounts() -> receiveChanges()"; - std::cerr << std::endl; -#endif - - changes.push_back(msgChanges); - receiveHelperChanges(changes); - } - else - { - delete(msgChanges); - } + notifyChanges(changes); // DONE!. background_cleanup(); @@ -830,3 +802,89 @@ void p3PostBase::handleResponse(uint32_t token, uint32_t req_type) } } +static const uint32_t GXS_POSTED_CONFIG_MAX_TIME_NOTIFY_STORAGE = 86400*30*2 ; // ignore notifications for 2 months +static const uint8_t GXS_POSTED_CONFIG_SUBTYPE_NOTIFY_RECORD = 0x01 ; + +struct RsGxsPostedNotifyRecordsItem: public RsItem +{ + + RsGxsPostedNotifyRecordsItem() + : RsItem(RS_PKT_VERSION_SERVICE,RS_SERVICE_GXS_TYPE_POSTED_CONFIG,GXS_POSTED_CONFIG_SUBTYPE_NOTIFY_RECORD) + {} + + virtual ~RsGxsPostedNotifyRecordsItem() {} + + void serial_process( RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx ) + { RS_SERIAL_PROCESS(records); } + + void clear() {} + + std::map records; +}; + +class GxsPostedConfigSerializer : public RsServiceSerializer +{ +public: + GxsPostedConfigSerializer() : RsServiceSerializer(RS_SERVICE_GXS_TYPE_POSTED_CONFIG) {} + virtual ~GxsPostedConfigSerializer() {} + + RsItem* create_item(uint16_t service_id, uint8_t item_sub_id) const + { + if(service_id != RS_SERVICE_GXS_TYPE_POSTED_CONFIG) + return NULL; + + switch(item_sub_id) + { + case GXS_POSTED_CONFIG_SUBTYPE_NOTIFY_RECORD: return new RsGxsPostedNotifyRecordsItem(); + default: + return NULL; + } + } +}; + +bool p3PostBase::saveList(bool &cleanup, std::list&saveList) +{ + cleanup = true ; + + RsGxsPostedNotifyRecordsItem *item = new RsGxsPostedNotifyRecordsItem ; + + item->records = mKnownPosted ; + + saveList.push_back(item) ; + return true; +} + +bool p3PostBase::loadList(std::list& loadList) +{ + while(!loadList.empty()) + { + RsItem *item = loadList.front(); + loadList.pop_front(); + + rstime_t now = time(NULL); + + RsGxsPostedNotifyRecordsItem *fnr = dynamic_cast(item) ; + + if(fnr != NULL) + { + mKnownPosted.clear(); + + for(auto it(fnr->records.begin());it!=fnr->records.end();++it) + if( now < it->second + GXS_POSTED_CONFIG_MAX_TIME_NOTIFY_STORAGE) + mKnownPosted.insert(*it) ; + } + + delete item ; + } + return true; +} + +RsSerialiser* p3PostBase::setupSerialiser() +{ + RsSerialiser* rss = new RsSerialiser; + rss->addSerialType(new GxsPostedConfigSerializer()); + + return rss; +} + diff --git a/libretroshare/src/services/p3postbase.h b/libretroshare/src/services/p3postbase.h index 1e63b0dcd..40804be1f 100644 --- a/libretroshare/src/services/p3postbase.h +++ b/libretroshare/src/services/p3postbase.h @@ -63,42 +63,44 @@ bool encodePostCache(std::string &str, const PostStats &s); bool extractPostCache(const std::string &str, PostStats &s); -class p3PostBase: public RsGenExchange, public GxsTokenQueue, public RsTickEvent +class p3PostBase: public RsGenExchange, public GxsTokenQueue, public RsTickEvent, public p3Config { - public: +public: p3PostBase(RsGeneralDataService *gds, RsNetworkExchangeService *nes, RsGixs* gixs, - RsSerialType* serviceSerialiser, uint16_t serviceType); + RsSerialType* serviceSerialiser, uint16_t serviceType); -virtual void service_tick(); + virtual void service_tick(); - // This should be overloaded to call RsGxsIfaceHelper::receiveChanges(). -virtual void receiveHelperChanges(std::vector& changes) = 0; +protected: - protected: + virtual void notifyChanges(std::vector& changes); -virtual void notifyChanges(std::vector& changes); + // Overloaded from GxsTokenQueue for Request callbacks. + virtual void handleResponse(uint32_t token, uint32_t req_type); - // Overloaded from GxsTokenQueue for Request callbacks. -virtual void handleResponse(uint32_t token, uint32_t req_type); + // Overloaded from RsTickEvent. + virtual void handle_event(uint32_t event_type, const std::string &elabel); - // Overloaded from RsTickEvent. -virtual void handle_event(uint32_t event_type, const std::string &elabel); + // overloads p3Config + virtual RsSerialiser* setupSerialiser() override; // @see p3Config::setupSerialiser() + virtual bool saveList(bool &cleanup, std::list&saveList) override; // @see p3Config::saveList(bool &cleanup, std::list&) + virtual bool loadList(std::list& loadList) override; // @see p3Config::loadList(std::list&) - public: +public: - ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// -virtual void setMessageReadStatus(uint32_t& token, const RsGxsGrpMsgIdPair& msgId, bool read); + virtual void setMessageReadStatus(uint32_t& token, const RsGxsGrpMsgIdPair& msgId, bool read); - protected: +protected: - p3GxsCommentService *mCommentService; + p3GxsCommentService *mCommentService; - private: +private: -static uint32_t postBaseAuthenPolicy(); + static uint32_t postBaseAuthenPolicy(); // Background processing. void background_tick(); @@ -119,13 +121,14 @@ static uint32_t postBaseAuthenPolicy(); bool background_cleanup(); - RsMutex mPostBaseMtx; + RsMutex mPostBaseMtx; bool mBgProcessing; bool mBgIncremental; - std::list mBgGroupList; - std::map mBgStatsMap; + std::set mBgGroupList; + std::map mBgStatsMap; + std::map mKnownPosted; }; #endif diff --git a/libretroshare/src/services/p3posted.cc b/libretroshare/src/services/p3posted.cc index 1a1efe0b6..88ff25808 100644 --- a/libretroshare/src/services/p3posted.cc +++ b/libretroshare/src/services/p3posted.cc @@ -30,7 +30,7 @@ * #define POSTED_DEBUG 1 ****/ -RsPosted *rsPosted = NULL; +/*extern*/ RsPosted* rsPosted = nullptr; /********************************************************************************/ /******************* Startup / Tick ******************************************/ @@ -94,55 +94,75 @@ bool p3Posted::getGroupData(const uint32_t &token, std::vector &g return ok; } -bool p3Posted::getPostData(const uint32_t &token, std::vector &msgs, std::vector &cmts) +bool p3Posted::getPostData( + const uint32_t &token, std::vector &msgs, + std::vector &cmts, + std::vector &vots) { #ifdef POSTED_DEBUG - std::cerr << "p3Posted::getPostData()"; - std::cerr << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << std::endl; #endif GxsMsgDataMap msgData; - bool ok = RsGenExchange::getMsgData(token, msgData); rstime_t now = time(NULL); - - if(ok) + if(!RsGenExchange::getMsgData(token, msgData)) { - GxsMsgDataMap::iterator mit = msgData.begin(); - - for(; mit != msgData.end(); ++mit) + RsErr() << __PRETTY_FUNCTION__ << " ERROR in request" << std::endl; + return false; + } + + GxsMsgDataMap::iterator mit = msgData.begin(); + + for(; mit != msgData.end(); ++mit) + { + std::vector& msgItems = mit->second; + std::vector::iterator vit = msgItems.begin(); + + for(; vit != msgItems.end(); ++vit) { - std::vector& msgItems = mit->second; - std::vector::iterator vit = msgItems.begin(); - - for(; vit != msgItems.end(); ++vit) + RsGxsPostedPostItem* postItem = + dynamic_cast(*vit); + + if(postItem) { - RsGxsPostedPostItem* postItem = dynamic_cast(*vit); + // TODO Really needed all of these lines? + RsPostedPost msg = postItem->mPost; + msg.mMeta = postItem->meta; + postItem->toPostedPost(msg, true); + msg.calculateScores(now); - if(postItem) + msgs.push_back(msg); + delete postItem; + } + else + { + RsGxsCommentItem* cmtItem = + dynamic_cast(*vit); + if(cmtItem) { - RsPostedPost msg = postItem->mPost; - msg.mMeta = postItem->meta; - postItem->toPostedPost(msg, true); - msg.calculateScores(now); - - msgs.push_back(msg); - delete postItem; + RsGxsComment cmt; + RsGxsMsgItem *mi = (*vit); + cmt = cmtItem->mMsg; + cmt.mMeta = mi->meta; +#ifdef GXSCOMMENT_DEBUG + RsDbg() << __PRETTY_FUNCTION__ << " Found Comment:" << std::endl; + cmt.print(std::cerr," ", "cmt"); +#endif + cmts.push_back(cmt); + delete cmtItem; } else { - RsGxsCommentItem* cmtItem = dynamic_cast(*vit); - if(cmtItem) + RsGxsVoteItem* votItem = + dynamic_cast(*vit); + if(votItem) { - RsGxsComment cmt; + RsGxsVote vot; RsGxsMsgItem *mi = (*vit); - cmt = cmtItem->mMsg; - cmt.mMeta = mi->meta; -#ifdef GXSCOMMENT_DEBUG - std::cerr << "p3Posted::getPostData Found Comment:" << std::endl; - cmt.print(std::cerr," ", "cmt"); -#endif - cmts.push_back(cmt); - delete cmtItem; + vot = votItem->mMsg; + vot.mMeta = mi->meta; + vots.push_back(vot); + delete votItem; } else { @@ -150,24 +170,37 @@ bool p3Posted::getPostData(const uint32_t &token, std::vector &msg //const uint16_t RS_SERVICE_GXS_TYPE_CHANNELS = 0x0217; //const uint8_t RS_PKT_SUBTYPE_GXSCHANNEL_POST_ITEM = 0x03; //const uint8_t RS_PKT_SUBTYPE_GXSCOMMENT_COMMENT_ITEM = 0xf1; - std::cerr << "Not a PostedPostItem neither a RsGxsCommentItem" - << " PacketService=" << std::hex << (int)msg->PacketService() << std::dec - << " PacketSubType=" << std::hex << (int)msg->PacketSubType() << std::dec - << " type name =" << typeid(*msg).name() - << " , deleting!" << std::endl; + //const uint8_t RS_PKT_SUBTYPE_GXSCOMMENT_VOTE_ITEM = 0xf2; + RsErr() << __PRETTY_FUNCTION__ + << "Not a PostedPostItem neither a " + << "RsGxsCommentItem neither a RsGxsVoteItem" + << " PacketService=" << std::hex << (int)msg->PacketService() << std::dec + << " PacketSubType=" << std::hex << (int)msg->PacketSubType() << std::dec + << " type name =" << typeid(*msg).name() + << " , deleting!" << std::endl; delete *vit; } } } } } - else - { - std::cerr << "p3GxsChannels::getPostData() ERROR in request"; - std::cerr << std::endl; - } - return ok; + return true; +} + +bool p3Posted::getPostData( + const uint32_t &token, std::vector &posts, std::vector &cmts) +{ + std::vector vots; + return getPostData( token, posts, cmts, vots); +} + +bool p3Posted::getPostData( + const uint32_t &token, std::vector &posts) +{ + std::vector cmts; + std::vector vots; + return getPostData( token, posts, cmts, vots); } //Not currently used @@ -300,4 +333,138 @@ bool p3Posted::createPost(uint32_t &token, RsPostedPost &msg) return true; } -/********************************************************************************************/ +bool p3Posted::getBoardsInfo( + const std::list& boardsIds, + std::vector& groupsInfo ) +{ + uint32_t token; + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; + + if(boardsIds.empty()) + { + if( !requestGroupInfo(token, opts) || waitToken(token) != RsTokenService::COMPLETE ) + return false; + } + else + { + if( !requestGroupInfo(token, opts, boardsIds) || waitToken(token) != RsTokenService::COMPLETE ) + return false; + } + + return getGroupData(token, groupsInfo) && !groupsInfo.empty(); +} + +bool p3Posted::getBoardAllContent( const RsGxsGroupId& groupId, + std::vector& posts, + std::vector& comments, + std::vector& votes ) +{ + uint32_t token; + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; + + if( !requestMsgInfo(token, opts, std::list({groupId})) || waitToken(token) != RsTokenService::COMPLETE ) + return false; + + return getPostData(token, posts, comments, votes); +} + +bool p3Posted::getBoardContent( const RsGxsGroupId& groupId, + const std::set& contentsIds, + std::vector& posts, + std::vector& comments, + std::vector& votes ) +{ + uint32_t token; + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; + + GxsMsgReq msgIds; + msgIds[groupId] = contentsIds; + + if( !requestMsgInfo(token, opts, msgIds) || + waitToken(token) != RsTokenService::COMPLETE ) return false; + + return getPostData(token, posts, comments, votes); +} + +bool p3Posted::getBoardsSummaries(std::list& boards ) +{ + uint32_t token; + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_GROUP_META; + if( !requestGroupInfo(token, opts) || waitToken(token) != RsTokenService::COMPLETE ) return false; + + return getGroupSummary(token, boards); +} + +bool p3Posted::getBoardsServiceStatistics(GxsServiceStatistic& stat) +{ + uint32_t token; + if(!RsGxsIfaceHelper::requestServiceStatistic(token) || waitToken(token) != RsTokenService::COMPLETE) + return false; + + return RsGenExchange::getServiceStatistic(token,stat); +} + + +bool p3Posted::getBoardStatistics(const RsGxsGroupId& boardId,GxsGroupStatistic& stat) +{ + uint32_t token; + if(!RsGxsIfaceHelper::requestGroupStatistic(token, boardId) || waitToken(token) != RsTokenService::COMPLETE) + return false; + + return RsGenExchange::getGroupStatistic(token,stat); +} + +bool p3Posted::createBoard(RsPostedGroup& board) +{ + uint32_t token; + if(!createGroup(token, board)) + { + std::cerr << __PRETTY_FUNCTION__ << "Error! Failed creating group." << std::endl; + return false; + } + + if(waitToken(token,std::chrono::milliseconds(5000)) != RsTokenService::COMPLETE) + { + std::cerr << __PRETTY_FUNCTION__ << "Error! GXS operation failed." << std::endl; + return false; + } + + if(!RsGenExchange::getPublishedGroupMeta(token, board.mMeta)) + { + std::cerr << __PRETTY_FUNCTION__ << "Error! Failure getting updated " << " group data." << std::endl; + return false; + } + + return true; +} + +bool p3Posted::editBoard(RsPostedGroup& board) +{ + uint32_t token; + if(!updateGroup(token, board)) + { + std::cerr << __PRETTY_FUNCTION__ << " Error! Failed updating group." << std::endl; + return false; + } + + if(waitToken(token) != RsTokenService::COMPLETE) + { + std::cerr << __PRETTY_FUNCTION__ << " Error! GXS operation failed." << std::endl; + return false; + } + + if(!RsGenExchange::getPublishedGroupMeta(token, board.mMeta)) + { + std::cerr << __PRETTY_FUNCTION__ << " Error! Failure getting updated " << " group data." << std::endl; + return false; + } + + return true; +} + +RsPosted::~RsPosted() = default; +RsGxsPostedEvent::~RsGxsPostedEvent() = default; diff --git a/libretroshare/src/services/p3posted.h b/libretroshare/src/services/p3posted.h index 402f69298..bc213ac0d 100644 --- a/libretroshare/src/services/p3posted.h +++ b/libretroshare/src/services/p3posted.h @@ -39,12 +39,12 @@ class p3Posted: public p3PostBase, public RsPosted { - public: +public: p3Posted(RsGeneralDataService* gds, RsNetworkExchangeService* nes, RsGixs* gixs); -virtual RsServiceInfo getServiceInfo(); + virtual RsServiceInfo getServiceInfo(); - protected: +protected: virtual void notifyChanges(std::vector& changes) { @@ -53,15 +53,41 @@ virtual void notifyChanges(std::vector& changes) public: +#ifdef TO_REMOVE virtual void receiveHelperChanges(std::vector& changes) { return RsGxsIfaceHelper::receiveChanges(changes); } +#endif - // Posted Specific DataTypes. -virtual bool getGroupData(const uint32_t &token, std::vector &groups); -virtual bool getPostData(const uint32_t &token, std::vector &posts, std::vector &cmts); -virtual bool getPostData(const uint32_t &token, std::vector &posts) { std::vector cmts; return getPostData( token, posts, cmts);} + bool getBoardsInfo(const std::list& boardsIds, + std::vector& groupsInfo ) override; + + bool getBoardAllContent(const RsGxsGroupId& groupId, + std::vector& posts, + std::vector& comments, + std::vector& votes ) override; + + bool getBoardContent(const RsGxsGroupId& groupId, + const std::set& contentsIds, + std::vector& posts, + std::vector& comments, + std::vector& votes ) override; + + bool getBoardsSummaries(std::list& groupInfo) override; + + bool getBoardStatistics(const RsGxsGroupId& boardId,GxsGroupStatistic& stat) override; + + bool getBoardsServiceStatistics(GxsServiceStatistic& stat) override; + + bool editBoard(RsPostedGroup& board) override; + + bool createBoard(RsPostedGroup& board) override; + + virtual bool getGroupData(const uint32_t &token, std::vector &groups) override; + virtual bool getPostData(const uint32_t &token, std::vector &posts, std::vector &cmts, std::vector &vots) override; + virtual bool getPostData(const uint32_t &token, std::vector &posts, std::vector &cmts) override; + virtual bool getPostData(const uint32_t &token, std::vector &posts) override; //Not currently used //virtual bool getRelatedPosts(const uint32_t &token, std::vector &posts); @@ -89,10 +115,16 @@ virtual void setMessageReadStatus(uint32_t& token, const RsGxsGrpMsgIdPair& msgI std::vector &msgs ) { return mCommentService->getGxsRelatedComments(token, msgs); } - virtual bool createNewComment(uint32_t &token, RsGxsComment &msg) + virtual bool createNewComment(uint32_t &token, const RsGxsComment &msg) override { return mCommentService->createGxsComment(token, msg); } + virtual bool createComment(RsGxsComment& msg) override + { + uint32_t token; + + return mCommentService->createGxsComment(token, msg) && waitToken(token) == RsTokenService::COMPLETE ; + } virtual bool createNewVote(uint32_t &token, RsGxsVote &msg) { diff --git a/libretroshare/src/services/p3wiki.cc b/libretroshare/src/services/p3wiki.cc index 26c33881a..c1b8f86e1 100644 --- a/libretroshare/src/services/p3wiki.cc +++ b/libretroshare/src/services/p3wiki.cc @@ -40,7 +40,7 @@ RsWiki *rsWiki = NULL; p3Wiki::p3Wiki(RsGeneralDataService* gds, RsNetworkExchangeService* nes, RsGixs *gixs) :RsGenExchange(gds, nes, new RsGxsWikiSerialiser(), RS_SERVICE_GXS_TYPE_WIKI, gixs, wikiAuthenPolicy()), - RsWiki(this) + RsWiki(static_cast(*this)) { // Setup of dummy Pages. mAboutActive = false; @@ -102,8 +102,6 @@ void p3Wiki::notifyChanges(std::vector& changes) { std::cerr << "p3Wiki::notifyChanges() New stuff"; std::cerr << std::endl; - - RsGxsIfaceHelper::receiveChanges(changes); } /* Specific Service Data */ @@ -325,6 +323,38 @@ bool p3Wiki::updateCollection(uint32_t &token, RsWikiCollection &group) return true; } +// Blocking Interfaces. +bool p3Wiki::createCollection(RsWikiCollection &group) +{ + uint32_t token; + return submitCollection(token, group) && waitToken(token) == RsTokenService::COMPLETE; +} + +bool p3Wiki::updateCollection(const RsWikiCollection &group) +{ + uint32_t token; + RsWikiCollection update(group); + return updateCollection(token, update) && waitToken(token) == RsTokenService::COMPLETE; +} + +bool p3Wiki::getCollections(const std::list groupIds, std::vector &groups) +{ + uint32_t token; + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; + + if (groupIds.empty()) + { + if (!requestGroupInfo(token, opts) || waitToken(token) != RsTokenService::COMPLETE ) + return false; + } + else + { + if (!requestGroupInfo(token, opts, groupIds) || waitToken(token) != RsTokenService::COMPLETE ) + return false; + } + return getCollections(token, groups) && !groups.empty(); +} std::ostream &operator<<(std::ostream &out, const RsWikiCollection &group) { diff --git a/libretroshare/src/services/p3wiki.h b/libretroshare/src/services/p3wiki.h index f6f446920..a92aec795 100644 --- a/libretroshare/src/services/p3wiki.h +++ b/libretroshare/src/services/p3wiki.h @@ -56,17 +56,22 @@ public: virtual void service_tick(); /* Specific Service Data */ -virtual bool getCollections(const uint32_t &token, std::vector &collections); -virtual bool getSnapshots(const uint32_t &token, std::vector &snapshots); -virtual bool getComments(const uint32_t &token, std::vector &comments); +virtual bool getCollections(const uint32_t &token, std::vector &collections) override; +virtual bool getSnapshots(const uint32_t &token, std::vector &snapshots) override; +virtual bool getComments(const uint32_t &token, std::vector &comments) override; -virtual bool getRelatedSnapshots(const uint32_t &token, std::vector &snapshots); +virtual bool getRelatedSnapshots(const uint32_t &token, std::vector &snapshots) override; -virtual bool submitCollection(uint32_t &token, RsWikiCollection &collection); -virtual bool submitSnapshot(uint32_t &token, RsWikiSnapshot &snapshot); -virtual bool submitComment(uint32_t &token, RsWikiComment &comment); +virtual bool submitCollection(uint32_t &token, RsWikiCollection &collection) override; +virtual bool submitSnapshot(uint32_t &token, RsWikiSnapshot &snapshot) override; +virtual bool submitComment(uint32_t &token, RsWikiComment &comment) override; -virtual bool updateCollection(uint32_t &token, RsWikiCollection &collection); +virtual bool updateCollection(uint32_t &token, RsWikiCollection &collection) override; + +// Blocking Interfaces. +virtual bool createCollection(RsWikiCollection &collection) override; +virtual bool updateCollection(const RsWikiCollection &collection) override; +virtual bool getCollections(const std::list groupIds, std::vector &groups) override; private: diff --git a/libretroshare/src/services/p3wire.cc b/libretroshare/src/services/p3wire.cc index 23d31198f..283e9a804 100644 --- a/libretroshare/src/services/p3wire.cc +++ b/libretroshare/src/services/p3wire.cc @@ -3,7 +3,7 @@ * * * libretroshare: retroshare core library * * * - * Copyright 2012-2012 by Robert Fernie * + * Copyright 2012-2020 by Robert Fernie * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -30,10 +30,34 @@ RsWire *rsWire = NULL; +RsWireGroup::RsWireGroup() + :mGroupPulses(0),mGroupRepublishes(0),mGroupLikes(0),mGroupReplies(0) + ,mRefMentions(0),mRefRepublishes(0),mRefLikes(0),mRefReplies(0) +{ + return; +} + +uint32_t RsWirePulse::ImageCount() +{ + uint32_t images = 0; + if (!mImage1.empty()) { + images++; + } + if (!mImage2.empty()) { + images++; + } + if (!mImage3.empty()) { + images++; + } + if (!mImage4.empty()) { + images++; + } + return images; +} p3Wire::p3Wire(RsGeneralDataService* gds, RsNetworkExchangeService* nes, RsGixs *gixs) :RsGenExchange(gds, nes, new RsGxsWireSerialiser(), RS_SERVICE_GXS_TYPE_WIRE, gixs, wireAuthenPolicy()), - RsWire(this), mWireMtx("WireMtx") + RsWire(static_cast(*this)), mWireMtx("WireMtx") { } @@ -47,12 +71,12 @@ const uint16_t WIRE_MIN_MINOR_VERSION = 0; RsServiceInfo p3Wire::getServiceInfo() { - return RsServiceInfo(RS_SERVICE_GXS_TYPE_WIRE, - WIRE_APP_NAME, - WIRE_APP_MAJOR_VERSION, - WIRE_APP_MINOR_VERSION, - WIRE_MIN_MAJOR_VERSION, - WIRE_MIN_MINOR_VERSION); + return RsServiceInfo(RS_SERVICE_GXS_TYPE_WIRE, + WIRE_APP_NAME, + WIRE_APP_MAJOR_VERSION, + WIRE_APP_MINOR_VERSION, + WIRE_MIN_MAJOR_VERSION, + WIRE_MIN_MINOR_VERSION); } @@ -64,11 +88,14 @@ uint32_t p3Wire::wireAuthenPolicy() // Edits generally need an authors signature. + // Wire requires all TopLevel (Orig/Reply) msgs to be signed with both PUBLISH & AUTHOR. + // Reply References need to be signed by Author. flag = GXS_SERV::MSG_AUTHEN_ROOT_PUBLISH_SIGN | GXS_SERV::MSG_AUTHEN_CHILD_AUTHOR_SIGN; + flag |= GXS_SERV::MSG_AUTHEN_ROOT_AUTHOR_SIGN; RsGenExchange::setAuthenPolicyFlag(flag, policy, RsGenExchange::PUBLIC_GRP_BITS); - flag |= GXS_SERV::MSG_AUTHEN_ROOT_AUTHOR_SIGN; - flag |= GXS_SERV::MSG_AUTHEN_CHILD_PUBLISH_SIGN; + // expect the requirements to be the same for RESTRICTED / PRIVATE groups too. + // This needs to be worked through / fully evaluated. RsGenExchange::setAuthenPolicyFlag(flag, policy, RsGenExchange::RESTRICTED_GRP_BITS); RsGenExchange::setAuthenPolicyFlag(flag, policy, RsGenExchange::PRIVATE_GRP_BITS); @@ -83,16 +110,18 @@ void p3Wire::service_tick() return; } +RsTokenService* p3Wire::getTokenService() { -void p3Wire::notifyChanges(std::vector& changes) + return RsGenExchange::getTokenService(); +} + +void p3Wire::notifyChanges(std::vector& /*changes*/) { std::cerr << "p3Wire::notifyChanges() New stuff"; std::cerr << std::endl; - - RsGxsIfaceHelper::receiveChanges(changes); } - /* Specific Service Data */ + /* Specific Service Data */ bool p3Wire::getGroupData(const uint32_t &token, std::vector &groups) { std::cerr << "p3Wire::getGroupData()"; @@ -132,6 +161,45 @@ bool p3Wire::getGroupData(const uint32_t &token, std::vector &group return ok; } +bool p3Wire::getGroupPtrData(const uint32_t &token, std::map &groups) +{ + std::cerr << "p3Wire::getGroupPtrData()"; + std::cerr << std::endl; + + std::vector grpData; + bool ok = RsGenExchange::getGroupData(token, grpData); + + if(ok) + { + std::vector::iterator vit = grpData.begin(); + + for(; vit != grpData.end(); ++vit) + { + RsGxsWireGroupItem* item = dynamic_cast(*vit); + + if (item) + { + RsWireGroupSPtr pGroup = std::make_shared(item->group); + pGroup->mMeta = item->meta; + delete item; + + groups[pGroup->mMeta.mGroupId] = pGroup; + + std::cerr << "p3Wire::getGroupPtrData() Adding WireGroup to Vector: "; + std::cerr << pGroup->mMeta.mGroupId.toStdString(); + std::cerr << std::endl; + } + else + { + std::cerr << "Not a WireGroupItem, deleting!" << std::endl; + delete *vit; + } + + } + } + return ok; +} + bool p3Wire::getPulseData(const uint32_t &token, std::vector &pulses) { @@ -144,7 +212,6 @@ bool p3Wire::getPulseData(const uint32_t &token, std::vector &pulse for(; mit != msgData.end(); ++mit) { - RsGxsGroupId grpId = mit->first; std::vector& msgItems = mit->second; std::vector::iterator vit = msgItems.begin(); @@ -161,7 +228,7 @@ bool p3Wire::getPulseData(const uint32_t &token, std::vector &pulse } else { - std::cerr << "Not a WikiPulse Item, deleting!" << std::endl; + std::cerr << "Not a WirePulse Item, deleting!" << std::endl; delete *vit; } } @@ -170,6 +237,88 @@ bool p3Wire::getPulseData(const uint32_t &token, std::vector &pulse return ok; } +bool p3Wire::getPulsePtrData(const uint32_t &token, std::list &pulses) +{ + GxsMsgDataMap msgData; + bool ok = RsGenExchange::getMsgData(token, msgData); + + if(ok) + { + GxsMsgDataMap::iterator mit = msgData.begin(); + + for(; mit != msgData.end(); ++mit) + { + std::vector& msgItems = mit->second; + std::vector::iterator vit = msgItems.begin(); + + for(; vit != msgItems.end(); ++vit) + { + RsGxsWirePulseItem* item = dynamic_cast(*vit); + + if(item) + { + RsWirePulseSPtr pPulse = std::make_shared(item->pulse); + pPulse->mMeta = item->meta; + pulses.push_back(pPulse); + delete item; + } + else + { + std::cerr << "Not a WirePulse Item, deleting!" << std::endl; + delete *vit; + } + } + } + } + return ok; +} + +bool p3Wire::getRelatedPulseData(const uint32_t &token, std::vector &pulses) +{ + GxsMsgRelatedDataMap msgData; + std::cerr << "p3Wire::getRelatedPulseData()"; + std::cerr << std::endl; + bool ok = RsGenExchange::getMsgRelatedData(token, msgData); + + if (ok) + { + std::cerr << "p3Wire::getRelatedPulseData() is OK"; + std::cerr << std::endl; + GxsMsgRelatedDataMap::iterator mit = msgData.begin(); + + for(; mit != msgData.end(); ++mit) + { + std::vector& msgItems = mit->second; + std::vector::iterator vit = msgItems.begin(); + + for(; vit != msgItems.end(); ++vit) + { + RsGxsWirePulseItem* item = dynamic_cast(*vit); + + if(item) + { + RsWirePulse pulse = item->pulse; + pulse.mMeta = item->meta; + pulses.push_back(pulse); + delete item; + } + else + { + std::cerr << "Not a WirePulse Item, deleting!" << std::endl; + delete *vit; + } + } + } + } + else + { + std::cerr << "p3Wire::getRelatedPulseData() is NOT OK"; + std::cerr << std::endl; + } + + return ok; +} + bool p3Wire::createGroup(uint32_t &token, RsWireGroup &group) { @@ -177,12 +326,12 @@ bool p3Wire::createGroup(uint32_t &token, RsWireGroup &group) groupItem->group = group; groupItem->meta = group.mMeta; - std::cerr << "p3Wire::createGroup(): "; + std::cerr << "p3Wire::createGroup(): "; std::cerr << std::endl; std::cerr << group; std::cerr << std::endl; - std::cerr << "p3Wire::createGroup() pushing to RsGenExchange"; + std::cerr << "p3Wire::createGroup() pushing to RsGenExchange"; std::cerr << std::endl; RsGenExchange::publishGroup(token, groupItem); @@ -195,45 +344,81 @@ bool p3Wire::createPulse(uint32_t &token, RsWirePulse &pulse) std::cerr << "p3Wire::createPulse(): " << pulse; std::cerr << std::endl; - RsGxsWirePulseItem* pulseItem = new RsGxsWirePulseItem(); - pulseItem->pulse = pulse; - pulseItem->meta = pulse.mMeta; + RsGxsWirePulseItem* pulseItem = new RsGxsWirePulseItem(); + pulseItem->pulse = pulse; + pulseItem->meta = pulse.mMeta; - RsGenExchange::publishMsg(token, pulseItem); + RsGenExchange::publishMsg(token, pulseItem); return true; } +// Blocking Interfaces. +bool p3Wire::createGroup(RsWireGroup &group) +{ + uint32_t token; + return createGroup(token, group) && waitToken(token) == RsTokenService::COMPLETE; +} + +bool p3Wire::updateGroup(const RsWireGroup & /*group*/) +{ + // TODO + return false; +} + +bool p3Wire::getGroups(const std::list groupIds, std::vector &groups) +{ + uint32_t token; + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; + + if (groupIds.empty()) + { + if (!requestGroupInfo(token, opts) || waitToken(token) != RsTokenService::COMPLETE ) + { + return false; + } + } + else + { + if (!requestGroupInfo(token, opts, groupIds) || waitToken(token) != RsTokenService::COMPLETE ) + { + return false; + } + } + return getGroupData(token, groups) && !groups.empty(); +} + std::ostream &operator<<(std::ostream &out, const RsWireGroup &group) { - out << "RsWireGroup [ "; - out << " Name: " << group.mMeta.mGroupName; - out << " Desc: " << group.mDescription; - //out << " Category: " << group.mCategory; - out << " ]"; - return out; + out << "RsWireGroup [ "; + out << " Name: " << group.mMeta.mGroupName; + out << " Tagline: " << group.mTagline; + out << " Location: " << group.mLocation; + out << " ]"; + return out; } std::ostream &operator<<(std::ostream &out, const RsWirePulse &pulse) { - out << "RsWirePulse [ "; - out << "Title: " << pulse.mMeta.mMsgName; - out << "PulseText: " << pulse.mPulseText; - out << "]"; - return out; + out << "RsWirePulse [ "; + out << "Title: " << pulse.mMeta.mMsgName; + out << "PulseText: " << pulse.mPulseText; + out << "]"; + return out; } /***** FOR TESTING *****/ std::string p3Wire::genRandomId() { - std::string randomId; - for(int i = 0; i < 20; i++) - { - randomId += (char) ('a' + (RSRandom::random_u32() % 26)); - } + std::string randomId; + for(int i = 0; i < 20; i++) + { + randomId += (char) ('a' + (RSRandom::random_u32() % 26)); + } - return randomId; + return randomId; } void p3Wire::generateDummyData() @@ -242,3 +427,925 @@ void p3Wire::generateDummyData() } +// New Interfaces. +bool p3Wire::fetchPulse(RsGxsGroupId grpId, RsGxsMessageId msgId, RsWirePulseSPtr &pPulse) +{ + std::cerr << "p3Wire::fetchPulse(" << grpId << ", " << msgId << ") waiting for token"; + std::cerr << std::endl; + + uint32_t token; + { + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; + opts.mOptions = RS_TOKREQOPT_MSG_LATEST; + + GxsMsgReq ids; + std::set &set_msgIds = ids[grpId]; + set_msgIds.insert(msgId); + + getTokenService()->requestMsgInfo( + token, RS_TOKREQ_ANSTYPE_DATA, opts, ids); + } + + // wait for pulse request to completed. + std::cerr << "p3Wire::fetchPulse() waiting for token"; + std::cerr << std::endl; + + int result = waitToken(token); + if (result != RsTokenService::COMPLETE) + { + std::cerr << "p3Wire::fetchPulse() token FAILED, result: " << result; + std::cerr << std::endl; + return false; + } + + // retrieve Pulse. + std::cerr << "p3Wire::fetchPulse() retrieving token"; + std::cerr << std::endl; + { + bool okay = true; + std::vector pulses; + if (getPulseData(token, pulses)) { + if (pulses.size() == 1) { + // save to output pulse. + pPulse = std::make_shared(pulses[0]); + std::cerr << "p3Wire::fetchPulse() retrieved token: " << *pPulse; + std::cerr << std::endl; + std::cerr << "p3Wire::fetchPulse() ANS GrpId: " << pPulse->mMeta.mGroupId; + std::cerr << " MsgId: " << pPulse->mMeta.mMsgId; + std::cerr << " OrigMsgId: " << pPulse->mMeta.mOrigMsgId; + std::cerr << std::endl; + } else { + std::cerr << "p3Wire::fetchPulse() ERROR multiple pulses"; + std::cerr << std::endl; + okay = false; + } + } else { + std::cerr << "p3Wire::fetchPulse() ERROR failed to retrieve token"; + std::cerr << std::endl; + okay = false; + } + + if (!okay) { + std::cerr << "p3Wire::fetchPulse() tokenPulse ERROR"; + std::cerr << std::endl; + // TODO cancel other request. + return false; + } + } + return true; +} + +// New Interfaces. +bool p3Wire::createOriginalPulse(const RsGxsGroupId &grpId, RsWirePulseSPtr pPulse) +{ + // request Group. + std::list groupIds = { grpId }; + std::vector groups; + bool groupOkay = getGroups(groupIds, groups); + if (!groupOkay) { + std::cerr << "p3Wire::createOriginalPulse() getGroups failed"; + std::cerr << std::endl; + return false; + } + + if (groups.size() != 1) { + std::cerr << "p3Wire::createOriginalPulse() getGroups invalid size"; + std::cerr << std::endl; + return false; + } + + // ensure Group is suitable. + RsWireGroup group = groups[0]; + if ((group.mMeta.mGroupId != grpId) || + (!(group.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_PUBLISH))) + { + std::cerr << "p3Wire::createOriginalPulse() Group unsuitable"; + std::cerr << std::endl; + return false; + } + + // Create Msg. + // Start fresh, just to be sure nothing dodgy happens in UX world. + RsWirePulse pulse; + + pulse.mMeta.mGroupId = group.mMeta.mGroupId; + pulse.mMeta.mAuthorId = group.mMeta.mAuthorId; + pulse.mMeta.mThreadId.clear(); + pulse.mMeta.mParentId.clear(); + pulse.mMeta.mOrigMsgId.clear(); + + // copy info over + pulse.mPulseType = WIRE_PULSE_TYPE_ORIGINAL; + pulse.mSentiment = pPulse->mSentiment; + pulse.mPulseText = pPulse->mPulseText; + pulse.mImage1 = pPulse->mImage1; + pulse.mImage2 = pPulse->mImage2; + pulse.mImage3 = pPulse->mImage3; + pulse.mImage4 = pPulse->mImage4; + + // all mRefs should empty. + + uint32_t token; + createPulse(token, pulse); + + int result = waitToken(token); + if (result != RsTokenService::COMPLETE) + { + std::cerr << "p3Wire::createOriginalPulse() Failed to create Pulse"; + std::cerr << std::endl; + return false; + } + + return true; +} + +bool p3Wire::createReplyPulse(RsGxsGroupId grpId, RsGxsMessageId msgId, RsGxsGroupId replyWith, uint32_t reply_type, RsWirePulseSPtr pPulse) +{ + // check reply_type. can only be ONE. + if (!((reply_type == WIRE_PULSE_TYPE_REPLY) || + (reply_type == WIRE_PULSE_TYPE_REPUBLISH) || + (reply_type == WIRE_PULSE_TYPE_LIKE))) + { + std::cerr << "p3Wire::createReplyPulse() reply_type is invalid"; + std::cerr << std::endl; + return false; + } + + // request both groups. + std::list groupIds = { grpId, replyWith }; + std::vector groups; + bool groupOkay = getGroups(groupIds, groups); + if (!groupOkay) { + std::cerr << "p3Wire::createReplyPulse() getGroups failed"; + std::cerr << std::endl; + return false; + } + + // extract group info. + RsWireGroup replyToGroup; + RsWireGroup replyWithGroup; + + if (grpId == replyWith) + { + if (groups.size() != 1) { + std::cerr << "p3Wire::createReplyPulse() getGroups != 1"; + std::cerr << std::endl; + return false; + } + + replyToGroup = groups[0]; + replyWithGroup = groups[0]; + } + else + { + if (groups.size() != 2) { + std::cerr << "p3Wire::createReplyPulse() getGroups != 2"; + std::cerr << std::endl; + return false; + } + + if (groups[0].mMeta.mGroupId == grpId) { + replyToGroup = groups[0]; + replyWithGroup = groups[1]; + } else { + replyToGroup = groups[1]; + replyWithGroup = groups[0]; + } + } + + // check groupIds match + if ((replyToGroup.mMeta.mGroupId != grpId) || + (replyWithGroup.mMeta.mGroupId != replyWith)) + { + std::cerr << "p3Wire::createReplyPulse() groupid mismatch"; + std::cerr << std::endl; + return false; + } + + // ensure Group is suitable. + if ((!(replyToGroup.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_SUBSCRIBED)) || + (!(replyWithGroup.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_PUBLISH))) + { + std::cerr << "p3Wire::createReplyPulse() Group unsuitable"; + std::cerr << std::endl; + return false; + } + + // ********************************************************** + RsWirePulseSPtr replyToPulse; + if (!fetchPulse(grpId, msgId, replyToPulse)) + { + std::cerr << "p3Wire::createReplyPulse() fetchPulse FAILED"; + std::cerr << std::endl; + return false; + } + + // create Reply Msg. + RsWirePulse responsePulse; + + responsePulse.mMeta.mGroupId = replyWithGroup.mMeta.mGroupId; + responsePulse.mMeta.mAuthorId = replyWithGroup.mMeta.mAuthorId; + responsePulse.mMeta.mThreadId.clear(); + responsePulse.mMeta.mParentId.clear(); + responsePulse.mMeta.mOrigMsgId.clear(); + + responsePulse.mPulseType = WIRE_PULSE_TYPE_RESPONSE | reply_type; + responsePulse.mSentiment = pPulse->mSentiment; + responsePulse.mPulseText = pPulse->mPulseText; + responsePulse.mImage1 = pPulse->mImage1; + responsePulse.mImage2 = pPulse->mImage2; + responsePulse.mImage3 = pPulse->mImage3; + responsePulse.mImage4 = pPulse->mImage4; + + // mRefs refer to parent post. + responsePulse.mRefGroupId = replyToPulse->mMeta.mGroupId; + responsePulse.mRefGroupName = replyToGroup.mMeta.mGroupName; + responsePulse.mRefOrigMsgId = replyToPulse->mMeta.mOrigMsgId; + responsePulse.mRefAuthorId = replyToPulse->mMeta.mAuthorId; + responsePulse.mRefPublishTs = replyToPulse->mMeta.mPublishTs; + responsePulse.mRefPulseText = replyToPulse->mPulseText; + responsePulse.mRefImageCount = replyToPulse->ImageCount(); + + std::cerr << "p3Wire::createReplyPulse() create Response Pulse"; + std::cerr << std::endl; + + uint32_t token; + if (!createPulse(token, responsePulse)) + { + std::cerr << "p3Wire::createReplyPulse() FAILED to create Response Pulse"; + std::cerr << std::endl; + return false; + } + + int result = waitToken(token); + if (result != RsTokenService::COMPLETE) + { + std::cerr << "p3Wire::createReplyPulse() FAILED(2) to create Response Pulse"; + std::cerr << std::endl; + return false; + } + + // get MsgId. + std::pair responsePair; + if (!acknowledgeMsg(token, responsePair)) + { + std::cerr << "p3Wire::createReplyPulse() FAILED acknowledgeMsg for Response Pulse"; + std::cerr << std::endl; + return false; + } + + std::cerr << "p3Wire::createReplyPulse() Response Pulse ID: ("; + std::cerr << responsePair.first.toStdString() << ", "; + std::cerr << responsePair.second.toStdString() << ")"; + std::cerr << std::endl; + + // retrieve newly generated message. + // ********************************************************** + RsWirePulseSPtr createdResponsePulse; + if (!fetchPulse(responsePair.first, responsePair.second, createdResponsePulse)) + { + std::cerr << "p3Wire::createReplyPulse() fetch createdReponsePulse FAILED"; + std::cerr << std::endl; + return false; + } + + /* Check that pulses is created properly */ + if ((createdResponsePulse->mMeta.mGroupId != responsePulse.mMeta.mGroupId) || + (createdResponsePulse->mPulseText != responsePulse.mPulseText) || + (createdResponsePulse->mRefGroupId != responsePulse.mRefGroupId) || + (createdResponsePulse->mRefOrigMsgId != responsePulse.mRefOrigMsgId)) + { + std::cerr << "p3Wire::createReplyPulse() fetch createdReponsePulse FAILED"; + std::cerr << std::endl; + return false; + } + + // create ReplyTo Ref Msg. + std::cerr << "PulseAddDialog::postRefPulse() create Reference!"; + std::cerr << std::endl; + + // Reference Pulse. posted on Parent's Group. + RsWirePulse refPulse; + + refPulse.mMeta.mGroupId = replyToPulse->mMeta.mGroupId; + refPulse.mMeta.mAuthorId = replyWithGroup.mMeta.mAuthorId; // own author Id. + refPulse.mMeta.mThreadId = replyToPulse->mMeta.mOrigMsgId; + refPulse.mMeta.mParentId = replyToPulse->mMeta.mOrigMsgId; + refPulse.mMeta.mOrigMsgId.clear(); + + refPulse.mPulseType = WIRE_PULSE_TYPE_REFERENCE | reply_type; + refPulse.mSentiment = 0; // should this be =? createdResponsePulse->mSentiment; + + // Dont put parent PulseText into refPulse - it is available on Thread Msg. + // otherwise gives impression it is correctly setup Parent / Reply... + // when in fact the parent PublishTS, and AuthorId are wrong. + refPulse.mPulseText = ""; + + // refs refer back to own Post. + refPulse.mRefGroupId = replyWithGroup.mMeta.mGroupId; + refPulse.mRefGroupName = replyWithGroup.mMeta.mGroupName; + refPulse.mRefOrigMsgId = createdResponsePulse->mMeta.mOrigMsgId; + refPulse.mRefAuthorId = replyWithGroup.mMeta.mAuthorId; + refPulse.mRefPublishTs = createdResponsePulse->mMeta.mPublishTs; + refPulse.mRefPulseText = createdResponsePulse->mPulseText; + refPulse.mRefImageCount = createdResponsePulse->ImageCount(); + + // publish Ref Msg. + if (!createPulse(token, refPulse)) + { + std::cerr << "p3Wire::createReplyPulse() FAILED to create Ref Pulse"; + std::cerr << std::endl; + return false; + } + + result = waitToken(token); + if (result != RsTokenService::COMPLETE) + { + std::cerr << "p3Wire::createReplyPulse() FAILED(2) to create Ref Pulse"; + std::cerr << std::endl; + return false; + } + + // get MsgId. + std::pair refPair; + if (!acknowledgeMsg(token, refPair)) + { + std::cerr << "p3Wire::createReplyPulse() FAILED acknowledgeMsg for Ref Pulse"; + std::cerr << std::endl; + return false; + } + + std::cerr << "p3Wire::createReplyPulse() Success: Ref Pulse ID: ("; + std::cerr << refPair.first.toStdString() << ", "; + std::cerr << refPair.second.toStdString() << ")"; + std::cerr << std::endl; + + return true; +} + + + // Blocking, request structures for display. +#if 0 +bool p3Wire::createReplyPulse(uint32_t &token, RsWirePulse &pulse) +{ + + return true; +} + +bool p3Wire::createRepublishPulse(uint32_t &token, RsWirePulse &pulse) +{ + + return true; +} + +bool p3Wire::createLikePulse(uint32_t &token, RsWirePulse &pulse) +{ + + return true; +} +#endif + + // WireGroup Details. +bool p3Wire::getWireGroup(const RsGxsGroupId &groupId, RsWireGroupSPtr &grp) +{ + std::list groupIds = { groupId }; + std::map groups; + if (!fetchGroupPtrs(groupIds, groups)) + { + std::cerr << "p3Wire::getWireGroup() failed to fetchGroupPtrs"; + std::cerr << std::endl; + return false; + } + + if (groups.size() != 1) + { + std::cerr << "p3Wire::getWireGroup() invalid group size"; + std::cerr << std::endl; + return false; + } + + grp = groups.begin()->second; + + // TODO Should fill in Counters of pulses/likes/republishes/replies + return true; +} + +// TODO Remove duplicate ... +bool p3Wire::getWirePulse(const RsGxsGroupId &groupId, const RsGxsMessageId &msgId, RsWirePulseSPtr &pPulse) +{ + return fetchPulse(groupId, msgId, pPulse); +} + + + + +bool compare_time(const RsWirePulseSPtr& first, const RsWirePulseSPtr &second) +{ + return first->mMeta.mPublishTs > second->mMeta.mPublishTs; +} + + // should this filter them in some way? + // date, or count would be more likely. +bool p3Wire::getPulsesForGroups(const std::list &groupIds, std::list &pulsePtrs) +{ + // request all the pulses (Top-Level Thread Msgs). + std::cerr << "p3Wire::getPulsesForGroups()"; + std::cerr << std::endl; + + uint32_t token; + { + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; + opts.mOptions = RS_TOKREQOPT_MSG_LATEST | RS_TOKREQOPT_MSG_THREAD; + + getTokenService()->requestMsgInfo( + token, RS_TOKREQ_ANSTYPE_DATA, opts, groupIds); + } + + // wait for pulse request to completed. + std::cerr << "p3Wire::getPulsesForGroups() waiting for token"; + std::cerr << std::endl; + + int result = waitToken(token); + if (result != RsTokenService::COMPLETE) + { + std::cerr << "p3Wire::getPulsesForGroups() token FAILED, result: " << result; + std::cerr << std::endl; + return false; + } + + // retrieve Pulses. + std::cerr << "p3Wire::getPulsesForGroups() retrieving token"; + std::cerr << std::endl; + if (!getPulsePtrData(token, pulsePtrs)) + { + std::cerr << "p3Wire::getPulsesForGroups() tokenPulse ERROR"; + std::cerr << std::endl; + return false; + } + + std::cerr << "p3Wire::getPulsesForGroups() size = " << pulsePtrs.size(); + std::cerr << std::endl; + { + std::list::iterator it; + for (it = pulsePtrs.begin(); it != pulsePtrs.end(); it++) + { + std::cerr << "p3Wire::getPulsesForGroups() Flags: "; + std::cerr << (*it)->mPulseType << " Msg: " << (*it)->mPulseText; + std::cerr << std::endl; + } + } + + std::cerr << "p3Wire::getPulsesForGroups() size = " << pulsePtrs.size(); + std::cerr << " sorting and trimming"; + std::cerr << std::endl; + + // sort and filter list. + pulsePtrs.sort(compare_time); + + // trim to N max. + uint32_t N = 10; + if (pulsePtrs.size() > N) { + pulsePtrs.resize(N); + } + + // for each fill in details. + std::list::iterator it; + for (it = pulsePtrs.begin(); it != pulsePtrs.end(); it++) + { + if (!updatePulse(*it, 1)) + { + std::cerr << "p3Wire::getPulsesForGroups() Failed to updatePulse"; + std::cerr << std::endl; + return false; + } + } + + // update GroupPtrs for all pulsePtrs. + if (!updateGroups(pulsePtrs)) + { + std::cerr << "p3Wire::getPulsesForGroups() failed to updateGroups"; + std::cerr << std::endl; + return false; + } + + return true; +} + + +bool p3Wire::getPulseFocus(const RsGxsGroupId &groupId, const RsGxsMessageId &msgId, int /* type */, RsWirePulseSPtr &pPulse) +{ + std::cerr << "p3Wire::getPulseFocus("; + std::cerr << "grpId: " << groupId << " msgId: " << msgId; + std::cerr << " )"; + std::cerr << std::endl; + + if (!fetchPulse(groupId, msgId, pPulse)) + { + std::cerr << "p3Wire::getPulseFocus() failed to fetch Pulse"; + std::cerr << std::endl; + return false; + } + + if (!updatePulse(pPulse, 3)) + { + std::cerr << "p3Wire::getPulseFocus() failed to update Pulse"; + std::cerr << std::endl; + return false; + } + + /* Fill in GroupPtrs */ + std::list pulsePtrs; + pulsePtrs.push_back(pPulse); + + if (!updateGroups(pulsePtrs)) + { + std::cerr << "p3Wire::getPulseFocus() failed to updateGroups"; + std::cerr << std::endl; + return false; + } + + return true; +} + + +// function to update a pulse with the (Ref) child with actual data. +bool p3Wire::updatePulse(RsWirePulseSPtr pPulse, int levels) +{ + bool okay = true; + + // setup logging label. + std::ostringstream out; + out << "pulse[" << (void *) pPulse.get() << "], " << levels; + std::string label = out.str(); + + std::cerr << "p3Wire::updatePulse(" << label << ") starting"; + std::cerr << std::endl; + + // is pPulse is a REF, then request the original. + // if no original available the done. + if (pPulse->mPulseType & WIRE_PULSE_TYPE_REFERENCE) + { + RsWirePulseSPtr fullPulse; + std::cerr << "p3Wire::updatePulse(" << label << ") fetching REF ("; + std::cerr << "grpId: " << pPulse->mRefGroupId << " msgId: " << pPulse->mRefOrigMsgId; + std::cerr << " )"; + std::cerr << std::endl; + if (!fetchPulse(pPulse->mRefGroupId, pPulse->mRefOrigMsgId, fullPulse)) + { + std::cerr << "p3Wire::updatePulse(" << label << ") failed to fetch REF"; + std::cerr << std::endl; + return false; + } + std::cerr << "p3Wire::updatePulse(" << label << ") replacing REF"; + std::cerr << std::endl; + + *pPulse = *fullPulse; + } + + // Request children: (Likes / Retweets / Replies) + std::cerr << "p3Wire::updatePulse(" << label << ") requesting children"; + std::cerr << std::endl; + + uint32_t token; + { + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_MSG_RELATED_DATA; + // OR opts.mOptions = RS_TOKREQOPT_MSG_LATEST | RS_TOKREQOPT_MSG_PARENT; + opts.mOptions = RS_TOKREQOPT_MSG_LATEST | RS_TOKREQOPT_MSG_THREAD; + + std::vector msgIds = { + std::make_pair(pPulse->mMeta.mGroupId, pPulse->mMeta.mOrigMsgId) + }; + + getTokenService()->requestMsgRelatedInfo( + token, RS_TOKREQ_ANSTYPE_DATA, opts, msgIds); + } + + // wait for request to complete + std::cerr << "p3Wire::updatePulse(" << label << ") waiting for token"; + std::cerr << std::endl; + + int result = waitToken(token); + if (result != RsTokenService::COMPLETE) + { + std::cerr << "p3Wire::updatePulse(" << label << ") token FAILED, result: " << result; + std::cerr << std::endl; + return false; + } + + /* load children */ + okay = updatePulseChildren(pPulse, token); + if (!okay) + { + std::cerr << "p3Wire::updatePulse(" << label << ") FAILED to update Children"; + std::cerr << std::endl; + return false; + } + + /* if down to last level, no need to updateChildren */ + if (levels <= 1) + { + std::cerr << "p3Wire::updatePulse(" << label << ") Level <= 1 finished"; + std::cerr << std::endl; + return okay; + } + + /* recursively update children */ + std::cerr << "p3Wire::updatePulse(" << label << ") updating children recursively"; + std::cerr << std::endl; + std::list::iterator it; + for (it = pPulse->mReplies.begin(); it != pPulse->mReplies.end(); it++) + { + bool childOkay = updatePulse(*it, levels - 1); + if (!childOkay) { + std::cerr << "p3Wire::updatePulse(" << label << ") update children (reply) failed"; + std::cerr << std::endl; + } + } + + for (it = pPulse->mRepublishes.begin(); it != pPulse->mRepublishes.end(); it++) + { + bool childOkay = updatePulse(*it, levels - 1); + if (!childOkay) { + std::cerr << "p3Wire::updatePulse(" << label << ") update children (repub) failed"; + std::cerr << std::endl; + } + } + + return okay; +} + + +// function to update the (Ref) child with actual data. +bool p3Wire::updatePulseChildren(RsWirePulseSPtr pParent, uint32_t token) +{ + { + bool okay = true; + std::vector pulses; + if (getRelatedPulseData(token, pulses)) { + std::vector::iterator it; + for (it = pulses.begin(); it != pulses.end(); it++) + { + std::cerr << "p3Wire::updatePulseChildren() retrieved child: " << *it; + std::cerr << std::endl; + + RsWirePulseSPtr pPulse = std::make_shared(*it); + // switch on type. + if (it->mPulseType & WIRE_PULSE_TYPE_LIKE) { + pParent->mLikes.push_back(pPulse); + std::cerr << "p3Wire::updatePulseChildren() adding Like"; + std::cerr << std::endl; + } + else if (it->mPulseType & WIRE_PULSE_TYPE_REPUBLISH) { + pParent->mRepublishes.push_back(pPulse); + std::cerr << "p3Wire::updatePulseChildren() adding Republish"; + std::cerr << std::endl; + } + else if (it->mPulseType & WIRE_PULSE_TYPE_REPLY) { + pParent->mReplies.push_back(pPulse); + std::cerr << "p3Wire::updatePulseChildren() adding Reply"; + std::cerr << std::endl; + } + else { + std::cerr << "p3Wire::updatePulseChildren() unknown child type: " << it->mPulseType; + std::cerr << std::endl; + } + } + } else { + std::cerr << "p3Wire::updatePulseChildren() ERROR failed to retrieve token"; + std::cerr << std::endl; + okay = false; + } + + if (!okay) { + std::cerr << "p3Wire::updatePulseChildren() token ERROR"; + std::cerr << std::endl; + } + return okay; + } +} + +/* High-level utility function to update mGroupPtr / mRefGroupPtr links. + * fetches associated groups and reference them from pulses + * + * extractGroupIds (owner + refs). + * fetch all available GroupIDs. (just IDs - so light). + * do intersection of IDs. + * apply IDs. + */ + +bool p3Wire::updateGroups(std::list &pulsePtrs) +{ + std::set pulseGroupIds; + + std::list::iterator it; + for (it = pulsePtrs.begin(); it != pulsePtrs.end(); it++) + { + if (!extractGroupIds(*it, pulseGroupIds)) + { + std::cerr << "p3Wire::updateGroups() failed to extractGroupIds"; + std::cerr << std::endl; + return false; + } + } + + std::list availGroupIds; + if (!trimToAvailGroupIds(pulseGroupIds, availGroupIds)) + { + std::cerr << "p3Wire::updateGroups() failed to trimToAvailGroupIds"; + std::cerr << std::endl; + return false; + } + + std::map groups; + if (!fetchGroupPtrs(availGroupIds, groups)) + { + std::cerr << "p3Wire::updateGroups() failed to fetchGroupPtrs"; + std::cerr << std::endl; + return false; + } + + for (it = pulsePtrs.begin(); it != pulsePtrs.end(); it++) + { + if (!updateGroupPtrs(*it, groups)) + { + std::cerr << "p3Wire::updateGroups() failed to updateGroupPtrs"; + std::cerr << std::endl; + return false; + } + } + return true; +} + + +// this function doesn't depend on p3Wire, could make static. +bool p3Wire::extractGroupIds(RsWirePulseConstSPtr pPulse, std::set &groupIds) +{ + std::cerr << "p3Wire::extractGroupIds()"; + std::cerr << std::endl; + + if (!pPulse) { + std::cerr << "p3Wire::extractGroupIds() INVALID pPulse"; + std::cerr << std::endl; + return false; + } + + // install own groupId. + groupIds.insert(pPulse->mMeta.mGroupId); + + /* do this recursively */ + if (pPulse->mPulseType & WIRE_PULSE_TYPE_REFERENCE) { + // REPLY: mRefGroupId, PARENT was in mMeta.mGroupId. + groupIds.insert(pPulse->mRefGroupId); + /* skipping */ + return true; + } + + + if (pPulse->mPulseType & WIRE_PULSE_TYPE_RESPONSE) { + // REPLY: meta.mGroupId, PARENT: mRefGroupId + groupIds.insert(pPulse->mRefGroupId); + } + + /* iterate through children, recursively */ + std::list::const_iterator it; + for (it = pPulse->mReplies.begin(); it != pPulse->mReplies.end(); it++) + { + bool childOkay = extractGroupIds(*it, groupIds); + if (!childOkay) { + std::cerr << "p3Wire::extractGroupIds() update children (reply) failed"; + std::cerr << std::endl; + return false; + } + } + + for (it = pPulse->mRepublishes.begin(); it != pPulse->mRepublishes.end(); it++) + { + bool childOkay = extractGroupIds(*it, groupIds); + if (!childOkay) { + std::cerr << "p3Wire::extractGroupIds() update children (repub) failed"; + std::cerr << std::endl; + return false; + } + } + + // not bothering with LIKEs at the moment. TODO. + return true; +} + +bool p3Wire::updateGroupPtrs(RsWirePulseSPtr pPulse, const std::map &groups) +{ + std::map::const_iterator git; + git = groups.find(pPulse->mMeta.mGroupId); + if (git == groups.end()) { + // error + return false; + } + + pPulse->mGroupPtr = git->second; + + // if REF, fill in mRefGroupPtr based on mRefGroupId. + if (pPulse->mPulseType & WIRE_PULSE_TYPE_REFERENCE) { + // if RefGroupId is in list, fill in. No error if its not there. + std::map::const_iterator rgit; + rgit = groups.find(pPulse->mRefGroupId); + if (rgit != groups.end()) { + pPulse->mRefGroupPtr = rgit->second; + } + + // no children for REF pulse, so can return now. + return true; + } + + // if Response, fill in mRefGroupPtr based on mRefGroupId. + if (pPulse->mPulseType & WIRE_PULSE_TYPE_RESPONSE) { + // if RefGroupId is in list, fill in. No error if its not there. + std::map::const_iterator rgit; + rgit = groups.find(pPulse->mRefGroupId); + if (rgit != groups.end()) { + pPulse->mRefGroupPtr = rgit->second; + } + // do children as well. + } + + /* recursively apply to children */ + std::list::iterator it; + for (it = pPulse->mReplies.begin(); it != pPulse->mReplies.end(); it++) + { + bool childOkay = updateGroupPtrs(*it, groups); + if (!childOkay) { + std::cerr << "p3Wire::updateGroupPtrs() update children (reply) failed"; + std::cerr << std::endl; + return false; + } + } + + for (it = pPulse->mRepublishes.begin(); it != pPulse->mRepublishes.end(); it++) + { + bool childOkay = updateGroupPtrs(*it, groups); + if (!childOkay) { + std::cerr << "p3Wire::updateGroupPtrs() update children (repub) failed"; + std::cerr << std::endl; + return false; + } + } + + // not bothering with LIKEs at the moment. TODO. + return true; +} + +bool p3Wire::trimToAvailGroupIds(const std::set &pulseGroupIds, + std::list &availGroupIds) +{ + /* request all groupIds */ + std::cerr << "p3Wire::trimToAvailGroupIds()"; + std::cerr << std::endl; + + uint32_t token; + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_GROUP_IDS; + + if (!requestGroupInfo(token, opts) || waitToken(token) != RsTokenService::COMPLETE ) + { + std::cerr << "p3Wire::trimToAvailGroupIds() failed to fetch groups"; + std::cerr << std::endl; + return false; + } + + std::list localGroupIds; + if (!RsGenExchange::getGroupList(token, localGroupIds)) + { + std::cerr << "p3Wire::trimToAvailGroupIds() failed to get GroupIds"; + std::cerr << std::endl; + return false; + } + + /* do intersection between result ^ pulseGroups -> availGroupIds */ + std::set_intersection(localGroupIds.begin(), localGroupIds.end(), + pulseGroupIds.begin(), pulseGroupIds.end(), + std::back_inserter(availGroupIds)); + + return true; +} + +bool p3Wire::fetchGroupPtrs(const std::list &groupIds, + std::map &groups) +{ + std::cerr << "p3Wire::fetchGroupPtrs()"; + std::cerr << std::endl; + + uint32_t token; + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; + + if (!requestGroupInfo(token, opts, groupIds) || waitToken(token) != RsTokenService::COMPLETE ) + { + std::cerr << "p3Wire::fetchGroupPtrs() failed to fetch groups"; + std::cerr << std::endl; + return false; + } + return getGroupPtrData(token, groups); +} + diff --git a/libretroshare/src/services/p3wire.h b/libretroshare/src/services/p3wire.h index 3858d158e..2e7375578 100644 --- a/libretroshare/src/services/p3wire.h +++ b/libretroshare/src/services/p3wire.h @@ -3,7 +3,7 @@ * * * libretroshare: retroshare core library * * * - * Copyright 2012-2012 by Robert Fernie * + * Copyright 2012-2020 by Robert Fernie * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -34,37 +34,86 @@ * */ - class p3Wire: public RsGenExchange, public RsWire { public: - p3Wire(RsGeneralDataService* gds, RsNetworkExchangeService* nes, RsGixs *gixs); -virtual RsServiceInfo getServiceInfo(); -static uint32_t wireAuthenPolicy(); + p3Wire(RsGeneralDataService* gds, RsNetworkExchangeService* nes, RsGixs *gixs); + virtual RsServiceInfo getServiceInfo(); + static uint32_t wireAuthenPolicy(); protected: - -virtual void notifyChanges(std::vector& changes) ; + virtual void notifyChanges(std::vector& changes) ; public: + virtual void service_tick(); -virtual void service_tick(); + virtual RsTokenService* getTokenService(); - /* Specific Service Data */ -virtual bool getGroupData(const uint32_t &token, std::vector &groups); -virtual bool getPulseData(const uint32_t &token, std::vector &pulses); + /* Specific Service Data */ + virtual bool getGroupData(const uint32_t &token, std::vector &groups) override; + virtual bool getPulseData(const uint32_t &token, std::vector &pulses) override; -virtual bool createGroup(uint32_t &token, RsWireGroup &group); -virtual bool createPulse(uint32_t &token, RsWirePulse &pulse); + virtual bool createGroup(uint32_t &token, RsWireGroup &group) override; + virtual bool createPulse(uint32_t &token, RsWirePulse &pulse) override; - private: + // Blocking Interfaces. + virtual bool createGroup(RsWireGroup &group) override; + virtual bool updateGroup(const RsWireGroup &group) override; + virtual bool getGroups(const std::list grpIds, std::vector &groups) override; -virtual void generateDummyData(); -std::string genRandomId(); + // New Interfaces. + // Blocking, request structures for display. + virtual bool createOriginalPulse(const RsGxsGroupId &grpId, RsWirePulseSPtr pPulse) override; + virtual bool createReplyPulse(RsGxsGroupId grpId, RsGxsMessageId msgId, + RsGxsGroupId replyWith, uint32_t reply_type, + RsWirePulseSPtr pPulse) override; + +#if 0 + virtual bool createReplyPulse(uint32_t &token, RsWirePulse &pulse) override; + virtual bool createRepublishPulse(uint32_t &token, RsWirePulse &pulse) override; + virtual bool createLikePulse(uint32_t &token, RsWirePulse &pulse) override; +#endif + + virtual bool getWireGroup(const RsGxsGroupId &groupId, RsWireGroupSPtr &grp) override; + virtual bool getWirePulse(const RsGxsGroupId &groupId, const RsGxsMessageId &msgId, RsWirePulseSPtr &pPulse) override; + + virtual bool getPulsesForGroups(const std::list &groupIds, std::list &pulsePtrs) override; + + virtual bool getPulseFocus(const RsGxsGroupId &groupId, const RsGxsMessageId &msgId, int type, RsWirePulseSPtr &pPulse) override; + +private: + // Internal Service Data. + // They should eventually all be here. + bool getRelatedPulseData(const uint32_t &token, std::vector &pulses); + bool getGroupPtrData(const uint32_t &token, + std::map &groups); + bool getPulsePtrData(const uint32_t &token, std::list &pulses); + + // util functions fetching data. + bool fetchPulse(RsGxsGroupId grpId, RsGxsMessageId msgId, RsWirePulseSPtr &pPulse); + bool updatePulse(RsWirePulseSPtr pPulse, int levels); + bool updatePulseChildren(RsWirePulseSPtr pParent, uint32_t token); + + // update GroupPtrs + bool updateGroups(std::list &pulsePtrs); + + // sub utility functions used by updateGroups. + bool extractGroupIds(RsWirePulseConstSPtr pPulse, std::set &groupIds); + + bool updateGroupPtrs(RsWirePulseSPtr pPulse, + const std::map &groups); + + bool trimToAvailGroupIds(const std::set &pulseGroupIds, + std::list &availGroupIds); + + bool fetchGroupPtrs(const std::list &groupIds, + std::map &groups); + + + virtual void generateDummyData(); + std::string genRandomId(); RsMutex mWireMtx; - - }; #endif diff --git a/libretroshare/src/services/rseventsservice.cc b/libretroshare/src/services/rseventsservice.cc index c1aa0958c..3bbb852be 100644 --- a/libretroshare/src/services/rseventsservice.cc +++ b/libretroshare/src/services/rseventsservice.cc @@ -3,7 +3,9 @@ * * * libretroshare: retroshare core library * * * - * Copyright (C) 2019 Gioacchino Mazzurco * + * Copyright (C) 2019-2020 Gioacchino Mazzurco * + * Copyright (C) 2019-2020 Retroshare Team * + * Copyright (C) 2020 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -27,64 +29,62 @@ /*extern*/ RsEvents* rsEvents = nullptr; -RsEvent::~RsEvent() {}; -RsEvents::~RsEvents() {}; -bool isEventValid( - std::shared_ptr event, std::string& errorMessage ) +RsEvent::~RsEvent() = default; +RsEvents::~RsEvents() = default; + +/*static*/ const RsEventsErrorCategory RsEventsErrorCategory::instance; + +std::error_condition RsEventsErrorCategory::default_error_condition(int ev) +const noexcept { - if(!event) + switch(static_cast(ev)) { - errorMessage = "Event is null!"; - return false; + case RsEventsErrorNum::INVALID_HANDLER_ID: // [[fallthrough]]; + case RsEventsErrorNum::NULL_EVENT_POINTER: // [[fallthrough]]; + case RsEventsErrorNum::EVENT_TYPE_UNDEFINED: // [[fallthrough]]; + case RsEventsErrorNum::EVENT_TYPE_OUT_OF_RANGE: + return std::errc::invalid_argument; + default: + return std::error_condition(ev, *this); } - - if(event->mType <= RsEventType::NONE) - { - errorMessage = "Event has type NONE: " + - std::to_string( - static_cast::type >( - event->mType ) ); - return false; - } - - if(event->mType >= RsEventType::MAX) - { - errorMessage = "Event has type >= RsEventType::MAX: " + - std::to_string( - static_cast::type >( - event->mType ) ); - } - - return true; } -bool RsEventsService::postEvent( std::shared_ptr event, - std::string& errorMessage ) +std::error_condition RsEventsService::isEventTypeInvalid(RsEventType eventType) { - if(!isEventValid(event, errorMessage)) - { - std::cerr << __PRETTY_FUNCTION__ << " Error: "<< errorMessage - << std::endl; - return false; - } + if(eventType == RsEventType::__NONE) + return RsEventsErrorNum::EVENT_TYPE_UNDEFINED; + + if( eventType < RsEventType::__NONE || + eventType >= static_cast(mHandlerMaps.size()) ) + return RsEventsErrorNum::EVENT_TYPE_OUT_OF_RANGE; + + return std::error_condition(); +} + +std::error_condition RsEventsService::isEventInvalid( + std::shared_ptr event) +{ + if(!event) return RsEventsErrorNum::NULL_EVENT_POINTER; + return isEventTypeInvalid(event->mType); +} + +std::error_condition RsEventsService::postEvent( + std::shared_ptr event ) +{ + if(std::error_condition ec = isEventInvalid(event)) return ec; RS_STACK_MUTEX(mEventQueueMtx); mEventQueue.push_back(event); - return true; + return std::error_condition(); } -bool RsEventsService::sendEvent( std::shared_ptr event, - std::string& errorMessage ) +std::error_condition RsEventsService::sendEvent( + std::shared_ptr event ) { - if(!isEventValid(event, errorMessage)) - { - RsErr() << __PRETTY_FUNCTION__ << " "<< errorMessage << std::endl; - return false; - } - + if(std::error_condition ec = isEventInvalid(event)) return ec; handleEvent(event); - return true; + return std::error_condition(); } RsEventsHandlerId_t RsEventsService::generateUniqueHandlerId() @@ -99,43 +99,42 @@ RsEventsHandlerId_t RsEventsService::generateUniqueHandlerId_unlocked() return 1; } -bool RsEventsService::registerEventsHandler( - RsEventType eventType, +std::error_condition RsEventsService::registerEventsHandler( std::function)> multiCallback, - RsEventsHandlerId_t& hId ) + RsEventsHandlerId_t& hId, RsEventType eventType ) { RS_STACK_MUTEX(mHandlerMapMtx); - if( (int)eventType > mHandlerMaps.size() + 10) - { - RsErr() << "Cannot register an event handler for an event type larger than 10 plus the max pre-defined event (value passed was " << (int)eventType << " whereas max is " << (int)RsEventType::MAX << ")" << std::endl; - return false; - } + if(eventType != RsEventType::__NONE) + if(std::error_condition ec = isEventTypeInvalid(eventType)) + return ec; - if( (int)eventType >= mHandlerMaps.size()) - mHandlerMaps.resize( (int)eventType +1 ); + if(!hId) hId = generateUniqueHandlerId_unlocked(); + else if (hId > mLastHandlerId) + { + print_stacktrace(); + return RsEventsErrorNum::INVALID_HANDLER_ID; + } - if(!hId) - hId = generateUniqueHandlerId_unlocked(); - - mHandlerMaps[(int)eventType][hId] = multiCallback; - return true; + mHandlerMaps[static_cast(eventType)][hId] = multiCallback; + return std::error_condition(); } -bool RsEventsService::unregisterEventsHandler(RsEventsHandlerId_t hId) +std::error_condition RsEventsService::unregisterEventsHandler( + RsEventsHandlerId_t hId ) { RS_STACK_MUTEX(mHandlerMapMtx); - for(uint32_t i=0;i event) { - std::function)> mCallback; - - uint32_t event_type_index = static_cast(event->mType); - + if(std::error_condition ec = isEventInvalid(event)) { - RS_STACK_MUTEX(mHandlerMapMtx); /* LOCKED AREA */ - - if(event_type_index >= mHandlerMaps.size() || event_type_index < 1) - { - RsErr() << "Cannot handle an event of type " << event_type_index << ": out of scope!" << std::endl; - return; - } - - // Call all clients that registered a callback for this event type - - for(auto cbit: mHandlerMaps[event_type_index]) - cbit.second(event); - - // Also call all clients that registered with NONE, meaning that they expect all events - - for(auto cbit: mHandlerMaps[static_cast(RsEventType::NONE)]) - cbit.second(event); + RsErr() << __PRETTY_FUNCTION__ << " " << ec << std::endl; + print_stacktrace(); + return; } + + RS_STACK_MUTEX(mHandlerMapMtx); + /* It is important to also call the callback under mutex protection to + * ensure they are not unregistered in the meanwhile. + * If a callback try to fiddle with registering/unregistering it will + * deadlock */ + + // Call all clients that registered a callback for this event type + for(auto cbit: mHandlerMaps[static_cast(event->mType)]) + cbit.second(event); + + /* Also call all clients that registered with NONE, meaning that they + * expect all events */ + for(auto cbit: mHandlerMaps[static_cast(RsEventType::__NONE)]) + cbit.second(event); } diff --git a/libretroshare/src/services/rseventsservice.h b/libretroshare/src/services/rseventsservice.h index 805a173a0..4f6ce98eb 100644 --- a/libretroshare/src/services/rseventsservice.h +++ b/libretroshare/src/services/rseventsservice.h @@ -3,7 +3,8 @@ * * * libretroshare: retroshare core library * * * - * Copyright (C) 2019 Gioacchino Mazzurco * + * Copyright (C) 2019-2020 Gioacchino Mazzurco * + * Copyright (C) 2020 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -24,6 +25,7 @@ #include #include #include +#include #include "retroshare/rsevents.h" #include "util/rsthreads.h" @@ -35,42 +37,44 @@ class RsEventsService : public: RsEventsService(): mHandlerMapMtx("RsEventsService::mHandlerMapMtx"), mLastHandlerId(1), - mHandlerMaps(static_cast(RsEventType::MAX)), mEventQueueMtx("RsEventsService::mEventQueueMtx") {} /// @see RsEvents - bool postEvent( - std::shared_ptr event, - std::string& errorMessage = RS_DEFAULT_STORAGE_PARAM(std::string) - ) override; + std::error_condition postEvent( + std::shared_ptr event ) override; /// @see RsEvents - bool sendEvent( - std::shared_ptr event, - std::string& errorMessage = RS_DEFAULT_STORAGE_PARAM(std::string) - ) override; + std::error_condition sendEvent( + std::shared_ptr event ) override; /// @see RsEvents RsEventsHandlerId_t generateUniqueHandlerId() override; /// @see RsEvents - bool registerEventsHandler( - RsEventType eventType, + std::error_condition registerEventsHandler( std::function)> multiCallback, - RsEventsHandlerId_t& hId = RS_DEFAULT_STORAGE_PARAM(RsEventsHandlerId_t, 0) - ) override; + RsEventsHandlerId_t& hId = RS_DEFAULT_STORAGE_PARAM(RsEventsHandlerId_t, 0), + RsEventType eventType = RsEventType::__NONE ) override; /// @see RsEvents - bool unregisterEventsHandler(RsEventsHandlerId_t hId) override; + std::error_condition unregisterEventsHandler( + RsEventsHandlerId_t hId ) override; protected: + std::error_condition isEventTypeInvalid(RsEventType eventType); + std::error_condition isEventInvalid(std::shared_ptr event); + RsMutex mHandlerMapMtx; RsEventsHandlerId_t mLastHandlerId; - std::vector< - std::map< - RsEventsHandlerId_t, - std::function)> > > mHandlerMaps; + /** Storage for event handlers, keep 10 extra types for plugins that might + * be released indipendently */ + std::array< + std::map< + RsEventsHandlerId_t, + std::function)> >, + static_cast(RsEventType::__MAX) + 10 + > mHandlerMaps; RsMutex mEventQueueMtx; std::deque< std::shared_ptr > mEventQueue; diff --git a/libretroshare/src/tcponudp/udpstunner.cc b/libretroshare/src/tcponudp/udpstunner.cc index f7a0ec47a..4409f256f 100644 --- a/libretroshare/src/tcponudp/udpstunner.cc +++ b/libretroshare/src/tcponudp/udpstunner.cc @@ -164,12 +164,12 @@ int UdpStunner::grabExclusiveMode(std::string holder) /* returns seconds since commsage = 1; } #ifdef DEBUG_UDP_STUNNER_FILTER -#endif std::cerr << "UdpStunner::grabExclusiveMode() SUCCESS. last comms: " << commsage; std::cerr << " ago"; std::cerr << std::endl; std::cerr << "UdpStunner::grabExclusiveMode() Exclusive held by: " << mExclusiveHolder; std::cerr << std::endl; +#endif return commsage; } @@ -181,13 +181,12 @@ int UdpStunner::releaseExclusiveMode(std::string holder, bool forceStun) if (!mExclusiveMode) { #ifdef DEBUG_UDP_STUNNER_FILTER -#endif std::cerr << "UdpStunner::cancelExclusiveMode() ERROR, not in exclusive Mode"; std::cerr << std::endl; +#endif return 0; } - rstime_t now = time(NULL); mExclusiveMode = false; if (forceStun) { @@ -217,10 +216,11 @@ int UdpStunner::releaseExclusiveMode(std::string holder, bool forceStun) } #ifdef DEBUG_UDP_STUNNER_FILTER -#endif + rstime_t now = time(nullptr); std::cerr << "UdpStunner::cancelExclusiveMode() Canceled. Was in ExclusiveMode for: " << now - mExclusiveModeTS; std::cerr << " secs"; std::cerr << std::endl; +#endif return 1; } @@ -326,7 +326,7 @@ bool UdpStunner::locked_handleStunPkt(void *data, int size, struct sockaddr_in & #endif /* generate a response */ int len; - void *pkt = UdpStun_generate_stun_reply(&from, &len); + void *pkt = UdpStun_generate_stun_reply(&from, &len, data); if (!pkt) return false; @@ -485,7 +485,7 @@ bool UdpStun_response(void *stun_pkt, int size, struct sockaddr_in &addr) return false; } - if (htons(((uint16_t *) stun_pkt)[0]) != 0x0101) + if (htons(((uint16_t *) stun_pkt)[0]) != UdpStunner::STUN_BINDING_RESPONSE) { /* not a response */ return false; @@ -517,33 +517,38 @@ bool UdpStun_generate_stun_pkt(void *stun_pkt, int *len) } /* just the header */ - ((uint16_t *) stun_pkt)[0] = (uint16_t) htons(0x0001); + ((uint16_t *) stun_pkt)[0] = (uint16_t) htons(UdpStunner::STUN_BINDING_REQUEST); ((uint16_t *) stun_pkt)[1] = (uint16_t) htons(20); /* only header */ - /* transaction id - should be random */ - ((uint32_t *) stun_pkt)[1] = (uint32_t) htonl(0x0020); - ((uint32_t *) stun_pkt)[2] = (uint32_t) htonl(0x0121); - ((uint32_t *) stun_pkt)[3] = (uint32_t) htonl(0x0111); - ((uint32_t *) stun_pkt)[4] = (uint32_t) htonl(0x1010); + /* RFC 3489 + * The transaction ID is used to correlate requests and responses. + * + * RFC 5389 introduces a mmgic cokie at the location where preciously the transaction ID was located: + * In RFC 3489, this field was part of + * the transaction ID; placing the magic cookie in this location allows + * a server to detect if the client will understand certain attributes + * that were added in this revised specification. + */ + RsRandom::random_bytes(&((uint8_t *) stun_pkt)[4], 4 * sizeof (uint32_t)); *len = 20; return true; } -void *UdpStun_generate_stun_reply(struct sockaddr_in *stun_addr, int *len) +void *UdpStun_generate_stun_reply(struct sockaddr_in *stun_addr, int *len, const void *transId) { /* just the header */ void *stun_pkt = rs_malloc(28); - if(!stun_pkt) - return NULL ; + if(!stun_pkt) + return nullptr ; - ((uint16_t *) stun_pkt)[0] = (uint16_t) htons(0x0101); + ((uint16_t *) stun_pkt)[0] = (uint16_t) htons(UdpStunner::STUN_BINDING_RESPONSE); ((uint16_t *) stun_pkt)[1] = (uint16_t) htons(28); /* only header + 8 byte addr */ - /* transaction id - should be random */ - ((uint32_t *) stun_pkt)[1] = (uint32_t) htonl(0x0f20); - ((uint32_t *) stun_pkt)[2] = (uint32_t) htonl(0x0f21); - ((uint32_t *) stun_pkt)[3] = (uint32_t) htonl(0x0f11); - ((uint32_t *) stun_pkt)[4] = (uint32_t) htonl(0x1010); + /* RFC 3489 + * The Binding Response MUST contain the same transaction ID contained in the Binding Request. + */ + memcpy(&((uint32_t *) stun_pkt)[1], &((uint32_t *) transId)[1], 4 * sizeof (uint32_t)); + /* now add address * 0 1 2 3 * @@ -586,20 +591,20 @@ bool UdpStun_isStunPacket(void *data, int size) return false; } - if ((size == 20) && (0x0001 == ntohs(((uint16_t *) data)[0]))) + if ((size == 20) && (UdpStunner::STUN_BINDING_REQUEST == ntohs(((uint16_t *) data)[0]))) { #ifdef DEBUG_UDP_STUNNER_FILTER - std::cerr << "UdpStunner::isStunPacket() (size=20 & data[0]=0x0001) -> true"; + std::cerr << "UdpStunner::isStunPacket() (size=20 & data[0]=STUN_BINDING_REQUEST) -> true"; std::cerr << std::endl; #endif /* request */ return true; } - if ((size == 28) && (0x0101 == ntohs(((uint16_t *) data)[0]))) + if ((size == 28) && (UdpStunner::STUN_BINDING_RESPONSE == ntohs(((uint16_t *) data)[0]))) { #ifdef DEBUG_UDP_STUNNER_FILTER - std::cerr << "UdpStunner::isStunPacket() (size=28 & data[0]=0x0101) -> true"; + std::cerr << "UdpStunner::isStunPacket() (size=28 & data[0]=STUN_BINDING_RESPONSE) -> true"; std::cerr << std::endl; #endif /* response */ @@ -947,15 +952,15 @@ bool UdpStunner::locked_recvdStun(const struct sockaddr_in &remote, const str if (remote.sin_addr.s_addr == extaddr.sin_addr.s_addr) { #ifdef DEBUG_UDP_STUNNER -#endif std::cerr << "UdpStunner::locked_recvdStun() WARNING, BAD PEER: "; std::cerr << "Stun Peer Returned its own address: " << rs_inet_ntoa(remote.sin_addr); std::cerr << std::endl; +#endif return false; } #endif - bool found = true; + bool found = false; std::list::iterator it; for(it = mStunList.begin(); it != mStunList.end(); ++it) { diff --git a/libretroshare/src/tcponudp/udpstunner.h b/libretroshare/src/tcponudp/udpstunner.h index 8365b0fd9..00accaa45 100644 --- a/libretroshare/src/tcponudp/udpstunner.h +++ b/libretroshare/src/tcponudp/udpstunner.h @@ -30,10 +30,10 @@ #include "util/rsthreads.h" #include -/* UdpStun. +/** + * @brief The TouStunPeer class * Stuns peers to determine external addresses. */ - class TouStunPeer { public: @@ -53,10 +53,17 @@ class TouStunPeer return; } + /// id for identification std::string id; - struct sockaddr_in remote, eaddr; + /// Remote address of the peer. + struct sockaddr_in remote; + /// Our external IP address as reported by the peer. + struct sockaddr_in eaddr; + /// true when a response was received in the past bool response; + /// used to rate limit STUN requests rstime_t lastsend; + /// fail counter for dead/bad peer detection (0 = good) uint32_t failCount; }; @@ -65,6 +72,13 @@ class TouStunPeer * #define UDPSTUN_ALLOW_LOCALNET 1 */ +/** + * @brief The UdpStunner class + * The UDP stunner implements the STUN protocol to determin the NAT type (behind that RS is usually running). + * It maintains a list of DHT peers that are regulary contacted. + * + * The actual NAT type determination logic is located in void pqiNetStateBox::determineNetworkState() + */ class UdpStunner: public UdpSubReceiver { public: @@ -102,6 +116,12 @@ virtual int status(std::ostream &out); /* monitoring / updates */ int tick(); + /* + * based on RFC 3489 + */ + static constexpr uint16_t STUN_BINDING_REQUEST = 0x0001; + static constexpr uint16_t STUN_BINDING_RESPONSE = 0x0101; + private: bool checkStunDesired(); @@ -123,7 +143,7 @@ bool locked_checkExternalAddress(); struct sockaddr_in eaddr; /* external addr */ - bool eaddrKnown; + bool eaddrKnown; bool eaddrStable; /* if true then usable. if false -> Symmettric NAT */ rstime_t eaddrTime; @@ -143,13 +163,19 @@ bool locked_checkExternalAddress(); #endif + /// The UDP stunner will only (actively) contact it's peers when mPassiveStunMode is false. (has priority over mForceRestun bool mPassiveStunMode; - uint32_t mTargetStunPeriod; + /// Time between STUNs + uint32_t mTargetStunPeriod; + /// Rate that determines how often STUN attempts are successfull double mSuccessRate; + /// Some variables used for tracking who and when exclusive mode is enabled bool mExclusiveMode; /* when this is switched on, the stunner stays silent (and extAddr is maintained) */ rstime_t mExclusiveModeTS; std::string mExclusiveHolder; + + /// force a STUN immediately bool mForceRestun; }; @@ -158,7 +184,14 @@ bool locked_checkExternalAddress(); bool UdpStun_isStunPacket(void *data, int size); bool UdpStun_response(void *stun_pkt, int size, struct sockaddr_in &addr); -void *UdpStun_generate_stun_reply(struct sockaddr_in *stun_addr, int *len); +/** + * @brief UdpStun_generate_stun_reply Generates a STUN reply package. + * @param stun_addr The address to set in the response field. + * @param len Lenght of the generated package (always 28). + * @param transId The transaction ID of the request package. + * @return Pointer to the generated reply package. + */ +void *UdpStun_generate_stun_reply(struct sockaddr_in *stun_addr, int *len, const void* transId); bool UdpStun_generate_stun_pkt(void *stun_pkt, int *len); #endif diff --git a/libretroshare/src/turtle/rsturtleitem.h b/libretroshare/src/turtle/rsturtleitem.h index 842a32aa9..c13a22199 100644 --- a/libretroshare/src/turtle/rsturtleitem.h +++ b/libretroshare/src/turtle/rsturtleitem.h @@ -166,7 +166,13 @@ class RsTurtleRegExpSearchRequestItem: public RsTurtleFileSearchRequestItem class RsTurtleGenericSearchRequestItem: public RsTurtleSearchRequestItem { public: - RsTurtleGenericSearchRequestItem() : RsTurtleSearchRequestItem(RS_TURTLE_SUBTYPE_GENERIC_SEARCH_REQUEST) {} + RsTurtleGenericSearchRequestItem() + : RsTurtleSearchRequestItem(RS_TURTLE_SUBTYPE_GENERIC_SEARCH_REQUEST), + service_id(0), + search_data_len(0), + request_type(0), + search_data(nullptr) + {} virtual ~RsTurtleGenericSearchRequestItem() { clear(); } uint16_t service_id ; // service to search @@ -221,7 +227,11 @@ class RsTurtleFTSearchResultItem: public RsTurtleSearchResultItem class RsTurtleGenericSearchResultItem: public RsTurtleSearchResultItem { public: - RsTurtleGenericSearchResultItem() : RsTurtleSearchResultItem(RS_TURTLE_SUBTYPE_GENERIC_SEARCH_RESULT){} + RsTurtleGenericSearchResultItem() + : RsTurtleSearchResultItem(RS_TURTLE_SUBTYPE_GENERIC_SEARCH_RESULT), + result_data(nullptr), + result_data_len(0) + {} virtual ~RsTurtleGenericSearchResultItem() {} uint32_t count() const { return result_data_len/50 ; } // This is a blind size estimate. We should probably use the actual size to limit search results. diff --git a/libretroshare/src/use_libretroshare.pri b/libretroshare/src/use_libretroshare.pri index 681805a7f..5f291b29c 100644 --- a/libretroshare/src/use_libretroshare.pri +++ b/libretroshare/src/use_libretroshare.pri @@ -46,7 +46,7 @@ rs_jsonapi { LIBS *= -L$$clean_path($${RESTBED_BUILD_PATH}/) -lrestbed } else:sLibs *= restbed - win32-g++:dLibs *= wsock32 + win32-g++|win32-clang-g++:dLibs *= wsock32 } linux-* { @@ -55,7 +55,7 @@ linux-* { rs_deep_channels_index | rs_deep_files_index { mLibs += xapian - win32-g++:mLibs += rpcrt4 + win32-g++|win32-clang-g++:mLibs += rpcrt4 } rs_deep_files_index_ogg { @@ -81,7 +81,7 @@ rs_broadcast_discovery { LIBS *= -L$$clean_path($${UDP_DISCOVERY_BUILD_PATH}) -ludp-discovery } else:sLibs *= udp-discovery - win32-g++:dLibs *= wsock32 + win32-g++|win32-clang-g++:dLibs *= wsock32 } static { @@ -102,3 +102,8 @@ android-* { CONFIG *= qt QT *= network } + +################################### Pkg-Config Stuff ############################# + +LIBS *= $$system(pkg-config --libs $$PKGCONFIG) + diff --git a/libretroshare/src/file_sharing/file_tree.h b/libretroshare/src/util/cxx14retrocompat.h similarity index 57% rename from libretroshare/src/file_sharing/file_tree.h rename to libretroshare/src/util/cxx14retrocompat.h index c5328a6df..e7e7d9e99 100644 --- a/libretroshare/src/file_sharing/file_tree.h +++ b/libretroshare/src/util/cxx14retrocompat.h @@ -1,9 +1,9 @@ /******************************************************************************* - * libretroshare/src/file_sharing: file_tree.h * * * - * libretroshare: retroshare core library * + * libretroshare < C++14 retro-compatibility helpers * * * - * Copyright 2018 by Cyril Soler * + * Copyright (C) 2020 Gioacchino Mazzurco * + * Copyright (C) 2020 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -18,37 +18,21 @@ * You should have received a copy of the GNU Lesser General Public License * * along with this program. If not, see . * * * - ******************************************************************************/ -#include "retroshare/rsfiles.h" + *******************************************************************************/ +#pragma once -class FileTreeImpl: public FileTree +#if __cplusplus < 201402L + +#include + +namespace std { -public: - FileTreeImpl() - { - mTotalFiles = 0 ; - mTotalSize = 0 ; - } +template using decay_t = typename decay::type; - virtual std::string toRadix64() const ; - virtual bool getDirectoryContent(uint32_t index,std::string& name,std::vector& subdirs,std::vector& subfiles) const ; - virtual void print() const ; +template +using enable_if_t = typename enable_if::type; - bool serialise(unsigned char *& data,uint32_t& data_size) const ; - bool deserialise(unsigned char* data, uint32_t data_size) ; - -protected: - void recurs_print(uint32_t index,const std::string& indent) const; - - static void recurs_buildFileTree(FileTreeImpl& ft, uint32_t index, const DirDetails& dd, bool remote, bool remove_top_dirs); - - struct DirData { - std::string name; - std::vector subdirs ; - std::vector subfiles ; - }; - std::vector mFiles ; - std::vector mDirs ; - - friend class FileTree ; -}; +template +using remove_const_t = typename remove_const::type; +} +#endif // __cplusplus < 201402L diff --git a/libretroshare/src/util/i2pcommon.cpp b/libretroshare/src/util/i2pcommon.cpp new file mode 100644 index 000000000..ec2ebfd6b --- /dev/null +++ b/libretroshare/src/util/i2pcommon.cpp @@ -0,0 +1,163 @@ +#include "i2pcommon.h" + +#include "util/rsbase64.h" +#include "util/rsdebug.h" + +namespace i2p { + +std::string keyToBase32Addr(const std::string &key) +{ + std::string copy(key); + + // replace I2P specific chars + std::replace(copy.begin(), copy.end(), '~', '/'); + // replacing the - with a + is not necessary, as RsBase64 can handle base64url encoding, too + // std::replace(copy.begin(), copy.end(), '-', '+'); + + // decode + std::vector bin; + RsBase64::decode(copy, bin); + + // hash + std::vector sha256 = RsUtil::BinToSha256(bin); + // encode + std::string out = Radix32::encode(sha256); + + // i2p uses lowercase + std::transform(out.begin(), out.end(), out.begin(), ::tolower); + out.append(".b32.i2p"); + + return out; +} + +const std::string makeOption(const std::string &lhs, const int8_t &rhs) { + return lhs + "=" + std::to_string(rhs); +} + +uint16_t readTwoBytesBE(std::vector::const_iterator &p) +{ + uint16_t val = 0; + val += *p++; + val <<= 8; + val += *p++; + return val; +} + +std::string publicKeyFromPrivate(std::string const &priv) +{ + /* + * https://geti2p.net/spec/common-structures#destination + * https://geti2p.net/spec/common-structures#keysandcert + * https://geti2p.net/spec/common-structures#certificate + */ + if (priv.length() < 884) // base64 ( = 663 bytes = KeyCert + priv Keys) + return std::string(); + + // creat a copy to work on, need to convert it to standard base64 + auto priv_copy(priv); + std::replace(priv_copy.begin(), priv_copy.end(), '~', '/'); + // replacing the - with a + is not necessary, as RsBase64 can handle base64url encoding, too + // std::replace(copy.begin(), copy.end(), '-', '+'); + + // get raw data + std::vector dataPriv; + RsBase64::decode(priv_copy, dataPriv); + + auto p = dataPriv.cbegin(); + RS_DBG("dataPriv.size ", dataPriv.size()); + + size_t publicKeyLen = 256 + 128; // default length (bytes) + uint8_t certType = 0; + uint16_t len = 0; + uint16_t signingKeyType = 0; + uint16_t cryptKey = 0; + + // only used for easy break + do { + try { + // jump to certificate + p += publicKeyLen; + // try to read type and length + certType = *p++; + len = readTwoBytesBE(p); + + // only 0 and 5 are used / valid at this point + // check for == 0 + if (certType == static_cast::type>(CertType::Null)) { + /* + * CertType.Null + * type null is followed by 0x00 0x00 + * so has to be 0! + */ + RS_DBG("cert is CertType.Null"); + publicKeyLen += 3; // add 0x00 0x00 0x00 + + if (len != 0) + // weird + RS_DBG("cert is CertType.Null but len != 0"); + + break; + } + + // check for != 5 + if (certType != static_cast::type>(CertType::Key)) { + // unsupported + RS_DBG("cert type ", certType, " is unsupported"); + return std::string(); + } + + RS_DBG("cert is CertType.Key"); + publicKeyLen += 7; // = 1 + 2 + 2 + 2 = 7 bytes + + /* + * "Key certificates were introduced in release 0.9.12. Prior to that release, all PublicKeys were 256-byte ElGamal keys, and all SigningPublicKeys were 128-byte DSA-SHA1 keys." + * --> there is space for 256+128 bytes, longer keys are splitted and appended to the certificate + * We don't need to bother with the splitting here as only the lenght is important! + */ + + // Signing Public Key + // likely 7 + signingKeyType = readTwoBytesBE(p); + + RS_DBG("signing pubkey type ", certType); + if (signingKeyType >= 3 && signingKeyType <= 6) { + RS_DBG("signing pubkey type ", certType, " has oversize"); + // calculate oversize + + if (signingKeyType >= signingKeyLengths.size()) { + // just in case + RS_DBG("signing pubkey type ", certType, " cannot be found in size data!"); + return std::string(); + } + + auto values = signingKeyLengths[signingKeyType]; + if (values.first <= 128) { + // just in case, it's supposed to be larger! + RS_DBG("signing pubkey type ", certType, " is oversize but size calculation would underflow!"); + return std::string(); + } + + publicKeyLen += values.first - 128; // 128 = default DSA key length = the space than can be used before the key must be splitted + } + + // Crypto Public Key + // likely 0 + cryptKey = readTwoBytesBE(p); + RS_DBG("crypto pubkey type ", cryptKey); + // info: these are all smaller than the default 256 bytes, so no oversize calculation is needed + + break; + } catch (const std::out_of_range &e) { + RS_DBG("hit exception! ", e.what()); + return std::string(); + } + } while(false); + + std::string pub; + auto data2 = std::vector(dataPriv.cbegin(), dataPriv.cbegin() + publicKeyLen); + RsBase64::encode(data2.data(), data2.size(), pub, false, false); + + return pub; +} + +} // namespace i2p diff --git a/libretroshare/src/util/i2pcommon.h b/libretroshare/src/util/i2pcommon.h new file mode 100644 index 000000000..1fd152079 --- /dev/null +++ b/libretroshare/src/util/i2pcommon.h @@ -0,0 +1,213 @@ +#ifndef I2PCOMMON_H +#define I2PCOMMON_H + +#include +#include +#include +#include + +#include "util/rsrandom.h" +#include "util/radix32.h" +#include "util/rsbase64.h" +#include "util/rsprint.h" +#include "util/rsdebug.h" + +/* + * This header provides common code for i2p related code, namely BOB and SAM3 support. + */ + +namespace i2p { + +static constexpr int8_t kDefaultLength = 3; // i2p default +static constexpr int8_t kDefaultQuantity = 3; // i2p default + 1 +static constexpr int8_t kDefaultVariance = 0; +static constexpr int8_t kDefaultBackupQuantity = 0; + +/** + * @brief The address struct + * This structure is a container for any i2p address/key. The public key is used for addressing and can be (optionally) hashed to generate the .b32.i2p address. + */ +struct address { + std::string base32; + std::string publicKey; + std::string privateKey; + + void clear() { + base32.clear(); + publicKey.clear(); + privateKey.clear(); + } +}; + +/** + * @brief The settings struct + * Common structure with all settings that are shared between any i2p backends + */ +struct settings { + bool enable; + struct address address; + + // connection parameter + int8_t inLength; + int8_t inQuantity; + int8_t inVariance; + int8_t inBackupQuantity; + + int8_t outLength; + int8_t outQuantity; + int8_t outVariance; + int8_t outBackupQuantity; + + void initDefault() { + enable = false; + address.clear(); + + inLength = kDefaultLength; + inQuantity = kDefaultQuantity; + inVariance = kDefaultVariance; + inBackupQuantity = kDefaultBackupQuantity; + + outLength = kDefaultLength; + outQuantity = kDefaultQuantity; + outVariance = kDefaultVariance; + outBackupQuantity = kDefaultBackupQuantity; + } +}; + +/* + Type Type Code Payload Length Total Length Notes + Null 0 0 3 + HashCash 1 varies varies Experimental, unused. Payload contains an ASCII colon-separated hashcash string. + Hidden 2 0 3 Experimental, unused. Hidden routers generally do not announce that they are hidden. + Signed 3 40 or 72 43 or 75 Experimental, unused. Payload contains a 40-byte DSA signature, optionally followed by the 32-byte Hash of the signing Destination. + Multiple 4 varies varies Experimental, unused. Payload contains multiple certificates. + Key 5 4+ 7+ Since 0.9.12. See below for details. +*/ +enum class CertType : uint8_t { + Null = 0, + HashCash = 1, + Hidden = 2, + Signed = 3, + Multiple = 4, + Key = 5 +}; + +/* + * public + Type Type Code Total Public Key Length Since Usage + DSA_SHA1 0 128 0.9.12 Legacy Router Identities and Destinations, never explicitly set + ECDSA_SHA256_P256 1 64 0.9.12 Older Destinations + ECDSA_SHA384_P384 2 96 0.9.12 Rarely if ever used for Destinations + ECDSA_SHA512_P521 3 132 0.9.12 Rarely if ever used for Destinations + RSA_SHA256_2048 4 256 0.9.12 Offline only; never used in Key Certificates for Router Identities or Destinations + RSA_SHA384_3072 5 384 0.9.12 Offline only; never used in Key Certificates for Router Identities or Destinations + RSA_SHA512_4096 6 512 0.9.12 Offline only; never used in Key Certificates for Router Identities or Destinations + EdDSA_SHA512_Ed25519 7 32 0.9.15 Recent Router Identities and Destinations + EdDSA_SHA512_Ed25519ph 8 32 0.9.25 Offline only; never used in Key Certificates for Router Identities or Destinations + reserved (GOST) 9 64 Reserved, see proposal 134 + reserved (GOST) 10 128 Reserved, see proposal 134 + RedDSA_SHA512_Ed25519 11 32 0.9.39 For Destinations and encrypted leasesets only; never used for Router Identities + reserved 65280-65534 Reserved for experimental use + reserved 65535 Reserved for future expansion + + * private + Type Length (bytes) Since Usage + DSA_SHA1 20 Legacy Router Identities and Destinations + ECDSA_SHA256_P256 32 0.9.12 Recent Destinations + ECDSA_SHA384_P384 48 0.9.12 Rarely used for Destinations + ECDSA_SHA512_P521 66 0.9.12 Rarely used for Destinations + RSA_SHA256_2048 512 0.9.12 Offline signing, never used for Router Identities or Destinations + RSA_SHA384_3072 768 0.9.12 Offline signing, never used for Router Identities or Destinations + RSA_SHA512_4096 1024 0.9.12 Offline signing, never used for Router Identities or Destinations + EdDSA_SHA512_Ed25519 32 0.9.15 Recent Router Identities and Destinations + EdDSA_SHA512_Ed25519ph 32 0.9.25 Offline signing, never used for Router Identities or Destinations + RedDSA_SHA512_Ed25519 32 0.9.39 For Destinations and encrypted leasesets only, never used for Router Identities + */ +enum class SigningKeyType : uint16_t { + DSA_SHA1 = 0, + ECDSA_SHA256_P256 = 1, + ECDSA_SHA384_P384 = 2, + ECDSA_SHA512_P521 = 3, + RSA_SHA256_2048 = 4, + RSA_SHA384_3072 = 5, + RSA_SHA512_4096 = 6, + EdDSA_SHA512_Ed25519 = 7, + EdDSA_SHA512_Ed25519ph = 8, + RedDSA_SHA512_Ed25519 = 11 +}; + +/* + * public + Type Type Code Total Public Key Length Usage + ElGamal 0 256 All Router Identities and Destinations + P256 1 64 Reserved, see proposal 145 + P384 2 96 Reserved, see proposal 145 + P521 3 132 Reserved, see proposal 145 + X25519 4 32 Not for use in key certs. See proposal 144 + reserved 65280-65534 Reserved for experimental use + reserved 65535 Reserved for future expansion + + * private + Type Length (bytes) Since Usage + ElGamal 256 All Router Identities and Destinations + P256 32 TBD Reserved, see proposal 145 + P384 48 TBD Reserved, see proposal 145 + P521 66 TBD Reserved, see proposal 145 + X25519 32 0.9.38 Little-endian. See proposal 144 +*/ +enum class CryptoKeyType : uint16_t { + ElGamal = 0, + P256 = 1, + P384 = 2, + P521 = 3, + X25519 = 4 +}; + +static const std::array, 5> cryptoKeyLengths { + /*CryptoKeyType::ElGamal*/ std::make_pair(256, 256), + /*CryptoKeyType::P256, */ std::make_pair( 64, 32), + /*CryptoKeyType::P384, */ std::make_pair( 96, 48), + /*CryptoKeyType::P521, */ std::make_pair(132, 66), + /*CryptoKeyType::X25519,*/ std::make_pair( 32, 32), +}; + +static const std::array, 12> signingKeyLengths { + /*SigningKeyType::DSA_SHA1, */ std::make_pair(128, 128), + /*SigningKeyType::ECDSA_SHA256_P256, */ std::make_pair( 64, 32), + /*SigningKeyType::ECDSA_SHA384_P384, */ std::make_pair( 96, 48), + /*SigningKeyType::ECDSA_SHA512_P521, */ std::make_pair(132, 66), + /*SigningKeyType::RSA_SHA256_2048, */ std::make_pair(256, 512), + /*SigningKeyType::RSA_SHA384_3072, */ std::make_pair(384, 768), + /*SigningKeyType::RSA_SHA512_4096, */ std::make_pair(512,1024), + /*SigningKeyType::EdDSA_SHA512_Ed25519 */ std::make_pair( 32, 32), + /*SigningKeyType::EdDSA_SHA512_Ed25519ph */ std::make_pair( 32, 32), + /*reserved (GOST) */ std::make_pair( 64, 0), + /*reserved (GOST) */ std::make_pair(128, 0), + /*SigningKeyType::RedDSA_SHA512_Ed25519 */ std::make_pair( 32, 32), +}; + +/** + * @brief makeOption Creates the string "lhs=rhs" used by BOB and SAM. Converts rhs + * @param lhs option to set + * @param rhs value to set + * @return concatenated string + */ +const std::string makeOption(const std::string &lhs, const int8_t &rhs); + +/** + * @brief keyToBase32Addr generated a base32 address (.b32.i2p) from a given public key + * @param key public key + * @return generated base32 address + */ +std::string keyToBase32Addr(const std::string &key); + +/** + * @brief publicKeyFromPrivate parses the private key and calculates the lenght of the public key + * @param priv private key (which includes the public key) to read + * @return public key used for addressing + */ +std::string publicKeyFromPrivate(const std::string &priv); + +} // namespace i2p + +#endif // I2PCOMMON_H diff --git a/libretroshare/src/util/radix64.h b/libretroshare/src/util/radix64.h index de6eb21f4..53dd7c256 100644 --- a/libretroshare/src/util/radix64.h +++ b/libretroshare/src/util/radix64.h @@ -26,7 +26,10 @@ #include #include -class Radix64 +#include "util/rsdeprecate.h" + +/** @deprecated use RsBase64 instead which supports also URL safe encoding */ +class RS_DEPRECATED_FOR(RsBase64) Radix64 { public: static std::vector decode(const std::string& buffer) @@ -195,5 +198,3 @@ again: return true ; } }; - - diff --git a/libretroshare/src/util/retrodb.cc b/libretroshare/src/util/retrodb.cc index a539c1676..122f3e6a1 100644 --- a/libretroshare/src/util/retrodb.cc +++ b/libretroshare/src/util/retrodb.cc @@ -185,7 +185,7 @@ bool RetroDb::execSQL(const std::string &query){ uint32_t delta = 3; - rstime_t stamp = time(NULL), now = 0; + rstime_t stamp = time(NULL); bool timeOut = false, ok = false; while(!timeOut){ @@ -202,10 +202,8 @@ bool RetroDb::execSQL(const std::string &query){ break; } - now = time(NULL); - delta = stamp - now; - - if(delta > TIME_LIMIT){ + if(time(NULL) > stamp + TIME_LIMIT) + { ok = false; timeOut = true; } diff --git a/libretroshare/src/util/rsbase64.cc b/libretroshare/src/util/rsbase64.cc new file mode 100644 index 000000000..f62c40cef --- /dev/null +++ b/libretroshare/src/util/rsbase64.cc @@ -0,0 +1,198 @@ +/******************************************************************************* + * * + * libretroshare base64 encoding utilities * + * * + * Copyright (C) 2015 Retroshare Team * + * Copyright (C) 2020 Gioacchino Mazzurco * + * Copyright (C) 2020 Asociación Civil Altermundi * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include "util/rsbase64.h" +#include "util/rsdebug.h" + +#if __cplusplus < 201703L +/* Solve weird undefined reference error with C++ < 17 see: + * https://stackoverflow.com/questions/8016780/undefined-reference-to-static-constexpr-char + */ +/*static*/ decltype(RsBase64::bDict) constexpr RsBase64::bDict; +/*static*/ decltype(RsBase64::uDict) constexpr RsBase64::uDict; +/*static*/ decltype(RsBase64::rDict) constexpr RsBase64::rDict; +/*static*/ decltype(RsBase64::sPad) constexpr RsBase64::sPad; +#endif + +/*static*/ void RsBase64::encode( + rs_view_ptr data, size_t len, std::string& outString, + bool padding, bool urlSafe ) +{ + if(!data || !len) + { + outString.clear(); + return; + } + + const char* sDict = urlSafe ? uDict : bDict; + + // Workaround if input and output are the same buffer. + bool inplace = (outString.data() == reinterpret_cast(data)); + std::string tBuff; + std::string& outStr = inplace ? tBuff : outString; + + auto encSize = encodedSize(len, padding); + outStr.resize(encSize); + char* p = &outStr[0]; + + for (; len >= 3; len -= 3, data += 3) + { + *p++ = sDict[ (data[0] >> 2) & 077 ]; + *p++ = sDict[ + (((data[0] << 4) & 060) | ((data[1] >> 4) & 017)) & 077 ]; + *p++ = sDict[ + (((data[1] << 2) & 074) | ((data[2] >> 6) & 03)) & 077 ]; + *p++ = sDict[ data[2] & 077 ]; + } + if (len == 2) + { + *p++ = sDict[ (data[0] >> 2) & 077 ]; + *p++ = sDict[ + (((data[0] << 4) & 060) | ((data[1] >> 4) & 017)) & 077 ]; + *p++ = sDict[ ((data[1] << 2) & 074) ]; + if(padding) *p++ = sPad; + } + else if (len == 1) + { + *p++ = sDict[ (data[0] >> 2) & 077 ]; + *p++ = sDict[ (data[0] << 4) & 060 ]; + if(padding) { *p++ = sPad; *p++ = sPad; } + } + + if(inplace) outString = tBuff; +} + +/*static*/ std::error_condition RsBase64::decode( + const std::string& encoded, std::vector& decoded ) +{ + size_t decSize; std::error_condition ec; + std::tie(decSize, ec) = decodedSize(encoded); + if(!decSize || ec) return ec; + + size_t encSize = encoded.size(); + decoded.resize(decSize); + + for (size_t i = 0, o = 0; i < encSize; i += 4, o += 3) + { + char input0 = encoded[i + 0]; + char input1 = encoded[i + 1]; + + /* At the end of the string, missing bytes 2 and 3 are considered + * padding '=' */ + char input2 = i + 2 < encoded.size() ? encoded[i + 2] : sPad; + char input3 = i + 3 < encSize ? encoded[i + 3] : sPad; + + // If any unknown characters appear, it's an error. + if(!( isBase64Char(input0) && isBase64Char(input1) && + isBase64Char(input2) && isBase64Char(input3) )) + return std::errc::argument_out_of_domain; + + /* If padding appears anywhere but the last 1 or 2 characters, or if + * it appears but encoded.size() % 4 != 0, it's an error. */ + bool at_end = (i + 4 >= encSize); + if ( (input0 == sPad) || (input1 == sPad) || + ( input2 == sPad && !at_end ) || + ( input2 == sPad && input3 != sPad ) || + ( input3 == sPad && !at_end) ) + return std::errc::illegal_byte_sequence; + + uint32_t b0 = rDict[static_cast(input0)] & 0x3f; + uint32_t b1 = rDict[static_cast(input1)] & 0x3f; + uint32_t b2 = rDict[static_cast(input2)] & 0x3f; + uint32_t b3 = rDict[static_cast(input3)] & 0x3f; + + uint32_t stream = (b0 << 18) | (b1 << 12) | (b2 << 6) | b3; + decoded[o + 0] = (stream >> 16) & 0xFF; + if (input2 != sPad) decoded[o + 1] = (stream >> 8) & 0xFF; + /* If there are any stale bits in this from input1, the text is + * malformed. */ + else if (((stream >> 8) & 0xFF) != 0) + return std::errc::invalid_argument; + + if (input3 != sPad) decoded[o + 2] = (stream >> 0) & 0xFF; + /* If there are any stale bits in this from input2, the text is + * malformed. */ + else if (((stream >> 0) & 0xFF) != 0) + return std::errc::invalid_argument; + } + + return std::error_condition(); +} + +/*static*/ size_t RsBase64::encodedSize(size_t decodedSize, bool padding) +{ + if(!decodedSize) return 0; + + // Thanks https://stackoverflow.com/a/45401395 + if(padding) return ceilDivision(decodedSize, 3) * 4; + return ceilDivision(decodedSize * 8, 6); +} + +/*static*/ std::tuple RsBase64::decodedSize( + const std::string& input ) +{ + const auto success = [](size_t val) + { return std::make_tuple(val, std::error_condition()); }; + + if(input.empty()) return success(0); + + auto mod = input.size() % 4; + if(mod == 1) std::make_tuple(0, std::errc::invalid_argument); + + size_t padded_size = ((input.size() + 3) / 4) * 3; + if (mod >= 2 || (mod == 0 && input[input.size() - 1] == sPad)) + { + /* If the last byte is '=', or the input size % 4 is 2 or 3 (thus + * there are implied '='s), then the actual size is 1-2 bytes + * smaller. */ + if ( mod == 2 || (mod == 0 && input[input.size() - 2] == sPad) ) + { + /* If the second-to-last byte is also '=', or the input + * size % 4 is 2 (implying a second '='), then the actual size + * is 2 bytes smaller. */ + return success(padded_size - 2); + } + else + { + /* Otherwise it's just the last character and the actual size is + * 1 byte smaller. */ + return success(padded_size - 1); + } + } + return success(padded_size); +} + +/*static*/ size_t RsBase64::stripInvalid( + const std::string& in, std::string& out ) +{ + size_t strippedCnt = 0; + auto inSize = in.size(); + out.resize(inSize); + for(size_t i = 0; i < inSize; ++i) + { + if(isBase64Char(in[i])) out[i-strippedCnt] = in[i]; + else ++strippedCnt; + } + out.resize(inSize-strippedCnt); + return strippedCnt; +} diff --git a/libretroshare/src/util/rsbase64.h b/libretroshare/src/util/rsbase64.h new file mode 100644 index 000000000..819870089 --- /dev/null +++ b/libretroshare/src/util/rsbase64.h @@ -0,0 +1,144 @@ +/******************************************************************************* + * * + * libretroshare base64 encoding utilities * + * * + * Copyright (C) 2015 Retroshare Team * + * Copyright (C) 2020 Gioacchino Mazzurco * + * Copyright (C) 2020 Asociación Civil Altermundi * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ +#pragma once + +#include +#include +#include +#include +#include + +#include "util/rsmemory.h" + +/** + * Implement methods to encode e decode to base64 format as per RFC 4648 + * This implementation support also the file name and URL safe base64url format + * @see https://tools.ietf.org/html/rfc4648#section-5 + */ +class RsBase64 +{ +public: + /// Enable base64url by default + static constexpr bool DEFAULT_URL_SAFE = true; + + /// Disable padding by default + static constexpr bool DEFAULT_PADDING = false; + + /** + * @brief Encode arbitrary data to base64 + * @param[in] data pointer to the input data buffer + * @param[in] len lenght of the input buffer + * @param[out] outString storage for the resulting base64 encoded string + * @param[in] padding set to true to enable padding to 32 bits + * @param[in] urlSafe pass true for base64url format, false for base64 format + */ + static void encode( + rs_view_ptr data, size_t len, + std::string& outString, + bool padding = DEFAULT_PADDING, bool urlSafe = DEFAULT_URL_SAFE ); + + /** + * @brief Decode data from a base64 encoded string + * @param[in] encoded encoded string + * @param[out] decoded storage for decoded data + * @return success or error details + */ + static std::error_condition decode( + const std::string& encoded, std::vector& decoded ); + + /** + * Remove invalid characters from base64 encoded string. + * Often when copy and pasting from one progam to another long base64 + * strings, new lines, spaces or other characters end up polluting the + * original text. This function is useful to cleanup the pollution before + * attempting to decode the message. + * @param in input string + * @param out storage for cleaned string. In-place operation in supported so + * the same input string may be passed. + * @return count of stripped invalid characters + */ + static size_t stripInvalid(const std::string& in, std::string& out); + + /** + * Calculate how much bytes are needed to store the base64 encoded version + * of some data. + * @param decodedSize size of the original decoded data + * @param padding true to enable base64 padding + * @return how much bytes would take to store the encoded version + */ + static size_t encodedSize( + size_t decodedSize, bool padding = DEFAULT_PADDING ); + + /** + * @brief Calculate how much space is needed to store the decoded version of + * a base64 encoded string + * @param input encoded string + * @return decoded size, plus error information on failure + */ + static std::tuple decodedSize( + const std::string& input ); + +private: + /// base64 conversion table + static constexpr char bDict[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + /// base64url conversion table + static constexpr char uDict[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + + /// This reverse table supports both base64 and base64url + static constexpr int8_t rDict[256] = { + /* index +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 */ + /* 0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 16 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 32 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, + /* 48 */ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 0, -1, -1, + /* 64 */ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + /* 80 */ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, + /* 96 */ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + /* 112 */ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + /* 128 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 144 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 160 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 176 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 192 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 208 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 224 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 240 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + }; + + /// base64 padding character + static constexpr char sPad = '='; + + /** Check if given character is valid either for base64 or for base64url + * @param c character to check + * @return true if valid false otherwise + */ + static inline bool isBase64Char(char c) + { return rDict[static_cast(c)] >= 0; } + + /** Perform ceil division without floating point operations */ + static inline size_t ceilDivision(size_t dividend, size_t divisor) + { return (dividend + divisor - 1) / divisor; } +}; diff --git a/libretroshare/src/util/rsdebug.cc b/libretroshare/src/util/rsdebug.cc index 93049e1b9..0a4777ab0 100644 --- a/libretroshare/src/util/rsdebug.cc +++ b/libretroshare/src/util/rsdebug.cc @@ -30,6 +30,12 @@ std::ostream &operator<<(std::ostream& out, const std::error_condition& err) << " category: " << err.category().name(); } +std::string rsErrorNotInCategory(int errNum, const std::string& categoryName) +{ + return "Error message for error: " + std::to_string(errNum) + + " not available in category: " + categoryName; +} + //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// diff --git a/libretroshare/src/util/rsdebug.h b/libretroshare/src/util/rsdebug.h index 1148f74f2..6409e9492 100644 --- a/libretroshare/src/util/rsdebug.h +++ b/libretroshare/src/util/rsdebug.h @@ -21,17 +21,23 @@ *******************************************************************************/ #pragma once -#include +#include +#include #include -/** Stream helper for std::error_condition */ -std::ostream &operator<<(std::ostream& out, const std::error_condition& err); - #ifdef __ANDROID__ # include -# include -# include +#else // def __ANDROID__ +# include +# include +# include +#endif // def __ANDROID__ + +#include "util/rsjson.h" + + +#ifdef __ANDROID__ enum class RsLoggerCategories { DEBUG = ANDROID_LOG_DEBUG, @@ -40,43 +46,7 @@ enum class RsLoggerCategories ERROR = ANDROID_LOG_ERROR, FATAL = ANDROID_LOG_FATAL }; - -template -struct t_RsLogger -{ - inline t_RsLogger() = default; - - typedef t_RsLogger stream_type; - - template - inline stream_type& operator<<(const T& val) - { ostr << val; return *this; } - - /// needed for manipulators and things like std::endl - stream_type& operator<<(std::ostream& (*pf)(std::ostream&)) - { - if(pf == static_cast( - &std::endl< char, std::char_traits > )) - { - __android_log_write( - static_cast(CATEGORY), - "RetroShare", ostr.str().c_str() ); - ostr.str() = ""; - } - else ostr << pf; - - return *this; - } - -private: - std::ostringstream ostr; -}; - #else // def __ANDROID__ - -#include -#include - enum class RsLoggerCategories { DEBUG = 'D', @@ -85,55 +55,105 @@ enum class RsLoggerCategories ERROR = 'E', FATAL = 'F' }; - -template -struct t_RsLogger -{ - inline t_RsLogger() = default; - - typedef decltype(std::cerr) stream_type; - - template - inline stream_type& operator<<(const T& val) - { - return std::cerr << static_cast(CATEGORY) << " " << time(nullptr) - << " " << val; - } -}; #endif // def __ANDROID__ -/** - * Comfortable debug message loggin, supports chaining like std::cerr but can - * be easly and selectively disabled at compile time to reduce generated binary - * size and performance impact without too many \#ifdef around. - * - * To selectively debug your context you can just add something like this in - * in that context, as an example for a class you can just add a line like this - * inside class declaration: -@code{.cpp} -RS_SET_CONTEXT_DEBUG_LEVEL(2) -@endcode - * And the you can write debug messages around the code of the class like this: -@code{.cpp} -Dbg1() << "Level 1 debug message example, this will be compiled and " - << "printed" << std::endl; -Dbg2() << "Level 2 debug message example, this will be compiled and " - << "printed" << std::endl; -Dbg3() << "Level 3 debug message example, this will not be compiled and " - << "printed, and without #ifdef around!!" << std::endl; -Dbg4() << "Level 4 debug message example, this will not be compiled and " - << "printed, and without #ifdef around!!" << std::endl; -@endcode - * To change the debugging level, for example to completely disable debug - * messages you can change it to 0 -@code{.cpp} -RS_SET_CONTEXT_DEBUG_LEVEL(0) -@endcode - * While to set it to maximim level you have to pass 4. - */ -using RsDbg = t_RsLogger; +/** Stream helper for std::error_condition */ +std::ostream &operator<<(std::ostream& out, const std::error_condition& err); +/** Provide unkown error message for all error categories to avoid duplicating + * the message around */ +std::string rsErrorNotInCategory(int errNum, const std::string& categoryName); + + +template +struct t_RsLogger : std::ostringstream +{ + t_RsLogger() { setPrefix(); } + ~t_RsLogger() { flush(); } + + /** Offer variadic style, this doesn't supports things like std::endl as + * paramether but when used toghether with conditional debugging macros + * reduces binary size as paramethers of suppressed calls are not evaluated + * and literally disappear in preprocessing fase @see RsDbg */ + template + explicit inline t_RsLogger(Args&&... args) + { + setPrefix(); + + /* Combine initializer list and comma operator so the compiler unpack + * template arguments and feed our own stream without recursion + * see https://stackoverflow.com/a/27375675 */ + using expander = int[]; + (void) expander {0, (void((*this) << std::forward(args)), 0)...}; + } + + /** Dump buffer stream to log */ + void flush() + { +#ifdef __ANDROID__ + __android_log_write( + static_cast(CATEGORY), + "RetroShare", str().c_str() ); +#else // def __ANDROID__ + (*this) << std::endl; + std::cerr << str(); +#endif // def __ANDROID__ + str() = ""; + } + +private: +#ifdef __ANDROID__ + inline void setPrefix() {} +#else // def __ANDROID__ + void setPrefix() + { + using namespace std::chrono; + const auto now = system_clock::now(); + const auto sec = time_point_cast(now); + const auto msec = duration_cast(now - sec); + (*this) << static_cast(CATEGORY) << " " + << sec.time_since_epoch().count() << "." + << std::setfill('0') << std::setw(3) << msec.count() << " "; + } +#endif // def __ANDROID__ +}; + + +/** + * Comfortable debug message logging, supports both variadic style and chaining + * style like std::cerr. + * Can be easly and selectively disabled at compile time. + * To reduce generated binary size and performance impact when debugging is + * disabled without too many \#ifdef around the code combining the variadic + * style with the leveled debugging macros is the way to go. + * + * To selectively debug your file you just need to include the header of desired + * debugging level (0 to 4) +@code{.cpp} +#include "util/rsdebuglevel2.h" +@endcode + * Then where you want to print debug messages use +@code{.cpp} +RS_DBG0("Hello 0 ", "my debug ", my_variable) << " message " << variable2; +RS_DBG1("Hello 1 ", "my debug ", my_variable) << " message " << variable2; +RS_DBG2("Hello 2 ", "my debug ", my_variable) << " message " << variable2; +RS_DBG3("Hello 3 ", "my debug ", my_variable) << " message " << variable2; +RS_DBG4("Hello 4 ", "my debug ", my_variable) << " message " << variable2; +@endcode + * To change the debugging level just include a different level header like + * `util/rsdebuglevel1.h`, debug messages with lower or equal level then the + * included header will be printed, the others will not. + * Remember then on messages with debug level higher then the included the + * paramethers you pass as macro arguments (variadic style) will disappear in + * the preprocessing phase, so their evaluation will not be included in the + * final binary and not executed at runtime, instead the paramether passed with + * `<<` (chaining style) will be in the compiled binary and evaluated at runtime + * even if are not printed, due to how C++ is made it is not possible to avoid + * this, so we suggest to use variadic style for debug messages. + */ +using RsDbg = t_RsLogger; +#define RS_DBG(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__) /** * Comfortable log information reporting helper, supports chaining like @@ -143,17 +163,22 @@ using RsDbg = t_RsLogger; RsInfo() << __PRETTY_FUNCTION__ << "My information message" << std::cerr; @endcode */ -using RsInfo = t_RsLogger; +using RsInfo = t_RsLogger; +#define RS_INFO(...) RsInfo(__PRETTY_FUNCTION__, " ", __VA_ARGS__) /// Similar to @see RsInfo but for warning messages -using RsWarn = t_RsLogger; +using RsWarn = t_RsLogger; +#define RS_WARN(...) RsWarn(__PRETTY_FUNCTION__, " ", __VA_ARGS__) /// Similar to @see RsInfo but for error messages -using RsErr = t_RsLogger; +using RsErr = t_RsLogger; +#define RS_ERR(...) RsErr(__PRETTY_FUNCTION__, " ", __VA_ARGS__) /** Similar to @see RsInfo but for fatal errors (the ones which cause RetroShare * to terminate) messages */ -using RsFatal = t_RsLogger; +using RsFatal = t_RsLogger; +#define RS_FATAL(...) RsFatal(__PRETTY_FUNCTION__, " ", __VA_ARGS__) + /** * Keeps compatible syntax with RsDbg but explicitely do nothing in a way that @@ -163,19 +188,31 @@ using RsFatal = t_RsLogger; struct RsNoDbg { inline RsNoDbg() = default; + template inline explicit RsNoDbg(Args...) {} - /** - * This match most of the types, but might be not enough for templated - * types - */ + /** This match most of the types, but might be not enough for templated + * types */ template inline RsNoDbg& operator<<(const T&) { return *this; } /// needed for manipulators and things like std::endl inline RsNoDbg& operator<<(std::ostream& (*/*pf*/)(std::ostream&)) { return *this; } + + /** Do nothing. Just for code compatibility with other logging classes */ + inline void flush() {} }; + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// All the following lines are DEPRECATED!! + +#include "util/rsdeprecate.h" + /** * Concatenate preprocessor tokens A and B without expanding macro definitions * (however, if invoked from a macro, macro arguments are expanded). @@ -194,41 +231,30 @@ struct RsNoDbg // A bunch of boilerplate, but just in one place #define RS_SET_CONTEXT_DEBUG_LEVEL0 \ - using Dbg1 = RsNoDbg; \ - using Dbg2 = RsNoDbg; \ - using Dbg3 = RsNoDbg; \ - using Dbg4 = RsNoDbg; + using Dbg1 RS_DEPRECATED_FOR(RS_DBG1) = RsNoDbg; \ + using Dbg2 RS_DEPRECATED_FOR(RS_DBG2) = RsNoDbg; \ + using Dbg3 RS_DEPRECATED_FOR(RS_DBG3) = RsNoDbg; \ + using Dbg4 RS_DEPRECATED_FOR(RS_DBG4) = RsNoDbg; #define RS_SET_CONTEXT_DEBUG_LEVEL1 \ - using Dbg1 = RsDbg; \ - using Dbg2 = RsNoDbg; \ - using Dbg3 = RsNoDbg; \ - using Dbg4 = RsNoDbg; + using Dbg1 RS_DEPRECATED_FOR(RS_DBG1) = RsDbg; \ + using Dbg2 RS_DEPRECATED_FOR(RS_DBG2) = RsNoDbg; \ + using Dbg3 RS_DEPRECATED_FOR(RS_DBG3) = RsNoDbg; \ + using Dbg4 RS_DEPRECATED_FOR(RS_DBG4) = RsNoDbg; #define RS_SET_CONTEXT_DEBUG_LEVEL2 \ - using Dbg1 = RsDbg; \ - using Dbg2 = RsDbg; \ - using Dbg3 = RsNoDbg; \ - using Dbg4 = RsNoDbg; + using Dbg1 RS_DEPRECATED_FOR(RS_DBG1) = RsDbg; \ + using Dbg2 RS_DEPRECATED_FOR(RS_DBG2) = RsDbg; \ + using Dbg3 RS_DEPRECATED_FOR(RS_DBG3) = RsNoDbg; \ + using Dbg4 RS_DEPRECATED_FOR(RS_DBG4) = RsNoDbg; #define RS_SET_CONTEXT_DEBUG_LEVEL3 \ - using Dbg1 = RsDbg; \ - using Dbg2 = RsDbg; \ - using Dbg3 = RsDbg; \ - using Dbg4 = RsNoDbg; + using Dbg1 RS_DEPRECATED_FOR(RS_DBG1) = RsDbg; \ + using Dbg2 RS_DEPRECATED_FOR(RS_DBG2) = RsDbg; \ + using Dbg3 RS_DEPRECATED_FOR(RS_DBG3) = RsDbg; \ + using Dbg4 RS_DEPRECATED_FOR(RS_DBG4) = RsNoDbg; #define RS_SET_CONTEXT_DEBUG_LEVEL4 \ - using Dbg1 = RsDbg; \ - using Dbg2 = RsDbg; \ - using Dbg3 = RsDbg; \ - using Dbg4 = RsDbg; - - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -/// All the following lines are DEPRECATED!! - -#include - -#include "util/rsdeprecate.h" + using Dbg1 RS_DEPRECATED_FOR(RS_DBG1) = RsDbg; \ + using Dbg2 RS_DEPRECATED_FOR(RS_DBG2) = RsDbg; \ + using Dbg3 RS_DEPRECATED_FOR(RS_DBG3) = RsDbg; \ + using Dbg4 RS_DEPRECATED_FOR(RS_DBG4) = RsDbg; namespace RsLog { enum RS_DEPRECATED_FOR("RsErr, RsDbg, RsNoDbg") logLvl { diff --git a/libretroshare/src/util/rsdebuglevel0.h b/libretroshare/src/util/rsdebuglevel0.h new file mode 100644 index 000000000..5c68ec0be --- /dev/null +++ b/libretroshare/src/util/rsdebuglevel0.h @@ -0,0 +1,42 @@ +/******************************************************************************* + * RetroShare debugging level * + * * + * Copyright (C) 2020 Gioacchino Mazzurco * + * Copyright (C) 2020 Asociación Civil Altermundi * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +// #pragma once // This is commented out on purpose! + +#include + +#undef RS_DEBUG_LEVEL +#define RS_DEBUG_LEVEL 0 + +#undef RS_DBG0 +#define RS_DBG0(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__) + +#undef RS_DBG1 +#define RS_DBG1(...) RsNoDbg("") + +#undef RS_DBG2 +#define RS_DBG2(...) RsNoDbg("") + +#undef RS_DBG3 +#define RS_DBG3(...) RsNoDbg("") + +#undef RS_DBG4 +#define RS_DBG4(...) RsNoDbg("") diff --git a/libretroshare/src/util/rsdebuglevel1.h b/libretroshare/src/util/rsdebuglevel1.h new file mode 100644 index 000000000..7e968e402 --- /dev/null +++ b/libretroshare/src/util/rsdebuglevel1.h @@ -0,0 +1,42 @@ +/******************************************************************************* + * RetroShare debugging level * + * * + * Copyright (C) 2020 Gioacchino Mazzurco * + * Copyright (C) 2020 Asociación Civil Altermundi * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +// #pragma once // This is commented out on purpose! + +#include + +#undef RS_DEBUG_LEVEL +#define RS_DEBUG_LEVEL 1 + +#undef RS_DBG0 +#define RS_DBG0(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__) + +#undef RS_DBG1 +#define RS_DBG1(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__) + +#undef RS_DBG2 +#define RS_DBG2(...) RsNoDbg() + +#undef RS_DBG3 +#define RS_DBG3(...) RsNoDbg() + +#undef RS_DBG4 +#define RS_DBG4(...) RsNoDbg() diff --git a/libretroshare/src/util/rsdebuglevel2.h b/libretroshare/src/util/rsdebuglevel2.h new file mode 100644 index 000000000..2cbf1a224 --- /dev/null +++ b/libretroshare/src/util/rsdebuglevel2.h @@ -0,0 +1,42 @@ +/******************************************************************************* + * RetroShare debugging level * + * * + * Copyright (C) 2020 Gioacchino Mazzurco * + * Copyright (C) 2020 Asociación Civil Altermundi * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +// #pragma once // This is commented out on purpose! + +#include + +#undef RS_DEBUG_LEVEL +#define RS_DEBUG_LEVEL 2 + +#undef RS_DBG0 +#define RS_DBG0(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__) + +#undef RS_DBG1 +#define RS_DBG1(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__) + +#undef RS_DBG2 +#define RS_DBG2(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__) + +#undef RS_DBG3 +#define RS_DBG3(...) RsNoDbg() + +#undef RS_DBG4 +#define RS_DBG4(...) RsNoDbg() diff --git a/libretroshare/src/util/rsdebuglevel3.h b/libretroshare/src/util/rsdebuglevel3.h new file mode 100644 index 000000000..53bcbfe9f --- /dev/null +++ b/libretroshare/src/util/rsdebuglevel3.h @@ -0,0 +1,42 @@ +/******************************************************************************* + * RetroShare debugging level * + * * + * Copyright (C) 2020 Gioacchino Mazzurco * + * Copyright (C) 2020 Asociación Civil Altermundi * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +// #pragma once // This is commented out on purpose! + +#include + +#undef RS_DEBUG_LEVEL +#define RS_DEBUG_LEVEL 3 + +#undef RS_DBG0 +#define RS_DBG0(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__) + +#undef RS_DBG1 +#define RS_DBG1(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__) + +#undef RS_DBG2 +#define RS_DBG2(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__) + +#undef RS_DBG3 +#define RS_DBG3(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__) + +#undef RS_DBG4 +#define RS_DBG4(...) RsNoDbg() diff --git a/libretroshare/src/util/rsdebuglevel4.h b/libretroshare/src/util/rsdebuglevel4.h new file mode 100644 index 000000000..f8697cad8 --- /dev/null +++ b/libretroshare/src/util/rsdebuglevel4.h @@ -0,0 +1,42 @@ +/******************************************************************************* + * RetroShare debugging level * + * * + * Copyright (C) 2020 Gioacchino Mazzurco * + * Copyright (C) 2020 Asociación Civil Altermundi * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +// #pragma once // This is commented out on purpose! + +#include + +#undef RS_DEBUG_LEVEL +#define RS_DEBUG_LEVEL 4 + +#undef RS_DBG0 +#define RS_DBG0(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__) + +#undef RS_DBG1 +#define RS_DBG1(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__) + +#undef RS_DBG2 +#define RS_DBG2(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__) + +#undef RS_DBG3 +#define RS_DBG3(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__) + +#undef RS_DBG4 +#define RS_DBG4(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__) diff --git a/libretroshare/src/util/rsdir.cc b/libretroshare/src/util/rsdir.cc index 1b794f982..fe9907d2d 100644 --- a/libretroshare/src/util/rsdir.cc +++ b/libretroshare/src/util/rsdir.cc @@ -3,7 +3,9 @@ * * * libretroshare: retroshare core library * * * - * Copyright 2004-2007 by Robert Fernie * + * Copyright (C) 2004-2007 Robert Fernie * + * Copyright (C) 2020 Gioacchino Mazzurco * + * Copyright (C) 2020 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -265,36 +267,48 @@ bool RsDirUtil::fileExists(const std::string& filename) bool RsDirUtil::moveFile(const std::string& source,const std::string& dest) { - // Check that the destination directory exists. If not, create it. + Dbg3() << __PRETTY_FUNCTION__<< " source: " << source + << " dest: " << dest << std::endl; std::string dest_dir ; std::string dest_file ; - splitDirFromFile(dest,dest_dir,dest_file) ; + splitDirFromFile(dest, dest_dir, dest_file); - std::cerr << "Moving file " << source << " to " << dest << std::endl; - std::cerr << "Checking that directory " << dest_dir << " actually exists." << std::endl; + if(!checkDirectory(dest_dir)) + { + if(!std::filesystem::create_directories(dest_dir)) + { + RsErr() << __PRETTY_FUNCTION__ << " failure creating directory: " + << dest_dir << std::endl; + return false; + } + } - if(!checkCreateDirectory(dest_dir)) - return false ; + // First try a rename + if(renameFile(source,dest)) + { + Dbg3() << __PRETTY_FUNCTION__ << " plain rename worked" << std::endl; + return true; + } - // First try a rename - // + /* If not, try to copy. The src and dest probably belong to different file + * systems */ + if(!copyFile(source,dest)) + { + RsErr() << __PRETTY_FUNCTION__ << " failure copying file" << std::endl; + return false; + } - if(renameFile(source,dest)) - return true ; + // delete the original + if(!removeFile(source)) + { + RsErr() << __PRETTY_FUNCTION__ << " failure deleting original file" + << std::endl; + return false; + } - // If not, try to copy. The src and dest probably belong to different file systems - - if(!copyFile(source,dest)) - return false ; - - // copy was successful, let's delete the original - - if(!removeFile(source)) - return false ; - - return true ; + return true; } bool RsDirUtil::removeFile(const std::string& filename) @@ -425,7 +439,7 @@ bool RsDirUtil::checkFile(const std::string& filename,uint64_t& file_size,bool d } -bool RsDirUtil::checkDirectory(const std::string& dir) +bool RsDirUtil::checkDirectory(const std::string& dir) { int val; mode_t st_mode; @@ -461,11 +475,9 @@ bool RsDirUtil::checkDirectory(const std::string& dir) } -bool RsDirUtil::checkCreateDirectory(const std::string& dir) +bool RsDirUtil::checkCreateDirectory(const std::string& dir) { -#ifdef RSDIR_DEBUG - std::cerr << "RsDirUtil::checkCreateDirectory() dir: " << dir << std::endl; -#endif + Dbg3() << __PRETTY_FUNCTION__ << " " << dir << std::endl; #ifdef WINDOWS_SYS std::wstring wdir; @@ -516,6 +528,23 @@ bool RsDirUtil::checkCreateDirectory(const std::string& dir) return true; } +#if __cplusplus < 201703L +bool std::filesystem::create_directories(const std::string& path) +{ + for( std::string::size_type lastIndex = 0; lastIndex < std::string::npos; + lastIndex = path.find('/', lastIndex) ) + { + std::string&& curDir = path.substr(0, ++lastIndex); + if(!RsDirUtil::checkCreateDirectory(curDir)) + { + RsErr() << __PRETTY_FUNCTION__ << " failure creating: " << curDir + << " of: " << path << std::endl; + return false; + } + } + return true; +} +#endif // __cplusplus < 201703L std::string RsDirUtil::removeSymLinks(const std::string& path) { diff --git a/libretroshare/src/util/rsdir.h b/libretroshare/src/util/rsdir.h index b6ef4891d..50e636541 100644 --- a/libretroshare/src/util/rsdir.h +++ b/libretroshare/src/util/rsdir.h @@ -3,7 +3,9 @@ * * * libretroshare: retroshare core library * * * - * Copyright 2004-2007 by Robert Fernie * + * Copyright (C) 2004-2007 Robert Fernie * + * Copyright (C) 2020 Gioacchino Mazzurco * + * Copyright (C) 2020 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -20,8 +22,7 @@ * * *******************************************************************************/ -#ifndef RSUTIL_DIRFNS_H -#define RSUTIL_DIRFNS_H +#pragma once #include #include @@ -84,7 +85,10 @@ int breakupDirList(const std::string& path, std::list &subdirs bool splitDirFromFile(const std::string& full_path,std::string& dir, std::string& file); bool copyFile(const std::string& source,const std::string& dest); -bool moveFile(const std::string& source,const std::string& dest); + +/** Move file. If destination directory doesn't exists create it. */ +bool moveFile(const std::string& source, const std::string& dest); + bool removeFile(const std::string& file); bool fileExists(const std::string& file); bool checkFile(const std::string& filename,uint64_t& file_size,bool disallow_empty_file = false); @@ -141,8 +145,20 @@ bool getWideFileHash(std::wstring filepath, RsFileHash &hash, u FILE *rs_fopen(const char* filename, const char* mode); std::string convertPathToUnix(std::string path); + +/** Concatenate two path pieces putting '/' separator between them only if + * needed */ std::string makePath(const std::string &path1, const std::string &path2); + +RS_SET_CONTEXT_DEBUG_LEVEL(1); } - -#endif +#if __cplusplus < 201703L +namespace std +{ +namespace filesystem +{ +bool create_directories(const std::string& path); +} +} +#endif // __cplusplus < 201703L diff --git a/libretroshare/src/util/rsendian.h b/libretroshare/src/util/rsendian.h new file mode 100644 index 000000000..d27c374ff --- /dev/null +++ b/libretroshare/src/util/rsendian.h @@ -0,0 +1,59 @@ +/******************************************************************************* + * * + * libretroshare endiannes utilities * + * * + * Copyright (C) 2020 Gioacchino Mazzurco * + * Copyright (C) 2020 Asociación Civil Altermundi * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ +#pragma once + +/** + * @file + * This file provide convenient integer endiannes conversion utilities. + * Instead of providing them with different names for each type (a la C htonl, + * htons, ntohl, ntohs ), which make them uncomfortable to use, expose a + * templated function `rs_endian_fix` to reorder integers bytes representation + * when sending or receiving from the network. */ + +/* enforce LITTLE_ENDIAN on Windows */ +#ifdef WINDOWS_SYS +# define BYTE_ORDER 1234 +# define LITTLE_ENDIAN 1234 +# define BIG_ENDIAN 4321 +#else +#include +#endif + +template inline INTTYPE rs_endian_fix(INTTYPE val) +{ +#if BYTE_ORDER == LITTLE_ENDIAN + INTTYPE swapped = 0; + for (size_t i = 0; i < sizeof(INTTYPE); ++i) + swapped |= (val >> (8*(sizeof(INTTYPE)-i-1)) & 0xFF) << (8*i); + return swapped; +#else + return val; +#endif +}; + +#ifndef BYTE_ORDER +# error "ENDIAN determination Failed (BYTE_ORDER not defined)" +#endif + +#if !(BYTE_ORDER == BIG_ENDIAN || BYTE_ORDER == LITTLE_ENDIAN) +# error "ENDIAN determination Failed (unkown BYTE_ORDER value)" +#endif diff --git a/libretroshare/src/util/rsjson.h b/libretroshare/src/util/rsjson.h index db864a73f..13073f3b2 100644 --- a/libretroshare/src/util/rsjson.h +++ b/libretroshare/src/util/rsjson.h @@ -29,7 +29,7 @@ typedef rapidjson::Document RsJson; /** - * Print out RsJson to a stream, use std::stringstream to get the string + * Print out RsJson to a stream, use std::ostringstream to get the string * @param[out] out output stream * @param[in] jDoc JSON document to print * @return same output stream passed as out parameter diff --git a/libretroshare/src/util/rsmemcache.h b/libretroshare/src/util/rsmemcache.h index 95249c28e..53d5efa27 100644 --- a/libretroshare/src/util/rsmemcache.h +++ b/libretroshare/src/util/rsmemcache.h @@ -60,7 +60,12 @@ public: bool is_cached(const Key &key) const; bool fetch(const Key &key, Value &data); - Value &ref(const Key &key); // like map[] installs empty one if non-existent. + + // Like map[] installs empty one if non-existent. + + Value& ref(const Key &key); + Value& operator[](const Key& key) { return ref(key); } + bool store(const Key &key, const Value &data); bool erase(const Key &key); // clean up cache. @@ -70,7 +75,8 @@ public: template bool applyToAllCachedEntries(ClientClass& c,bool (ClientClass::*method)(Value&)); - uint32_t size() const { return mDataMap.size() ; } + uint32_t size() const { return mDataMap.size() ; } + void printStats(std::ostream& out); private: bool update_lrumap(const Key &key, rstime_t old_ts, rstime_t new_ts); @@ -96,7 +102,6 @@ private: std::string mName; // some statistics. - void printStats(std::ostream &out); void clearStats(); mutable uint32_t mStats_inserted; diff --git a/libretroshare/src/util/rsmemory.h b/libretroshare/src/util/rsmemory.h index 01dc4d8d8..eb2889a6f 100644 --- a/libretroshare/src/util/rsmemory.h +++ b/libretroshare/src/util/rsmemory.h @@ -4,7 +4,7 @@ * libretroshare: retroshare core library * * * * Copyright 2012 Cyril Soler * - * Copyright 2019 Gioacchino Mazzurco * + * Copyright 2019-2020 Gioacchino Mazzurco * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -62,7 +62,6 @@ bool myFunnyFunction( */ #define RS_DEFAULT_STORAGE_PARAM(Type,...) *std::unique_ptr(new Type(__VA_ARGS__)) - /** @brief Safely dynamic cast between std::unique_ptr of different types * std::unique_ptr semantic rely on the invariant that only one instance own * the object, when casting between differents types one would be tempted to do @@ -90,9 +89,28 @@ bool rs_unique_cast( return false; } +/** Mark a pointer as non-owned aka it must not be deleted/freed in that context. + * If a function take an `rs_view_ptr` as paramether it means that she will not + * own (aka free/delete) the passed memory, instead the caller is in charge of + * managing it. If a function return an `rs_view_ptr` it means the memory is + * managed internally and the caller should not call free/delete on it. + * @see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1408r0.pdf */ +template using rs_view_ptr = T*; + +/** Mark a pointer as owned aka the receiving context is in charge of dealing + * with it by free/delete once finished. + * If a function take an `rs_owner_ptr` as paramether it means that she will own + * (aka free/delete when finished using it) the passed memory, instead the + * caller is NOT in charge of managing it. + * If a function return an `rs_owner_ptr` it means the memory is NOT managed + * internally and the caller should call free/delete on it once finished using + * it. + * @see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1408r0.pdf */ +template using rs_owner_ptr = T*; void *rs_malloc(size_t size) ; +/** @deprecated use std::unique_ptr instead // This is a scope guard to release the memory block when going of of the current scope. // Can be very useful to auto-delete some memory on quit without the need to call free each time. // @@ -109,11 +127,11 @@ void *rs_malloc(size_t size) ; // [do something] // // } // mem gets freed automatically -// +*/ class RsTemporaryMemory { public: - RsTemporaryMemory(size_t s) + explicit RsTemporaryMemory(size_t s) { _mem = (unsigned char *)rs_malloc(s) ; @@ -127,14 +145,7 @@ public: size_t size() const { return _size ; } - ~RsTemporaryMemory() - { - if(_mem != NULL) - { - free(_mem) ; - _mem = NULL ; - } - } + ~RsTemporaryMemory() { free(_mem); } private: unsigned char *_mem ; diff --git a/libretroshare/src/util/rsrandom.cc b/libretroshare/src/util/rsrandom.cc index 7ea8eb968..dc7d9ac67 100644 --- a/libretroshare/src/util/rsrandom.cc +++ b/libretroshare/src/util/rsrandom.cc @@ -121,13 +121,26 @@ double RsRandom::random_f64() return random_u64() / (double)(~(uint64_t)0) ; } -std::string RsRandom::random_alphaNumericString(uint32_t len) +/*static*/ std::string RsRandom::alphaNumeric(uint32_t length) { - std::string s = "" ; + std::string s; + while(s.size() < length) + { + uint8_t rChar; random_bytes(&rChar, 1); rChar = rChar % 123; + /* if(isalnum(val)) isalnum result may vary depend on locale!! */ + if( (rChar >= 48 && rChar <= 57) /* 0-9 */ || + (rChar >= 65 && rChar <= 90) /* A-Z */ || + (rChar >= 97 && rChar <= 122) /* a-z */ ) + s += static_cast(rChar); + } - for(uint32_t i=0;i(&ret[0]), length); + for(uint32_t i=0; i RsUrl::getQueryV(const std::string& key) { if(hasQueryK(key)) return &(mQuery.find(key)->second); return nullptr; diff --git a/libretroshare/src/util/rsurl.h b/libretroshare/src/util/rsurl.h index 0610585c5..6e6d124c3 100644 --- a/libretroshare/src/util/rsurl.h +++ b/libretroshare/src/util/rsurl.h @@ -20,6 +20,7 @@ #include #include +#include "util/rsmemory.h" #include "serialiser/rsserializable.h" struct sockaddr_storage; @@ -63,7 +64,7 @@ struct RsUrl : RsSerializable RsUrl& setQueryKV(const std::string& key, const std::string& value); RsUrl& delQueryK(const std::string& key); bool hasQueryK(const std::string& key); - const std::string* getQueryV(const std::string& key); + rs_view_ptr getQueryV(const std::string& key); const std::string& fragment() const; RsUrl& setFragment(const std::string& fragment); diff --git a/openpgpsdk/src/openpgpsdk.pro b/openpgpsdk/src/openpgpsdk.pro index fa117381a..66341d0a3 100644 --- a/openpgpsdk/src/openpgpsdk.pro +++ b/openpgpsdk/src/openpgpsdk.pro @@ -21,7 +21,7 @@ linux-* { OBJECTS_DIR = temp/linux/obj } -win32-g++ { +win32-g++|win32-clang-g++ { HEADERS += openpgpsdk/opsstring.h SOURCES += openpgpsdk/opsstring.c diff --git a/openpgpsdk/src/openpgpsdk/openssl_crypto.c b/openpgpsdk/src/openpgpsdk/openssl_crypto.c index d1573437d..086dad77a 100644 --- a/openpgpsdk/src/openpgpsdk/openssl_crypto.c +++ b/openpgpsdk/src/openpgpsdk/openssl_crypto.c @@ -22,6 +22,7 @@ /** \file */ +#include #include #include #include diff --git a/openpgpsdk/src/openpgpsdk/readerwriter.c b/openpgpsdk/src/openpgpsdk/readerwriter.c index f3a16c364..78b45e28c 100644 --- a/openpgpsdk/src/openpgpsdk/readerwriter.c +++ b/openpgpsdk/src/openpgpsdk/readerwriter.c @@ -462,9 +462,9 @@ char *ops_get_passphrase(void) { #ifndef __ANDROID__ return ops_malloc_passphrase(getpass("Passphrase: ")); -#else // __ANDROID__ - return ops_malloc_passphrase("getpass not supported on android"); -#warning "getpass not supported on android" +#else // __ANDROID + // We should never get here on Android, getpass not supported. + abort(); #endif // __ANDROID__ } diff --git a/plugins/FeedReader/gui/FeedReaderDialog.cpp b/plugins/FeedReader/gui/FeedReaderDialog.cpp index 5a00cdd6d..b795eb33b 100644 --- a/plugins/FeedReader/gui/FeedReaderDialog.cpp +++ b/plugins/FeedReader/gui/FeedReaderDialog.cpp @@ -452,7 +452,7 @@ void FeedReaderDialog::calculateFeedItem(QTreeWidgetItem *item, uint32_t &unread font.setBold(unreadCountItem != 0); item->setFont(i, font); - item->setTextColor(COLUMN_FEED_NAME, deactivated ? colorDeactivated : colorActivated); + item->setData(COLUMN_FEED_NAME, Qt::ForegroundRole, deactivated ? colorDeactivated : colorActivated); } QIcon icon = item->data(COLUMN_FEED_DATA, ROLE_FEED_ICON).value(); diff --git a/plugins/FeedReader/services/rsFeedReaderItems.cc b/plugins/FeedReader/services/rsFeedReaderItems.cc index 538f8ff93..a88abad17 100644 --- a/plugins/FeedReader/services/rsFeedReaderItems.cc +++ b/plugins/FeedReader/services/rsFeedReaderItems.cc @@ -60,11 +60,6 @@ void RsFeedReaderFeed::clear() content.clear(); } -std::ostream &RsFeedReaderFeed::print(std::ostream &out, uint16_t /*indent*/) -{ - return out; -} - uint32_t RsFeedReaderSerialiser::sizeFeed(RsFeedReaderFeed *item) { uint32_t s = 8; /* header */ diff --git a/retroshare-gui/src/gui/ChatLobbyWidget.cpp b/retroshare-gui/src/gui/ChatLobbyWidget.cpp index b139d0113..475495674 100644 --- a/retroshare-gui/src/gui/ChatLobbyWidget.cpp +++ b/retroshare-gui/src/gui/ChatLobbyWidget.cpp @@ -27,12 +27,14 @@ #include "chat/ChatTabWidget.h" #include "chat/CreateLobbyDialog.h" #include "common/RSTreeWidgetItem.h" +#include "common/RSElidedItemDelegate.h" #include "gui/RetroShareLink.h" #include "gui/gxs/GxsIdDetails.h" #include "gui/Identity/IdEditDialog.h" #include "gui/settings/rsharesettings.h" #include "util/HandleRichText.h" #include "util/QtVersion.h" +#include "gui/common/FilesDefs.h" #include "retroshare/rsmsgs.h" #include "retroshare/rspeers.h" @@ -157,6 +159,7 @@ ChatLobbyWidget::ChatLobbyWidget(QWidget *parent, Qt::WindowFlags flags) ui.lobbyTreeWidget->setColumnHidden(COLUMN_USER_COUNT,true) ; ui.lobbyTreeWidget->setColumnHidden(COLUMN_TOPIC,true) ; ui.lobbyTreeWidget->setSortingEnabled(true) ; + ui.lobbyTreeWidget->setItemDelegateForColumn(COLUMN_NAME, new RSElidedItemDelegate()); float fact = QFontMetricsF(font()).height()/14.0f; @@ -197,7 +200,7 @@ ChatLobbyWidget::ChatLobbyWidget(QWidget *parent, Qt::WindowFlags flags)

Chat rooms work pretty much like IRC. \ They allow you to talk anonymously with tons of people without the need to make friends.

\

A chat room can be public (your friends see it) or private (your friends can't see it, unless you \ - invite them with ). \ + invite them with ). \ Once you have been invited to a private room, you will be able to see it when your friends \ are using it.

\

The list at left shows \ @@ -279,7 +282,7 @@ void ChatLobbyWidget::lobbyTreeWidgetCustomPopupMenu(QPoint) QMenu contextMnu(this); if (item && item->type() == TYPE_FOLDER) { - QAction *action = contextMnu.addAction(QIcon(IMAGE_CREATE), tr("Create chat room"), this, SLOT(createChatLobby())); + QAction *action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_CREATE), tr("Create chat room"), this, SLOT(createChatLobby())); action->setData(item->data(COLUMN_DATA, ROLE_PRIVACYLEVEL).toInt()); } @@ -289,7 +292,7 @@ void ChatLobbyWidget::lobbyTreeWidgetCustomPopupMenu(QPoint) rsIdentity->getOwnIds(own_identities) ; if (item->data(COLUMN_DATA, ROLE_SUBSCRIBED).toBool()) - contextMnu.addAction(QIcon(IMAGE_UNSUBSCRIBE), tr("Leave this room"), this, SLOT(unsubscribeItem())); + contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_UNSUBSCRIBE), tr("Leave this room"), this, SLOT(unsubscribeItem())); else { QTreeWidgetItem *item = ui.lobbyTreeWidget->currentItem(); @@ -304,18 +307,18 @@ void ChatLobbyWidget::lobbyTreeWidgetCustomPopupMenu(QPoint) if(own_identities.empty()) { if(removed) - contextMnu.addAction(QIcon(IMAGE_SUBSCRIBE), tr("Create a non anonymous identity and enter this room"), this, SLOT(createIdentityAndSubscribe())); + contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_SUBSCRIBE), tr("Create a non anonymous identity and enter this room"), this, SLOT(createIdentityAndSubscribe())); else - contextMnu.addAction(QIcon(IMAGE_SUBSCRIBE), tr("Create an identity and enter this chat room"), this, SLOT(createIdentityAndSubscribe())); + contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_SUBSCRIBE), tr("Create an identity and enter this chat room"), this, SLOT(createIdentityAndSubscribe())); } else if(own_identities.size() == 1) { - QAction *action = contextMnu.addAction(QIcon(IMAGE_SUBSCRIBE), tr("Enter this chat room"), this, SLOT(subscribeChatLobbyAs())); + QAction *action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_SUBSCRIBE), tr("Enter this chat room"), this, SLOT(subscribeChatLobbyAs())); action->setData(QString::fromStdString((own_identities.front()).toStdString())) ; } else { - QMenu *mnu = contextMnu.addMenu(QIcon(IMAGE_SUBSCRIBE),tr("Enter this chat room as...")) ; + QMenu *mnu = contextMnu.addMenu(FilesDefs::getIconFromQtResourcePath(IMAGE_SUBSCRIBE),tr("Enter this chat room as...")) ; for(std::list::const_iterator it=own_identities.begin();it!=own_identities.end();++it) { @@ -342,7 +345,7 @@ void ChatLobbyWidget::lobbyTreeWidgetCustomPopupMenu(QPoint) contextMnu.addAction(QIcon(IMAGE_SUBSCRIBE), tr("Add Auto Subscribe"), this, SLOT(autoSubscribeItem())); #endif - contextMnu.addAction(QIcon(IMAGE_COPYRSLINK), tr("Copy RetroShare Link"), this, SLOT(copyItemLink())); + contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COPYRSLINK), tr("Copy RetroShare Link"), this, SLOT(copyItemLink())); } contextMnu.addSeparator();//------------------------------------------------------------------- @@ -386,6 +389,8 @@ static void updateItem(QTreeWidget *treeWidget, QTreeWidgetItem *item, ChatLobby item->setData(COLUMN_DATA, ROLE_FLAGS, lobby_flags.toUInt32()); item->setData(COLUMN_DATA, ROLE_AUTOSUBSCRIBE, autoSubscribe); + //TODO (Phenom): Add qproperty for these text colors in stylesheets + // As palette is not updated by stylesheet QColor color = treeWidget->palette().color(QPalette::Active, QPalette::Text); if (!subscribed) { @@ -395,7 +400,7 @@ static void updateItem(QTreeWidget *treeWidget, QTreeWidgetItem *item, ChatLobby } for (int column = 0; column < COLUMN_COUNT; ++column) { - item->setTextColor(column, color); + item->setData(column, Qt::ForegroundRole, color); } QString tooltipstr = QObject::tr("Subject:")+" "+item->text(COLUMN_TOPIC)+"\n" +QObject::tr("Participants:")+" "+QString::number(count)+"\n" @@ -407,7 +412,7 @@ static void updateItem(QTreeWidget *treeWidget, QTreeWidgetItem *item, ChatLobby tooltipstr += QObject::tr("\nSecurity: no anonymous IDs") ; QColor foreground = QColor(0, 128, 0); // green for (int column = 0; column < COLUMN_COUNT; ++column) - item->setTextColor(column, foreground); + item->setData(column, Qt::ForegroundRole, foreground); } item->setToolTip(0,tooltipstr) ; } @@ -418,9 +423,7 @@ void ChatLobbyWidget::addChatPage(ChatLobbyDialog *d) if(_lobby_infos.find(d->id()) == _lobby_infos.end()) { - ui.stackedWidget->addWidget(d) ; - - connect(d,SIGNAL(lobbyLeave(ChatLobbyId)),this,SLOT(unsubscribeChatLobby(ChatLobbyId))) ; + connect(d,SIGNAL(dialogClose(ChatDialog*)),this,SLOT(dialogClose(ChatDialog*))); connect(d,SIGNAL(typingEventReceived(ChatLobbyId)),this,SLOT(updateTypingStatus(ChatLobbyId))) ; connect(d,SIGNAL(messageReceived(bool,ChatLobbyId,QDateTime,QString,QString)),this,SLOT(updateMessageChanged(bool,ChatLobbyId,QDateTime,QString,QString))) ; connect(d,SIGNAL(peerJoined(ChatLobbyId)),this,SLOT(updatePeerEntering(ChatLobbyId))) ; @@ -429,14 +432,33 @@ void ChatLobbyWidget::addChatPage(ChatLobbyDialog *d) ChatLobbyId id = d->id(); _lobby_infos[id].dialog = d ; _lobby_infos[id].default_icon = QIcon() ; - _lobby_infos[id].last_typing_event = time(NULL) ; + _lobby_infos[id].last_typing_event = time(nullptr) ; - ChatLobbyInfo linfo ; - if(rsMsgs->getChatLobbyInfo(id,linfo)) - _lobby_infos[id].default_icon = (linfo.lobby_flags & RS_CHAT_LOBBY_FLAGS_PUBLIC) ? QIcon(IMAGE_PUBLIC):QIcon(IMAGE_PRIVATE) ; - else - std::cerr << "(EE) cannot find info for room " << std::hex << id << std::dec << std::endl; + ChatLobbyInfo linfo ; + if(rsMsgs->getChatLobbyInfo(id,linfo)) + _lobby_infos[id].default_icon = (linfo.lobby_flags & RS_CHAT_LOBBY_FLAGS_PUBLIC) ? FilesDefs::getIconFromQtResourcePath(IMAGE_PUBLIC):FilesDefs::getIconFromQtResourcePath(IMAGE_PRIVATE) ; + else + std::cerr << "(EE) cannot find info for room " << std::hex << id << std::dec << std::endl; } + + ui.stackedWidget->addWidget(d) ; +} + +void ChatLobbyWidget::removeChatPage(ChatLobbyDialog *d) +{ + // check that the page already exist. + + if(_lobby_infos.find(d->id()) != _lobby_infos.end()) + { + ui.stackedWidget->removeWidget(d) ; + } +} + +void ChatLobbyWidget::dialogClose(ChatDialog* cd) +{ + ChatLobbyDialog* d = dynamic_cast(cd); + if(_lobby_infos.find(d->id()) != _lobby_infos.end()) + unsubscribeChatLobby(d->id()); } void ChatLobbyWidget::setCurrentChatPage(ChatLobbyDialog *d) @@ -594,7 +616,7 @@ void ChatLobbyWidget::updateDisplay() if (item == NULL) { item = new RSTreeWidgetItem(compareRole, TYPE_LOBBY); - icon = (lobby.lobby_flags & RS_CHAT_LOBBY_FLAGS_PUBLIC) ? QIcon(IMAGE_PUBLIC) : QIcon(IMAGE_PRIVATE); + icon = (lobby.lobby_flags & RS_CHAT_LOBBY_FLAGS_PUBLIC) ? FilesDefs::getIconFromQtResourcePath(IMAGE_PUBLIC) : FilesDefs::getIconFromQtResourcePath(IMAGE_PRIVATE); lobby_item->addChild(item); } @@ -602,7 +624,7 @@ void ChatLobbyWidget::updateDisplay() { if (item->data(COLUMN_DATA, ROLE_SUBSCRIBED).toBool() != subscribed) { // Replace icon - icon = (lobby.lobby_flags & RS_CHAT_LOBBY_FLAGS_PUBLIC) ? QIcon(IMAGE_PUBLIC) : QIcon(IMAGE_PRIVATE); + icon = (lobby.lobby_flags & RS_CHAT_LOBBY_FLAGS_PUBLIC) ? FilesDefs::getIconFromQtResourcePath(IMAGE_PUBLIC) : FilesDefs::getIconFromQtResourcePath(IMAGE_PRIVATE); } } if (!icon.isNull()) { @@ -662,12 +684,12 @@ void ChatLobbyWidget::updateDisplay() if (item == NULL) { item = new RSTreeWidgetItem(compareRole, TYPE_LOBBY); - icon = (lobby.lobby_flags & RS_CHAT_LOBBY_FLAGS_PUBLIC) ? QIcon(IMAGE_PUBLIC) : QIcon(IMAGE_PRIVATE); + icon = (lobby.lobby_flags & RS_CHAT_LOBBY_FLAGS_PUBLIC) ? FilesDefs::getIconFromQtResourcePath(IMAGE_PUBLIC) : FilesDefs::getIconFromQtResourcePath(IMAGE_PRIVATE); itemParent->addChild(item); } else { if (!item->data(COLUMN_DATA, ROLE_SUBSCRIBED).toBool()) { // Replace icon - icon = (lobby.lobby_flags & RS_CHAT_LOBBY_FLAGS_PUBLIC) ? QIcon(IMAGE_PUBLIC) : QIcon(IMAGE_PRIVATE); + icon = (lobby.lobby_flags & RS_CHAT_LOBBY_FLAGS_PUBLIC) ? FilesDefs::getIconFromQtResourcePath(IMAGE_PUBLIC) : FilesDefs::getIconFromQtResourcePath(IMAGE_PRIVATE); } } if (!icon.isNull()) { @@ -708,7 +730,7 @@ void ChatLobbyWidget::createChatLobby() void ChatLobbyWidget::showLobby(QTreeWidgetItem *item) { - if (item == NULL || item->type() != TYPE_LOBBY) { + if (item == nullptr || item->type() != TYPE_LOBBY) { showBlankPage(0) ; return; } @@ -718,7 +740,11 @@ void ChatLobbyWidget::showLobby(QTreeWidgetItem *item) if(_lobby_infos.find(id) == _lobby_infos.end()) showBlankPage(id) ; else - ui.stackedWidget->setCurrentWidget(_lobby_infos[id].dialog) ; + { + _lobby_infos[id].dialog->showDialog(RS_CHAT_FOCUS); + if (_lobby_infos[id].dialog->isWindowed()) + showBlankPage(id, true); + } } // this function is for the case where we don't have any identity yet @@ -772,17 +798,17 @@ bool ChatLobbyWidget::showLobbyAnchor(ChatLobbyId id, QString anchor) { QTreeWidgetItem *item = getTreeWidgetItem(id) ; - if(item != NULL) { + if(item != nullptr) { if(item->type() == TYPE_LOBBY) { if(_lobby_infos.find(id) == _lobby_infos.end()) { showBlankPage(id) ; } else { - //ChatLobbyDialog cldChatLobby =_lobby_infos[id].dialog; - ui.stackedWidget->setCurrentWidget(_lobby_infos[id].dialog) ; - ChatLobbyDialog *cldCW=NULL ; - if (NULL != (cldCW = dynamic_cast(ui.stackedWidget->currentWidget()))) - cldCW->getChatWidget()->scrollToAnchor(anchor); + _lobby_infos[id].dialog->showDialog(RS_CHAT_FOCUS); + if (_lobby_infos[id].dialog->isWindowed()) + showBlankPage(id, true); + + _lobby_infos[id].dialog->getChatWidget()->scrollToAnchor(anchor); } ui.lobbyTreeWidget->setCurrentItem(item); @@ -857,20 +883,20 @@ void ChatLobbyWidget::autoSubscribeLobby(QTreeWidgetItem *item) subscribeChatLobbyAtItem(item); } -void ChatLobbyWidget::showBlankPage(ChatLobbyId id) +void ChatLobbyWidget::showBlankPage(ChatLobbyId id, bool subscribed /*= false*/) { // show the default blank page. ui.stackedWidget->setCurrentWidget(ui._lobby_blank_page) ; // Update information std::vector lobbies; - rsMsgs->getListOfNearbyChatLobbies(lobbies); + rsMsgs->getListOfNearbyChatLobbies(lobbies); - std::list my_ids ; - rsIdentity->getOwnIds(my_ids) ; + std::list my_ids ; + rsIdentity->getOwnIds(my_ids) ; + + trimAnonIds(my_ids) ; - trimAnonIds(my_ids) ; - for(std::vector::const_iterator it(lobbies.begin());it!=lobbies.end();++it) if( (*it).lobby_id == id) { @@ -881,8 +907,10 @@ void ChatLobbyWidget::showBlankPage(ChatLobbyId id) ui.lobbysec_lineEdit->setText( (( (*it).lobby_flags & RS_CHAT_LOBBY_FLAGS_PGP_SIGNED)?tr("No anonymous IDs"):tr("Anonymous IDs accepted")) ); ui.lobbypeers_lineEdit->setText( QString::number((*it).total_number_of_peers) ); - QString text = tr("You're not subscribed to this chat room; Double click-it to enter and chat.") ; - + QString text = tr("You're subscribed to this chat room; Double click to show window and chat.") ; + if (!subscribed) + { + text = tr("You're not subscribed to this chat room; Double click-it to enter and chat.") ; if(my_ids.empty()) { if( (*it).lobby_flags & RS_CHAT_LOBBY_FLAGS_PGP_SIGNED) @@ -890,8 +918,9 @@ void ChatLobbyWidget::showBlankPage(ChatLobbyId id) else text += "\n\n"+tr("You will need to create an identity in order to join chat rooms.") ; } + } - ui.lobbyInfoLabel->setText(text); + ui.lobbyInfoLabel->setText(text); return ; } @@ -968,7 +997,7 @@ void ChatLobbyWidget::updateTypingStatus(ChatLobbyId id) if(item != NULL) { - item->setIcon(COLUMN_NAME,QIcon(IMAGE_TYPING)) ; + item->setIcon(COLUMN_NAME,FilesDefs::getIconFromQtResourcePath(IMAGE_TYPING)) ; _lobby_infos[id].last_typing_event = time(NULL) ; QTimer::singleShot(5000,this,SLOT(resetLobbyTreeIcons())) ; @@ -980,7 +1009,7 @@ void ChatLobbyWidget::updatePeerLeaving(ChatLobbyId id) if(item != NULL) { - item->setIcon(COLUMN_NAME,QIcon(IMAGE_PEER_LEAVING)) ; + item->setIcon(COLUMN_NAME,FilesDefs::getIconFromQtResourcePath(IMAGE_PEER_LEAVING)) ; _lobby_infos[id].last_typing_event = time(NULL) ; QTimer::singleShot(5000,this,SLOT(resetLobbyTreeIcons())) ; @@ -992,7 +1021,7 @@ void ChatLobbyWidget::updatePeerEntering(ChatLobbyId id) if(item != NULL) { - item->setIcon(COLUMN_NAME,QIcon(IMAGE_PEER_ENTERING)) ; + item->setIcon(COLUMN_NAME,FilesDefs::getIconFromQtResourcePath(IMAGE_PEER_ENTERING)) ; _lobby_infos[id].last_typing_event = time(NULL) ; QTimer::singleShot(5000,this,SLOT(resetLobbyTreeIcons())) ; @@ -1037,6 +1066,8 @@ void ChatLobbyWidget::unsubscribeChatLobby(ChatLobbyId id) } ui.stackedWidget->removeWidget(it->second.dialog) ; + disconnect(it->second.dialog,SIGNAL(dialogClose(ChatDialog*)),this,SLOT(dialogClose(ChatDialog*))); + it->second.dialog->leaveLobby(); _lobby_infos.erase(it) ; } @@ -1075,7 +1106,7 @@ void ChatLobbyWidget::updateCurrentLobby() if(_lobby_infos.find(id) != _lobby_infos.end()) { int iPrivacyLevel= item->parent()->data(COLUMN_DATA, ROLE_PRIVACYLEVEL).toInt(); - QIcon icon = (iPrivacyLevel==CHAT_LOBBY_PRIVACY_LEVEL_PUBLIC) ? QIcon(IMAGE_PUBLIC) : QIcon(IMAGE_PRIVATE); + QIcon icon = (iPrivacyLevel==CHAT_LOBBY_PRIVACY_LEVEL_PUBLIC) ? FilesDefs::getIconFromQtResourcePath(IMAGE_PUBLIC) : FilesDefs::getIconFromQtResourcePath(IMAGE_PRIVATE); _lobby_infos[id].default_icon = icon ; item->setIcon(COLUMN_NAME, icon) ; } @@ -1099,7 +1130,7 @@ void ChatLobbyWidget::updateMessageChanged(bool incoming, ChatLobbyId id, QDateT if(bIsCurrentItem) return ; - _lobby_infos[id].default_icon = QIcon(IMAGE_MESSAGE) ; + _lobby_infos[id].default_icon = FilesDefs::getIconFromQtResourcePath(IMAGE_MESSAGE) ; QTreeWidgetItem *item = getTreeWidgetItem(id) ; diff --git a/retroshare-gui/src/gui/ChatLobbyWidget.h b/retroshare-gui/src/gui/ChatLobbyWidget.h index 8c6f07081..a9bb01895 100644 --- a/retroshare-gui/src/gui/ChatLobbyWidget.h +++ b/retroshare-gui/src/gui/ChatLobbyWidget.h @@ -19,9 +19,10 @@ *******************************************************************************/ #pragma once +#include + #include "ui_ChatLobbyWidget.h" -#include "RsAutoUpdatePage.h" #include "chat/ChatLobbyUserNotify.h" #include "gui/gxs/GxsIdChooser.h" @@ -38,6 +39,7 @@ class RSTreeWidgetItemCompareRole; class ChatTabWidget ; +class ChatDialog ; class ChatLobbyDialog ; class QTextBrowser ; @@ -69,6 +71,7 @@ public: void setCurrentChatPage(ChatLobbyDialog *) ; // used by ChatLobbyDialog to raise. void addChatPage(ChatLobbyDialog *) ; + void removeChatPage(ChatLobbyDialog *) ; bool showLobbyAnchor(ChatLobbyId id, QString anchor) ; uint unreadCount(); @@ -77,6 +80,7 @@ signals: void unreadCountChanged(uint unreadCount); protected slots: + void dialogClose(ChatDialog*); void lobbyChanged(); void lobbyTreeWidgetCustomPopupMenu(QPoint); void createChatLobby(); @@ -87,7 +91,7 @@ protected slots: void displayChatLobbyEvent(qulonglong lobby_id, int event_type, const RsGxsId& gxs_id, const QString& str); void readChatLobbyInvites(); void showLobby(QTreeWidgetItem *lobby_item) ; - void showBlankPage(ChatLobbyId id) ; + void showBlankPage(ChatLobbyId id, bool subscribed = false) ; void unsubscribeChatLobby(ChatLobbyId id) ; void createIdentityAndSubscribe(); void subscribeChatLobbyAs() ; diff --git a/retroshare-gui/src/gui/Circles/CirclesDialog.cpp b/retroshare-gui/src/gui/Circles/CirclesDialog.cpp index 64dff16df..41fd4e9f2 100644 --- a/retroshare-gui/src/gui/Circles/CirclesDialog.cpp +++ b/retroshare-gui/src/gui/Circles/CirclesDialog.cpp @@ -50,7 +50,7 @@ /** Constructor */ CirclesDialog::CirclesDialog(QWidget *parent) - : RsGxsUpdateBroadcastPage(rsGxsCircles, parent) + : MainPage(parent) { /* Invoke the Qt Designer generated object setup routine */ ui.setupUi(this); diff --git a/retroshare-gui/src/gui/Circles/CirclesDialog.h b/retroshare-gui/src/gui/Circles/CirclesDialog.h index ecaff3f29..ac87f9f61 100644 --- a/retroshare-gui/src/gui/Circles/CirclesDialog.h +++ b/retroshare-gui/src/gui/Circles/CirclesDialog.h @@ -27,11 +27,11 @@ #include "util/TokenQueue.h" #include "ui_CirclesDialog.h" -#define IMAGE_CIRCLES ":/images/circles/circles_32.png" +#define IMAGE_CIRCLES ":/icons/png/circles.png" class UIStateHelper; -class CirclesDialog : public RsGxsUpdateBroadcastPage, public TokenResponse +class CirclesDialog : public MainPage, public TokenResponse { Q_OBJECT diff --git a/retroshare-gui/src/gui/Circles/CreateCircleDialog.cpp b/retroshare-gui/src/gui/Circles/CreateCircleDialog.cpp index 8808f17a3..40340605b 100644 --- a/retroshare-gui/src/gui/Circles/CreateCircleDialog.cpp +++ b/retroshare-gui/src/gui/Circles/CreateCircleDialog.cpp @@ -24,11 +24,14 @@ #include #include +#include #include #include #include "gui/common/AvatarDefs.h" +#include "gui/common/FilesDefs.h" +#include "util/qtthreadsutils.h" #include "gui/Circles/CreateCircleDialog.h" #include "gui/gxs/GxsIdDetails.h" #include "gui/Identity/IdDialog.h" @@ -40,8 +43,8 @@ #define CREATECIRCLEDIALOG_IDINFO 3 #define RSCIRCLEID_COL_NICKNAME 0 -#define RSCIRCLEID_COL_KEYID 1 -#define RSCIRCLEID_COL_IDTYPE 2 +#define RSCIRCLEID_COL_IDTYPE 1 +#define RSCIRCLEID_COL_KEYID 2 /** Constructor */ CreateCircleDialog::CreateCircleDialog() @@ -51,10 +54,7 @@ CreateCircleDialog::CreateCircleDialog() ui.setupUi(this); /* Setup Queue */ - mCircleQueue = new TokenQueue(rsGxsCircles->getTokenService(), this); - mIdQueue = new TokenQueue(rsIdentity->getTokenService(), this); - - ui.headerFrame->setHeaderImage(QPixmap(":/icons/png/circles.png")); + ui.headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/circles.png")); // connect up the buttons. connect(ui.addButton, SIGNAL(clicked()), this, SLOT(addMember())); @@ -80,13 +80,18 @@ CreateCircleDialog::CreateCircleDialog() headerText = headerItem->text(RSCIRCLEID_COL_KEYID); ui.IdFilter->addFilter(QIcon(), headerText, RSCIRCLEID_COL_KEYID, QString("%1 %2").arg(tr("Search"), headerText)); + /* Set initial column width */ + int fontWidth = QFontMetricsF(ui.treeWidget_IdList->font()).width("W"); + ui.treeWidget_IdList->setColumnWidth(RSCIRCLEID_COL_NICKNAME, 17 * fontWidth); + ui.treeWidget_membership->setColumnWidth(RSCIRCLEID_COL_NICKNAME, 17 * fontWidth); + ui.removeButton->setEnabled(false); ui.addButton->setEnabled(false); ui.radioButton_ListAll->setChecked(true); QObject::connect(ui.radioButton_ListAll, SIGNAL(toggled(bool)), this, SLOT(idTypeChanged())) ; QObject::connect(ui.radioButton_ListAllPGP, SIGNAL(toggled(bool)), this, SLOT(idTypeChanged())) ; - QObject::connect(ui.radioButton_ListKnownPGP, SIGNAL(toggled(bool)), this, SLOT(idTypeChanged())) ; + QObject::connect(ui.radioButton_ListFriendPGP, SIGNAL(toggled(bool)), this, SLOT(idTypeChanged())) ; QObject::connect(ui.radioButton_Public, SIGNAL(toggled(bool)), this, SLOT(updateCircleType(bool))) ; QObject::connect(ui.radioButton_Self, SIGNAL(toggled(bool)), this, SLOT(updateCircleType(bool))) ; @@ -98,17 +103,17 @@ CreateCircleDialog::CreateCircleDialog() mIsExternalCircle = true; mClearList = true; #if QT_VERSION >= 0x040700 - ui.circleName->setPlaceholderText(QApplication::translate("CreateCircleDialog", "Circle name", 0)); + ui.circleName->setPlaceholderText(QApplication::translate("CreateCircleDialog", "Circle name", 0)); #endif + ui.treeWidget_IdList->setColumnHidden(RSCIRCLEID_COL_KEYID,true); // no need to show this. the tooltip will do it. + //ui.idChooser->loadIds(0,RsGxsId()); ui.circleComboBox->loadCircles(RsGxsCircleId()); } CreateCircleDialog::~CreateCircleDialog() { - delete(mCircleQueue); - delete(mIdQueue); } void CreateCircleDialog::editExistingId(const RsGxsGroupId &circleId, const bool &clearList /*= true*/,bool readonly) @@ -147,14 +152,15 @@ void CreateCircleDialog::editExistingId(const RsGxsGroupId &circleId, const bool ui.removeButton->setEnabled(!readonly) ; if(readonly) - { - ui.buttonBox->setStandardButtons(QDialogButtonBox::Cancel); - ui.buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Close")); - ui.peersSelection_GB->hide() ; - ui.addButton->hide() ; - ui.removeButton->hide() ; - } - requestCircle(circleId); + { + ui.buttonBox->setStandardButtons(QDialogButtonBox::Cancel); + ui.buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Close")); + ui.peersSelection_GB->hide() ; + ui.addButton->hide() ; + ui.removeButton->hide() ; + } + + loadCircle(circleId); } @@ -233,7 +239,7 @@ void CreateCircleDialog::setupForExternalCircle() ui.idChooser->show(); //ui.toolButton_NewId->show(); - requestGxsIdentities(); + loadIdentities(); } void CreateCircleDialog::selectedId(QTreeWidgetItem *current, QTreeWidgetItem *previous) @@ -266,7 +272,7 @@ void CreateCircleDialog::addMember(const RsGxsIdGroup &idGroup) { QString keyId = QString::fromStdString(idGroup.mMeta.mGroupId.toStdString()); QString nickname = QString::fromUtf8(idGroup.mMeta.mGroupName.c_str()); - QString idtype = tr("Anon Id"); + QString idtype = tr("[Anonymous Id]"); QPixmap pixmap ; @@ -327,7 +333,7 @@ void CreateCircleDialog::addCircle(const RsGxsCircleDetails &cirDetails) QString keyId = QString::fromStdString(gxs_id.toStdString()); QString nickname = QString::fromUtf8(gxs_details.mNickname.c_str()); - QString idtype = tr("Anon Id"); + QString idtype = tr("[Anonymous Id]"); QPixmap pixmap ; @@ -336,8 +342,8 @@ void CreateCircleDialog::addCircle(const RsGxsCircleDetails &cirDetails) addMember(keyId, idtype, nickname, QIcon(pixmap)); - }//if(!gxs_id.isNull() && rsIdentity->getIdDetails(gxs_id,gxs_details)) - }//for (itUnknownPeers it = cirDetails.mUnknownPeers.begin() + } + } typedef std::set::const_iterator itAllowedPeers; for (itAllowedPeers it = cirDetails.mAllowedNodes.begin() ; it != cirDetails.mAllowedNodes.end() ; ++it ) @@ -355,8 +361,8 @@ void CreateCircleDialog::addCircle(const RsGxsCircleDetails &cirDetails) addMember(keyId, idtype, nickname, QIcon(avatar)); - }//if(!gpg_id.isNull() && rsPeers->getGPGDetails(gpg_id,details)) - }//for (itAllowedPeers it = cirDetails.mAllowedPeers.begin() + } + } } void CreateCircleDialog::removeMember() @@ -388,7 +394,7 @@ void CreateCircleDialog::createCircle() QMessageBox::warning(this, tr("RetroShare"),tr("Please set a name for your Circle"), QMessageBox::Ok, QMessageBox::Ok); return; //Don't add a empty Subject!! - }//if(name.isEmpty()) + } RsGxsCircleGroup circle = mCircleGroup; // init with loaded group @@ -462,16 +468,6 @@ void CreateCircleDialog::createCircle() } - // if (mIsExistingCircle) - // { - // std::cerr << "CreateCircleDialog::createCircle() Existing Circle TODO"; - // std::cerr << std::endl; - // - // // cannot edit these yet. - // QMessageBox::warning(this, tr("RetroShare"),tr("Cannot Edit Existing Circles Yet"), QMessageBox::Ok, QMessageBox::Ok); - // return; - // } - if (mIsExternalCircle) { #ifdef DEBUG_CREATE_CIRCLE_DIALOG @@ -663,107 +659,77 @@ void CreateCircleDialog::updateCircleGUI() } } -void CreateCircleDialog::requestCircle(const RsGxsGroupId &groupId) +void CreateCircleDialog::loadCircle(const RsGxsGroupId& groupId) { - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; - - std::list groupIds; - groupIds.push_back(groupId); - -#ifdef DEBUG_CREATE_CIRCLE_DIALOG - std::cerr << "CreateCircleDialog::requestCircle() Requesting Group Summary(" << groupId << ")"; - std::cerr << std::endl; -#endif - - uint32_t token; - mCircleQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, groupIds, CREATECIRCLEDIALOG_CIRCLEINFO); -} - -void CreateCircleDialog::loadCircle(uint32_t token) -{ -#ifdef DEBUG_CREATE_CIRCLE_DIALOG - std::cerr << "CreateCircleDialog::loadCircle(" << token << ")"; - std::cerr << std::endl; -#endif - QTreeWidget *tree = ui.treeWidget_membership; - if (mClearList) tree->clear(); - std::vector groups; - if (!rsGxsCircles->getGroupData(token, groups)) { - std::cerr << "CreateCircleDialog::loadCircle() Error getting GroupData"; - std::cerr << std::endl; - return; - } + RsThread::async([groupId,this]() + { + std::vector circlesInfo ; - if (groups.size() != 1) { - std::cerr << "CreateCircleDialog::loadCircle() Error Group.size() != 1"; - std::cerr << std::endl; - return; - } - - mCircleGroup = groups[0]; - -#ifdef DEBUG_CREATE_CIRCLE_DIALOG - std::cerr << "CreateCircleDialog::loadCircle() LoadedGroup.meta: " << mCircleGroup.mMeta << std::endl; + if(! rsGxsCircles->getCirclesInfo(std::list({ groupId }), circlesInfo) || circlesInfo.size() != 1) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve circle info for circle " << groupId << std::endl; + return; + } + + RsGxsCircleGroup grp(circlesInfo[0]); + + RsQThreadUtils::postToObject( [grp,this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ + + mCircleGroup = grp; + +#ifdef DEBUG_CREATE_CIRCLE_DIALOG + std::cerr << "CreateCircleDialog::loadCircle() LoadedGroup.meta: " << mCircleGroup.mMeta << std::endl; #endif - updateCircleGUI(); + updateCircleGUI(); + + }, this ); + }); + } -/*void CreateCircleDialog::getPgpIdentities() +void CreateCircleDialog::loadIdentities() { - std::cerr << "CreateCircleDialog::getPgpIdentities()"; - std::cerr << std::endl; + RsThread::async([this]() + { + std::list ids_meta; - QTreeWidget *tree = ui.treeWidget_IdList; + if(!rsIdentity->getIdentitiesSummaries(ids_meta)) + { + RS_ERR("failed to retrieve identities ids for all identities"); + return; + } - tree->clear(); - std::list ids; - std::list::iterator it; + std::set ids; + for(auto& meta:ids_meta) ids.insert(RsGxsId(meta.mGroupId)); - rsPeers->getGPGAcceptedList(ids); - for(it = ids.begin(); it != ids.end(); ++it) { - RsPeerDetails details; + auto id_groups = std::make_unique>(); + if(!rsIdentity->getIdentitiesInfo(ids, *id_groups)) + { + RS_ERR("failed to retrieve identities group info for all identities"); + return; + } - rsPeers->getGPGDetails(*it, details); + RsQThreadUtils::postToObject( + [id_groups = std::move(id_groups), this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ - QString keyId = QString::fromStdString(details.gpg_id.toStdString()); - QString nickname = QString::fromUtf8(details.name.c_str()); - QString idtype = tr("PGP Identity"); + fillIdentitiesList(*id_groups); + }, this ); + }); - QTreeWidgetItem *item = new QTreeWidgetItem(); - item->setText(RSCIRCLEID_COL_NICKNAME, nickname); - item->setText(RSCIRCLEID_COL_KEYID, keyId); - item->setText(RSCIRCLEID_COL_IDTYPE, idtype); - tree->addTopLevelItem(item); - - // Local Circle. - if (mIsExistingCircle) - if ( mCircleGroup.mLocalFriends.find(details.gpg_id) != mCircleGroup.mLocalFriends.end()) // check if its in the circle. - addMember(keyId, idtype, nickname); - } - - filterIds(); -}*/ - - -void CreateCircleDialog::requestGxsIdentities() -{ - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; - -#ifdef DEBUG_CREATE_CIRCLE_DIALOG - std::cerr << "CreateCircleDialog::requestIdentities()"; - std::cerr << std::endl; -#endif - - uint32_t token; - mIdQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, CREATECIRCLEDIALOG_IDINFO); } -void CreateCircleDialog::loadIdentities(uint32_t token) +void CreateCircleDialog::fillIdentitiesList(const std::vector& id_groups) { #ifdef DEBUG_CREATE_CIRCLE_DIALOG std::cerr << "CreateCircleDialog::loadIdentities(" << token << ")"; @@ -771,36 +737,22 @@ void CreateCircleDialog::loadIdentities(uint32_t token) #endif QTreeWidget *tree = ui.treeWidget_IdList; - tree->clear(); - bool acceptAnonymous = ui.radioButton_ListAll->isChecked(); - bool acceptAllPGP = ui.radioButton_ListAllPGP->isChecked(); - //bool acceptKnownPGP = ui.radioButton_ListKnownPGP->isChecked(); + bool acceptAll = ui.radioButton_ListAll->isChecked(); + bool acceptOnlySignedIdentities = ui.radioButton_ListAllPGP->isChecked(); + bool acceptOnlyIdentitiesSignedByFriend = ui.radioButton_ListFriendPGP->isChecked(); - RsGxsIdGroup idGroup; - std::vector datavector; - std::vector::iterator vit; - if (!rsIdentity->getGroupData(token, datavector)) { - std::cerr << "CreateCircleDialog::insertIdentities() Error getting GroupData"; - std::cerr << std::endl; - return; - } - - for(vit = datavector.begin(); vit != datavector.end(); ++vit) + for(const auto& idGroup:id_groups) { - idGroup = (*vit); + bool isSigned = !idGroup.mPgpId.isNull(); + bool isSignedByFriendNode = isSigned && rsPeers->isPgpFriend(idGroup.mPgpId); /* do filtering */ bool ok = false; - if (acceptAnonymous) - ok = true; - else if (acceptAllPGP) - ok = idGroup.mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID_kept_for_compatibility ; - else if (idGroup.mPgpKnown) - ok = idGroup.mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID_kept_for_compatibility ; - if (!ok) { + if(!(acceptAll ||(acceptOnlySignedIdentities && isSigned) ||(acceptOnlyIdentitiesSignedByFriend && isSignedByFriendNode))) + { #ifdef DEBUG_CREATE_CIRCLE_DIALOG std::cerr << "CreateCircleDialog::insertIdentities() Skipping ID: " << data.mMeta.mGroupId; std::cerr << std::endl; @@ -810,23 +762,21 @@ void CreateCircleDialog::loadIdentities(uint32_t token) QString keyId = QString::fromStdString(idGroup.mMeta.mGroupId.toStdString()); QString nickname = QString::fromUtf8(idGroup.mMeta.mGroupName.c_str()); - QString idtype = tr("Anon Id"); + QString idtype ; QPixmap pixmap ; if(idGroup.mImage.mSize == 0 || !GxsIdDetails::loadPixmapFromData(idGroup.mImage.mData, idGroup.mImage.mSize, pixmap, GxsIdDetails::SMALL)) pixmap = GxsIdDetails::makeDefaultIcon(RsGxsId(idGroup.mMeta.mGroupId),GxsIdDetails::SMALL) ; - if (idGroup.mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID_kept_for_compatibility) + if (!idGroup.mPgpId.isNull()) { - if (idGroup.mPgpKnown) { - RsPeerDetails details; - rsPeers->getGPGDetails(idGroup.mPgpId, details); - idtype = QString::fromUtf8(details.name.c_str()); - } - else - idtype = tr("PGP Linked Id"); + RsPeerDetails details; + if(rsPeers->getGPGDetails(idGroup.mPgpId, details)) + idtype = QString::fromUtf8(details.name.c_str()); + else + idtype = tr("[Unknown]"); } QTreeWidgetItem *item = new QTreeWidgetItem(); @@ -836,6 +786,11 @@ void CreateCircleDialog::loadIdentities(uint32_t token) item->setText(RSCIRCLEID_COL_IDTYPE, idtype); tree->addTopLevelItem(item); + RsIdentityDetails det; + + if(rsIdentity->getIdDetails(RsGxsId(idGroup.mMeta.mGroupId),det)) + item->setToolTip(RSCIRCLEID_COL_NICKNAME,GxsIdDetails::getComment(det)); + // External Circle. if (mIsExistingCircle) { @@ -851,43 +806,9 @@ void CreateCircleDialog::loadIdentities(uint32_t token) } } -void CreateCircleDialog::loadRequest(const TokenQueue *queue, const TokenRequest &req) -{ -#ifdef DEBUG_CREATE_CIRCLE_DIALOG - std::cerr << "CreateCircleDialog::loadRequest() UserType: " << req.mUserType; - std::cerr << std::endl; -#endif - - if (queue == mCircleQueue) { - /* now switch on req */ - switch(req.mUserType) { - case CREATECIRCLEDIALOG_CIRCLEINFO: - loadCircle(req.mToken); - break; - - default: - std::cerr << "CreateCircleDialog::loadRequest() UNKNOWN UserType "; - std::cerr << std::endl; - } - } - - if (queue == mIdQueue) { - /* now switch on req */ - switch(req.mUserType) { - case CREATECIRCLEDIALOG_IDINFO: - loadIdentities(req.mToken); - break; - - default: - std::cerr << "CreateCircleDialog::loadRequest() UNKNOWN UserType "; - std::cerr << std::endl; - } - } -} - void CreateCircleDialog::idTypeChanged() { - requestGxsIdentities(); + loadIdentities(); } void CreateCircleDialog::filterChanged(const QString &text) { diff --git a/retroshare-gui/src/gui/Circles/CreateCircleDialog.h b/retroshare-gui/src/gui/Circles/CreateCircleDialog.h index dd5e52494..8ced0ecdc 100644 --- a/retroshare-gui/src/gui/Circles/CreateCircleDialog.h +++ b/retroshare-gui/src/gui/Circles/CreateCircleDialog.h @@ -25,12 +25,10 @@ #include "ui_CreateCircleDialog.h" -#include "util/TokenQueue.h" - #include #include -class CreateCircleDialog : public QDialog, public TokenResponse +class CreateCircleDialog : public QDialog { Q_OBJECT @@ -45,8 +43,6 @@ public: void addMember(const RsGxsIdGroup &idGroup); void addCircle(const RsGxsCircleDetails &cirDetails); - virtual void loadRequest(const TokenQueue *queue, const TokenRequest &req); - private slots: void addMember(); void removeMember(); @@ -75,17 +71,11 @@ private: bool mIsExternalCircle; bool mReadOnly; - void loadCircle(uint32_t token); - void loadIdentities(uint32_t token); - - void requestCircle(const RsGxsGroupId &groupId); - void requestGxsIdentities(); - //void getPgpIdentities(); + void loadCircle(const RsGxsGroupId& groupId); + void loadIdentities(); void filterIds(); - - TokenQueue *mCircleQueue; - TokenQueue *mIdQueue; + void fillIdentitiesList(const std::vector& id_groups); RsGxsCircleGroup mCircleGroup; // for editting existing Circles. bool mClearList; diff --git a/retroshare-gui/src/gui/Circles/CreateCircleDialog.ui b/retroshare-gui/src/gui/Circles/CreateCircleDialog.ui index 8e3b5ed2e..61923d757 100644 --- a/retroshare-gui/src/gui/Circles/CreateCircleDialog.ui +++ b/retroshare-gui/src/gui/Circles/CreateCircleDialog.ui @@ -6,8 +6,8 @@ 0 0 - 600 - 500 + 721 + 561 @@ -106,8 +106,8 @@ - - :/images/back.png:/images/back.png + + :/icons/png/arrow-left.png:/icons/png/arrow-left.png @@ -120,8 +120,8 @@ - - :/images/forward.png:/images/forward.png + + :/icons/png/arrow-right.png:/icons/png/arrow-right.png @@ -161,12 +161,7 @@ - ID - - - - - Type + Profile @@ -210,9 +205,9 @@ - + - Signed by known nodes + Signed by friend node @@ -411,7 +406,7 @@ <html><head/><body><p>Circles can be restricted to the members of another circle. Only the members of that second circle will be allowed to see the new circle and its content (list of members, etc).</p></body></html> - Only visible to members of: + Only &visible to members of: @@ -455,6 +450,11 @@ + + GxsIdChooser + QComboBox +

gui/gxs/GxsIdChooser.h
+ LineEditClear QLineEdit @@ -471,11 +471,6 @@
gui/common/HeaderFrame.h
1
- - GxsIdChooser - QComboBox -
gui/gxs/GxsIdChooser.h
-
GxsCircleChooser QComboBox diff --git a/retroshare-gui/src/gui/FileTransfer/BannedFilesDialog.h b/retroshare-gui/src/gui/FileTransfer/BannedFilesDialog.h index 28b8e8df1..a65dac238 100644 --- a/retroshare-gui/src/gui/FileTransfer/BannedFilesDialog.h +++ b/retroshare-gui/src/gui/FileTransfer/BannedFilesDialog.h @@ -20,7 +20,7 @@ #pragma once -#include "RsAutoUpdatePage.h" +#include #include "ui_BannedFilesDialog.h" class BannedFilesDialog: public QDialog diff --git a/retroshare-gui/src/gui/FileTransfer/FileTransferInfoWidget.cpp b/retroshare-gui/src/gui/FileTransfer/FileTransferInfoWidget.cpp index e7aef654b..6adb61a68 100644 --- a/retroshare-gui/src/gui/FileTransfer/FileTransferInfoWidget.cpp +++ b/retroshare-gui/src/gui/FileTransfer/FileTransferInfoWidget.cpp @@ -23,11 +23,12 @@ #include #include #include -#include -#include +#include "retroshare/rsfiles.h" +#include "retroshare/rstypes.h" #include "util/misc.h" #include "FileTransferInfoWidget.h" -#include +#include "gui/RetroShareLink.h" +#include "gui/common/FilesDefs.h" // Variables to decide of display behaviour. All variables are expressed as a factor of font height // @@ -49,10 +50,10 @@ FileTransferInfoWidget::FileTransferInfoWidget(QWidget * /*parent*/, Qt::WindowF int S = 0.9*QFontMetricsF(font()).height(); - downloadedPixmap = QPixmap(":/icons/tile_downloaded_48.png").scaledToHeight(S,Qt::SmoothTransformation); - downloadingPixmap = QPixmap(":/icons/tile_downloading_48.png").scaledToHeight(S,Qt::SmoothTransformation); - notDownloadPixmap = QPixmap(":/icons/tile_inactive_48.png").scaledToHeight(S,Qt::SmoothTransformation); - checkingPixmap = QPixmap(":/icons/tile_checking_48.png").scaledToHeight(S,Qt::SmoothTransformation); + downloadedPixmap = FilesDefs::getPixmapFromQtResourcePath(":/icons/tile_downloaded_48.png").scaledToHeight(S,Qt::SmoothTransformation); + downloadingPixmap = FilesDefs::getPixmapFromQtResourcePath(":/icons/tile_downloading_48.png").scaledToHeight(S,Qt::SmoothTransformation); + notDownloadPixmap = FilesDefs::getPixmapFromQtResourcePath(":/icons/tile_inactive_48.png").scaledToHeight(S,Qt::SmoothTransformation); + checkingPixmap = FilesDefs::getPixmapFromQtResourcePath(":/icons/tile_checking_48.png").scaledToHeight(S,Qt::SmoothTransformation); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); } diff --git a/retroshare-gui/src/gui/FileTransfer/FileTransferInfoWidget.h b/retroshare-gui/src/gui/FileTransfer/FileTransferInfoWidget.h index a82f08dbc..fdf45f2ff 100644 --- a/retroshare-gui/src/gui/FileTransfer/FileTransferInfoWidget.h +++ b/retroshare-gui/src/gui/FileTransfer/FileTransferInfoWidget.h @@ -24,7 +24,8 @@ #include #include #include -#include "RsAutoUpdatePage.h" + +#include #include struct FileChunksInfo ; diff --git a/retroshare-gui/src/gui/FileTransfer/SearchDialog.cpp b/retroshare-gui/src/gui/FileTransfer/SearchDialog.cpp index eba42e4ff..61c4bb5c2 100644 --- a/retroshare-gui/src/gui/FileTransfer/SearchDialog.cpp +++ b/retroshare-gui/src/gui/FileTransfer/SearchDialog.cpp @@ -43,16 +43,17 @@ #include /* Images for context menu icons */ -#define IMAGE_START ":/images/download.png" -#define IMAGE_REMOVE ":/images/delete.png" +#define IMAGE_START ":/icons/png/download.png" +#define IMAGE_SEARCHAGAIN ":/images/update.png" +#define IMAGE_REMOVE ":/images/delete.png" #define IMAGE_REMOVEALL ":/images/deleteall.png" #define IMAGE_DIRECTORY ":/images/folder16.png" #define IMAGE_OPENFOLDER ":/images/folderopen.png" -#define IMAGE_LIBRARY ":/images/library.png" -#define IMAGE_COLLCREATE ":/images/library_add.png" -#define IMAGE_COLLMODIF ":/images/library_edit.png" -#define IMAGE_COLLVIEW ":/images/library_view.png" -#define IMAGE_COLLOPEN ":/images/library.png" +#define IMAGE_LIBRARY ":/icons/collections.png" +#define IMAGE_COLLCREATE ":/iconss/png/add.png" +#define IMAGE_COLLMODIF ":/icons/png/pencil-edit-button.png" +#define IMAGE_COLLVIEW ":/images/find.png" +#define IMAGE_COLLOPEN ":/icons/collections.png" #define IMAGE_COPYLINK ":/images/copyrslink.png" #define IMAGE_BANFILE ":/icons/biohazard_red.png" @@ -415,9 +416,8 @@ void SearchDialog::download() std::cout << *it << "-" << std::endl; QColor foreground = textColorDownloading(); - QBrush brush(foreground); for (int i = 0; i < item->columnCount(); ++i) - item->setForeground(i, brush); + item->setData(i, Qt::ForegroundRole, foreground ); } } } @@ -671,14 +671,14 @@ void SearchDialog::searchSummaryWidgetCustomPopupMenu( QPoint /*point*/ ) QMenu contextMnu(this); QTreeWidgetItem* ci = ui.searchSummaryWidget->currentItem(); - QAction* action = contextMnu.addAction(tr("Search again"), this, SLOT(searchAgain())); + QAction* action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_SEARCHAGAIN),tr("Search again"), this, SLOT(searchAgain())); if (!ci || ci->data(SS_DATA_COL, ROLE_KEYWORDS).toString().isEmpty()) { action->setDisabled(true); } - contextMnu.addAction(QIcon(IMAGE_REMOVE), tr("Remove"), this, SLOT(searchRemove())); - contextMnu.addAction(QIcon(IMAGE_REMOVE), tr("Remove All"), this, SLOT(searchRemoveAll())); + contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_REMOVE), tr("Remove"), this, SLOT(searchRemove())); + contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_REMOVEALL), tr("Remove All"), this, SLOT(searchRemoveAll())); contextMnu.addSeparator(); - action = contextMnu.addAction(QIcon(IMAGE_COPYLINK), tr("Copy RetroShare Link"), this, SLOT(copySearchLink())); + action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COPYLINK), tr("Copy RetroShare Link"), this, SLOT(copySearchLink())); if (!ci || ci->data(SS_DATA_COL, ROLE_KEYWORDS).toString().isEmpty()) { action->setDisabled(true); } @@ -1269,10 +1269,9 @@ void SearchDialog::insertFile(qulonglong searchId, const FileDetail& file, int s foreground = textColorHighSources(); } - QBrush brush(foreground); for (int i = 0; i < item->columnCount(); ++i) { - item->setForeground(i, brush); + item->setData(i, Qt::ForegroundRole, foreground); } } @@ -1355,10 +1354,9 @@ void SearchDialog::insertFile(qulonglong searchId, const FileDetail& file, int s } if (setForeground) { - QBrush brush(foreground); for (int i = 0; i < item->columnCount(); ++i) { - item->setForeground(i, brush); + item->setData(i, Qt::ForegroundRole, foreground); } } @@ -1470,7 +1468,7 @@ void SearchDialog::hideOrShowSearchResult(QTreeWidgetItem* resultItem, QString c void SearchDialog::setIconAndType(QTreeWidgetItem *item, const QString& filename) { - item->setIcon(SR_NAME_COL, FilesDefs::getIconFromFilename(filename)); + item->setIcon(SR_NAME_COL, FilesDefs::getIconFromFileType(filename)); item->setText(SR_TYPE_COL, FilesDefs::getNameFromFilename(filename)); } diff --git a/retroshare-gui/src/gui/FileTransfer/SearchDialog.h b/retroshare-gui/src/gui/FileTransfer/SearchDialog.h index c911b8084..f14bfb642 100644 --- a/retroshare-gui/src/gui/FileTransfer/SearchDialog.h +++ b/retroshare-gui/src/gui/FileTransfer/SearchDialog.h @@ -23,7 +23,7 @@ #include #include "ui_SearchDialog.h" -#include "mainpage.h" +#include class AdvancedSearchDialog; class RSTreeWidgetItemCompareRole; diff --git a/retroshare-gui/src/gui/FileTransfer/SearchDialog.ui b/retroshare-gui/src/gui/FileTransfer/SearchDialog.ui index 47e221bf5..66992944d 100644 --- a/retroshare-gui/src/gui/FileTransfer/SearchDialog.ui +++ b/retroshare-gui/src/gui/FileTransfer/SearchDialog.ui @@ -6,8 +6,8 @@ 0 0 - 1531 - 889 + 793 + 511 @@ -26,28 +26,28 @@ - QFrame::Box + QFrame::NoFrame - QFrame::Sunken + QFrame::Plain - 2 + 3 - 2 + 3 - 2 + 3 - 2 + 3 - 0 + 2 @@ -414,7 +414,7 @@ Directory - + :/images/folder16.png:/images/folder16.png diff --git a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp index a1a2f23a9..3fdb6239d 100644 --- a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp +++ b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp @@ -37,6 +37,7 @@ #include "util/RsAction.h" #include "util/misc.h" #include "util/rstime.h" +#include "util/rsdir.h" #include #include @@ -55,27 +56,26 @@ #include /* Images for context menu icons */ -#define IMAGE_DOWNLOAD ":/images/download16.png" +#define IMAGE_DOWNLOAD ":/icons/png/download.png" #define IMAGE_PLAY ":/images/start.png" #define IMAGE_HASH_BUSY ":/images/settings.png" #define IMAGE_HASH_DONE ":/images/accepted16.png" #define IMAGE_MSG ":/images/message-mail.png" -#define IMAGE_ATTACHMENT ":/images/attachment.png" +#define IMAGE_ATTACHMENT ":/icons/png/attachements.png" #define IMAGE_FRIEND ":/images/peers_16x16.png" -#define IMAGE_PROGRESS ":/images/browse-looking.gif" #define IMAGE_COPYLINK ":/images/copyrslink.png" #define IMAGE_OPENFOLDER ":/images/folderopen.png" #define IMAGE_OPENFILE ":/images/fileopen.png" -#define IMAGE_LIBRARY ":/images/library.png" -#define IMAGE_CHANNEL ":/images/channels32.png" +#define IMAGE_LIBRARY ":/icons/collections.png" +#define IMAGE_CHANNEL ":/icons/png/channels.png" #define IMAGE_FORUMS ":/icons/png/forums.png" -#define IMAGE_COLLCREATE ":/images/library_add.png" -#define IMAGE_COLLMODIF ":/images/library_edit.png" -#define IMAGE_COLLVIEW ":/images/library_view.png" -#define IMAGE_COLLOPEN ":/images/library.png" -#define IMAGE_EDITSHARE ":/images/edit_16.png" +#define IMAGE_COLLCREATE ":/icons/png/add.png" +#define IMAGE_COLLMODIF ":/icons/png/pencil-edit-button.png" +#define IMAGE_COLLVIEW ":/images/find.png" +#define IMAGE_COLLOPEN ":/icons/collections.png" +#define IMAGE_EDITSHARE ":/icons/png/pencil-edit-button.png" #define IMAGE_MYFILES ":/icons/svg/folders1.svg" -#define IMAGE_REMOVE ":/images/deletemail24.png" +#define IMAGE_UNSHAREEXTRA ":/images/button_cancel.png" /*define viewType_CB value */ #define VIEW_TYPE_TREE 0 @@ -148,45 +148,50 @@ public: } }; +SharedFilesDialog::~SharedFilesDialog() +{ + delete tree_model; + delete flat_model; + delete tree_proxyModel; +} /** Constructor */ -SharedFilesDialog::SharedFilesDialog(RetroshareDirModel *_tree_model,RetroshareDirModel *_flat_model,QWidget *parent) +SharedFilesDialog::SharedFilesDialog(bool remote_mode, QWidget *parent) : RsAutoUpdatePage(1000,parent), model(NULL) { - /* Invoke the Qt Designer generated object setup routine */ - ui.setupUi(this); + /* Invoke the Qt Designer generated object setup routine */ + ui.setupUi(this); - NotifyQt *notify = NotifyQt::getInstance(); - connect(notify, SIGNAL(filesPreModChanged(bool)), this, SLOT(preModDirectories(bool))); - connect(notify, SIGNAL(filesPostModChanged(bool)), this, SLOT(postModDirectories(bool))); + NotifyQt *notify = NotifyQt::getInstance(); + connect(notify, SIGNAL(filesPreModChanged(bool)), this, SLOT(preModDirectories(bool))); + connect(notify, SIGNAL(filesPostModChanged(bool)), this, SLOT(postModDirectories(bool))); - connect(ui.viewType_CB, SIGNAL(currentIndexChanged(int)), this, SLOT(changeCurrentViewModel(int))); + connect(ui.viewType_CB, SIGNAL(currentIndexChanged(int)), this, SLOT(changeCurrentViewModel(int))); + connect(ui.dirTreeView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT( spawnCustomPopupMenu( QPoint ) ) ); + connect(ui.indicatorCBox, SIGNAL(currentIndexChanged(int)), this, SLOT(indicatorChanged(int))); - connect( ui.dirTreeView, SIGNAL( customContextMenuRequested( QPoint ) ), this, SLOT( spawnCustomPopupMenu( QPoint ) ) ); + tree_model = new TreeStyle_RDM(remote_mode); + flat_model = new FlatStyle_RDM(remote_mode); - connect(ui.indicatorCBox, SIGNAL(currentIndexChanged(int)), this, SLOT(indicatorChanged(int))); + connect(flat_model, SIGNAL(layoutChanged()), this, SLOT(updateDirTreeView()) ); - tree_model = _tree_model ; - flat_model = _flat_model ; - connect(flat_model, SIGNAL(layoutChanged()), this, SLOT(updateDirTreeView()) ); + // For filtering items we use a trick: the underlying model will use this FilterRole role to highlight selected items + // while the filterProxyModel will select them using the pre-chosen string "filtered". - // For filtering items we use a trick: the underlying model will use this FilterRole role to highlight selected items - // while the filterProxyModel will select them using the pre-chosen string "filtered". - - tree_proxyModel = new SFDSortFilterProxyModel(tree_model, this); - tree_proxyModel->setSourceModel(tree_model); - tree_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); - tree_proxyModel->setSortRole(RetroshareDirModel::SortRole); - tree_proxyModel->sort(COLUMN_NAME); - tree_proxyModel->setFilterRole(RetroshareDirModel::FilterRole); - tree_proxyModel->setFilterRegExp(QRegExp(QString(RETROSHARE_DIR_MODEL_FILTER_STRING))) ; + tree_proxyModel = new SFDSortFilterProxyModel(tree_model, this); + tree_proxyModel->setSourceModel(tree_model); + tree_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); + tree_proxyModel->setSortRole(RetroshareDirModel::SortRole); + tree_proxyModel->sort(COLUMN_NAME); + tree_proxyModel->setFilterRole(RetroshareDirModel::FilterRole); + tree_proxyModel->setFilterRegExp(QRegExp(QString(RETROSHARE_DIR_MODEL_FILTER_STRING))) ; flat_proxyModel = new SFDSortFilterProxyModel(flat_model, this); flat_proxyModel->setSourceModel(flat_model); flat_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); flat_proxyModel->setSortRole(RetroshareDirModel::SortRole); flat_proxyModel->sort(COLUMN_NAME); - flat_proxyModel->setFilterRole(RetroshareDirModel::FilterRole); - flat_proxyModel->setFilterRegExp(QRegExp(QString(RETROSHARE_DIR_MODEL_FILTER_STRING))) ; + flat_proxyModel->setFilterRole(RetroshareDirModel::FilterRole); + flat_proxyModel->setFilterRegExp(QRegExp(QString(RETROSHARE_DIR_MODEL_FILTER_STRING))) ; // Mr.Alice: I removed this because it causes a crash for some obscur reason. Apparently when the model is changed, the proxy model cannot // deal with the change by itself. Should I call something specific? I've no idea. Removing this does not seem to cause any harm either. @@ -195,31 +200,31 @@ SharedFilesDialog::SharedFilesDialog(RetroshareDirModel *_tree_model,RetroshareD flat_proxyModel->setDynamicSortFilter(false); connect(ui.filterClearButton, SIGNAL(clicked()), this, SLOT(clearFilter())); - connect(ui.filterStartButton, SIGNAL(clicked()), this, SLOT(startFilter())); - connect(ui.filterPatternLineEdit, SIGNAL(returnPressed()), this, SLOT(startFilter())); - connect(ui.filterPatternLineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(onFilterTextEdited())); - //Hidden by default, shown on onFilterTextEdited - ui.filterClearButton->hide(); - ui.filterStartButton->hide(); + connect(ui.filterStartButton, SIGNAL(clicked()), this, SLOT(startFilter())); + connect(ui.filterPatternLineEdit, SIGNAL(returnPressed()), this, SLOT(startFilter())); + connect(ui.filterPatternLineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(onFilterTextEdited())); + //Hidden by default, shown on onFilterTextEdited + ui.filterClearButton->hide(); + ui.filterStartButton->hide(); // mFilterTimer = new RsProtectedTimer( this ); // mFilterTimer->setSingleShot( true ); // Ensure the timer will fire only once after it was started // connect(mFilterTimer, SIGNAL(timeout()), this, SLOT(filterRegExpChanged())); - /* Set header resize modes and initial section sizes */ - QHeaderView * header = ui.dirTreeView->header () ; + /* Set header resize modes and initial section sizes */ + QHeaderView * header = ui.dirTreeView->header () ; - header->resizeSection ( COLUMN_NAME, 490 ); - header->resizeSection ( COLUMN_FILENB, 70 ); - header->resizeSection ( COLUMN_SIZE, 70 ); - header->resizeSection ( COLUMN_AGE, 100 ); - header->resizeSection ( COLUMN_FRIEND_ACCESS,100); - header->resizeSection ( COLUMN_WN_VISU_DIR, 100 ); + header->resizeSection ( COLUMN_NAME, 490 ); + header->resizeSection ( COLUMN_FILENB, 70 ); + header->resizeSection ( COLUMN_SIZE, 70 ); + header->resizeSection ( COLUMN_AGE, 100 ); + header->resizeSection ( COLUMN_FRIEND_ACCESS,100); + header->resizeSection ( COLUMN_WN_VISU_DIR, 100 ); - header->setStretchLastSection(false); + header->setStretchLastSection(false); - /* Set Multi Selection */ - ui.dirTreeView->setSelectionMode(QAbstractItemView::ExtendedSelection); + /* Set Multi Selection */ + ui.dirTreeView->setSelectionMode(QAbstractItemView::ExtendedSelection); /* Hide platform specific features */ copylinkAct = new QAction(QIcon(IMAGE_COPYLINK), tr( "Copy retroshare Links to Clipboard" ), this ); @@ -229,86 +234,86 @@ SharedFilesDialog::SharedFilesDialog(RetroshareDirModel *_tree_model,RetroshareD sendlinkAct = new QAction(QIcon(IMAGE_COPYLINK), tr( "Send retroshare Links" ), this ); connect( sendlinkAct , SIGNAL( triggered() ), this, SLOT( sendLinkTo( ) ) ); - removeExtraFileAct = new QAction(QIcon(IMAGE_REMOVE), tr( "Stop sharing this file" ), this ); + removeExtraFileAct = new QAction(QIcon(IMAGE_UNSHAREEXTRA), tr( "Stop sharing this file" ), this ); connect( removeExtraFileAct , SIGNAL( triggered() ), this, SLOT( removeExtraFile() ) ); - collCreateAct= new QAction(QIcon(IMAGE_COLLCREATE), tr("Create Collection..."), this) ; - connect(collCreateAct,SIGNAL(triggered()),this,SLOT(collCreate())) ; - collModifAct= new QAction(QIcon(IMAGE_COLLMODIF), tr("Modify Collection..."), this) ; - connect(collModifAct,SIGNAL(triggered()),this,SLOT(collModif())) ; - collViewAct= new QAction(QIcon(IMAGE_COLLVIEW), tr("View Collection..."), this) ; - connect(collViewAct,SIGNAL(triggered()),this,SLOT(collView())) ; - collOpenAct = new QAction(QIcon(IMAGE_COLLOPEN), tr( "Download from collection file..." ), this ) ; - connect(collOpenAct, SIGNAL(triggered()), this, SLOT(collOpen())) ; + collCreateAct= new QAction(QIcon(IMAGE_COLLCREATE), tr("Create Collection..."), this) ; + connect(collCreateAct,SIGNAL(triggered()),this,SLOT(collCreate())) ; + collModifAct= new QAction(QIcon(IMAGE_COLLMODIF), tr("Modify Collection..."), this) ; + connect(collModifAct,SIGNAL(triggered()),this,SLOT(collModif())) ; + collViewAct= new QAction(QIcon(IMAGE_COLLVIEW), tr("View Collection..."), this) ; + connect(collViewAct,SIGNAL(triggered()),this,SLOT(collView())) ; + collOpenAct = new QAction(QIcon(IMAGE_COLLOPEN), tr( "Download from collection file..." ), this ) ; + connect(collOpenAct, SIGNAL(triggered()), this, SLOT(collOpen())) ; } LocalSharedFilesDialog::LocalSharedFilesDialog(QWidget *parent) - : SharedFilesDialog(new TreeStyle_RDM(false),new FlatStyle_RDM(false),parent) + : SharedFilesDialog(false,parent) { - // Hide columns after loading the settings - ui.dirTreeView->setColumnHidden(COLUMN_WN_VISU_DIR, false) ; - ui.downloadButton->hide() ; + // Hide columns after loading the settings + ui.dirTreeView->setColumnHidden(COLUMN_WN_VISU_DIR, false) ; + ui.downloadButton->hide() ; - // load settings - processSettings(true); - // Setup the current view model. - // - changeCurrentViewModel(ui.viewType_CB->currentIndex()) ; + // load settings + processSettings(true); + // Setup the current view model. + // + changeCurrentViewModel(ui.viewType_CB->currentIndex()) ; - connect(ui.addShares_PB, SIGNAL(clicked()), this, SLOT(addShares())) ; - connect(ui.checkButton, SIGNAL(clicked()), this, SLOT(forceCheck())) ; + connect(ui.addShares_PB, SIGNAL(clicked()), this, SLOT(addShares())) ; + connect(ui.checkButton, SIGNAL(clicked()), this, SLOT(forceCheck())) ; - openfileAct = new QAction(QIcon(IMAGE_OPENFILE), tr("Open File"), this) ; - connect(openfileAct, SIGNAL(triggered()), this, SLOT(openfile())) ; - openfolderAct = new QAction(QIcon(IMAGE_OPENFOLDER), tr("Open Folder"), this) ; - connect(openfolderAct, SIGNAL(triggered()), this, SLOT(openfolder())) ; + openfileAct = new QAction(QIcon(IMAGE_OPENFILE), tr("Open File"), this) ; + connect(openfileAct, SIGNAL(triggered()), this, SLOT(openfile())) ; + openfolderAct = new QAction(QIcon(IMAGE_OPENFOLDER), tr("Open Folder"), this) ; + connect(openfolderAct, SIGNAL(triggered()), this, SLOT(openfolder())) ; - ui.titleBarPixmap->setPixmap(QPixmap(IMAGE_MYFILES)) ; + ui.titleBarPixmap->setPixmap(FilesDefs::getPixmapFromQtResourcePath(IMAGE_MYFILES)) ; ui.dirTreeView->setItemDelegateForColumn(COLUMN_FRIEND_ACCESS,new ShareFlagsItemDelegate()) ; } RemoteSharedFilesDialog::RemoteSharedFilesDialog(QWidget *parent) - : SharedFilesDialog(new TreeStyle_RDM(true),new FlatStyle_RDM(true),parent) + : SharedFilesDialog(true,parent) { - ui.dirTreeView->setColumnHidden(COLUMN_FRIEND_ACCESS, false) ; - ui.dirTreeView->setColumnHidden(COLUMN_WN_VISU_DIR, true) ; - ui.checkButton->hide() ; + ui.dirTreeView->setColumnHidden(COLUMN_FRIEND_ACCESS, false) ; + ui.dirTreeView->setColumnHidden(COLUMN_WN_VISU_DIR, true) ; + ui.checkButton->hide() ; - connect(ui.downloadButton, SIGNAL(clicked()), this, SLOT(downloadRemoteSelected())); + connect(ui.downloadButton, SIGNAL(clicked()), this, SLOT(downloadRemoteSelected())); connect(ui.dirTreeView, SIGNAL( expanded(const QModelIndex & ) ), this, SLOT( expanded(const QModelIndex & ) ) ); connect(ui.dirTreeView, SIGNAL( doubleClicked(const QModelIndex & ) ), this, SLOT( expanded(const QModelIndex & ) ) ); - // load settings - processSettings(true); - // Setup the current view model. - // - changeCurrentViewModel(ui.viewType_CB->currentIndex()) ; + // load settings + processSettings(true); + // Setup the current view model. + // + changeCurrentViewModel(ui.viewType_CB->currentIndex()) ; - ui.addShares_PB->hide() ; + ui.addShares_PB->hide() ; } void LocalSharedFilesDialog::addShares() { - ShareManager::showYourself(); + ShareManager::showYourself(); } void SharedFilesDialog::hideEvent(QHideEvent *) { - if(model!=NULL) - model->setVisible(false) ; + if(model!=NULL) + model->setVisible(false) ; } void SharedFilesDialog::showEvent(QShowEvent *) { - if(model!=NULL) - { + if(model!=NULL) + { std::set expanded_indexes,hidden_indexes,selected_indexes ; saveExpandedPathsAndSelection(expanded_indexes,hidden_indexes,selected_indexes); - model->setVisible(true) ; - model->update() ; + model->setVisible(true) ; + model->update() ; restoreExpandedPathsAndSelection(expanded_indexes,hidden_indexes,selected_indexes); } @@ -327,63 +332,63 @@ LocalSharedFilesDialog::~LocalSharedFilesDialog() void LocalSharedFilesDialog::processSettings(bool bLoad) { - Settings->beginGroup("LocalSharedFilesDialog"); + Settings->beginGroup("LocalSharedFilesDialog"); - if (bLoad) { - // load settings + if (bLoad) { + // load settings - // state of the trees - ui.dirTreeView->header()->restoreState(Settings->value("LocalDirTreeView").toByteArray()); + // state of the trees + ui.dirTreeView->header()->restoreState(Settings->value("LocalDirTreeView").toByteArray()); - // state of splitter - ui.splitter->restoreState(Settings->value("LocalSplitter").toByteArray()); + // state of splitter + ui.splitter->restoreState(Settings->value("LocalSplitter").toByteArray()); - // view type - ui.viewType_CB->setCurrentIndex(Settings->value("LocalViewType").toInt()); - } else { - // save settings + // view type + ui.viewType_CB->setCurrentIndex(Settings->value("LocalViewType").toInt()); + } else { + // save settings - // state of trees - Settings->setValue("LocalDirTreeView", ui.dirTreeView->header()->saveState()); + // state of trees + Settings->setValue("LocalDirTreeView", ui.dirTreeView->header()->saveState()); - // state of splitter - Settings->setValue("LocalSplitter", ui.splitter->saveState()); + // state of splitter + Settings->setValue("LocalSplitter", ui.splitter->saveState()); - // view type - Settings->setValue("LocalViewType", ui.viewType_CB->currentIndex()); - } + // view type + Settings->setValue("LocalViewType", ui.viewType_CB->currentIndex()); + } - Settings->endGroup(); + Settings->endGroup(); } void RemoteSharedFilesDialog::processSettings(bool bLoad) { - Settings->beginGroup("RemoteSharedFilesDialog"); + Settings->beginGroup("RemoteSharedFilesDialog"); - if (bLoad) { - // load settings + if (bLoad) { + // load settings - // state of the trees - ui.dirTreeView->header()->restoreState(Settings->value("RemoteDirTreeView").toByteArray()); + // state of the trees + ui.dirTreeView->header()->restoreState(Settings->value("RemoteDirTreeView").toByteArray()); - // state of splitter - ui.splitter->restoreState(Settings->value("RemoteSplitter").toByteArray()); + // state of splitter + ui.splitter->restoreState(Settings->value("RemoteSplitter").toByteArray()); - // view type - ui.viewType_CB->setCurrentIndex(Settings->value("RemoteViewType").toInt()); - } else { - // save settings + // view type + ui.viewType_CB->setCurrentIndex(Settings->value("RemoteViewType").toInt()); + } else { + // save settings - // state of trees - Settings->setValue("RemoteDirTreeView", ui.dirTreeView->header()->saveState()); + // state of trees + Settings->setValue("RemoteDirTreeView", ui.dirTreeView->header()->saveState()); - // state of splitter - Settings->setValue("RemoteSplitter", ui.splitter->saveState()); + // state of splitter + Settings->setValue("RemoteSplitter", ui.splitter->saveState()); - // view type - Settings->setValue("RemoteViewType", ui.viewType_CB->currentIndex()); - } + // view type + Settings->setValue("RemoteViewType", ui.viewType_CB->currentIndex()); + } - Settings->endGroup(); + Settings->endGroup(); } void SharedFilesDialog::changeCurrentViewModel(int viewTypeIndex) @@ -391,195 +396,195 @@ void SharedFilesDialog::changeCurrentViewModel(int viewTypeIndex) // disconnect( ui.dirTreeView, SIGNAL( collapsed(const QModelIndex & ) ), NULL, NULL ); // disconnect( ui.dirTreeView, SIGNAL( expanded(const QModelIndex & ) ), NULL, NULL ); - if(model!=NULL) - model->setVisible(false) ; + if(model!=NULL) + model->setVisible(false) ; - if(viewTypeIndex==VIEW_TYPE_TREE) - { - model = tree_model ; + if(viewTypeIndex==VIEW_TYPE_TREE) + { + model = tree_model ; proxyModel = tree_proxyModel ; - } - else - { - model = flat_model ; + } + else + { + model = flat_model ; proxyModel = flat_proxyModel ; } - showProperColumns() ; + showProperColumns() ; std::set expanded_indexes,hidden_indexes,selected_indexes ; saveExpandedPathsAndSelection(expanded_indexes,hidden_indexes,selected_indexes); if(isVisible()) - { + { model->setVisible(true) ; - model->update() ; - } + model->update() ; + } // connect( ui.dirTreeView, SIGNAL( collapsed(const QModelIndex & ) ), this, SLOT( collapsed(const QModelIndex & ) ) ); - ui.dirTreeView->setModel(proxyModel); - ui.dirTreeView->update(); + ui.dirTreeView->setModel(proxyModel); + ui.dirTreeView->update(); restoreExpandedPathsAndSelection(expanded_indexes,hidden_indexes,selected_indexes); QHeaderView * header = ui.dirTreeView->header () ; - QHeaderView_setSectionResizeModeColumn(header, COLUMN_NAME, QHeaderView::Interactive); + QHeaderView_setSectionResizeModeColumn(header, COLUMN_NAME, QHeaderView::Interactive); - ui.dirTreeView->header()->headerDataChanged(Qt::Horizontal, COLUMN_NAME, COLUMN_WN_VISU_DIR) ; + ui.dirTreeView->header()->headerDataChanged(Qt::Horizontal, COLUMN_NAME, COLUMN_WN_VISU_DIR) ; // recursRestoreExpandedItems(ui.dirTreeView->rootIndex(),expanded_indexes); - FilterItems(); + FilterItems(); } void LocalSharedFilesDialog::showProperColumns() { - if(model == tree_model) - { - ui.dirTreeView->setColumnHidden(COLUMN_FILENB, false) ; - ui.dirTreeView->setColumnHidden(COLUMN_FRIEND_ACCESS, false) ; - ui.dirTreeView->setColumnHidden(COLUMN_WN_VISU_DIR, false) ; + if(model == tree_model) + { + ui.dirTreeView->setColumnHidden(COLUMN_FILENB, false) ; + ui.dirTreeView->setColumnHidden(COLUMN_FRIEND_ACCESS, false) ; + ui.dirTreeView->setColumnHidden(COLUMN_WN_VISU_DIR, false) ; #ifdef DONT_USE_SEARCH_IN_TREE_VIEW - ui.filterLabel->hide(); - ui.filterPatternLineEdit->hide(); - ui.filterStartButton->hide(); - ui.filterClearButton->hide(); + ui.filterLabel->hide(); + ui.filterPatternLineEdit->hide(); + ui.filterStartButton->hide(); + ui.filterClearButton->hide(); #endif - } - else - { - ui.dirTreeView->setColumnHidden(COLUMN_FILENB, true) ; - ui.dirTreeView->setColumnHidden(COLUMN_FRIEND_ACCESS, true) ; - ui.dirTreeView->setColumnHidden(COLUMN_WN_VISU_DIR, false) ; + } + else + { + ui.dirTreeView->setColumnHidden(COLUMN_FILENB, true) ; + ui.dirTreeView->setColumnHidden(COLUMN_FRIEND_ACCESS, true) ; + ui.dirTreeView->setColumnHidden(COLUMN_WN_VISU_DIR, false) ; #ifdef DONT_USE_SEARCH_IN_TREE_VIEW - ui.filterLabel->show(); - ui.filterPatternLineEdit->show(); + ui.filterLabel->show(); + ui.filterPatternLineEdit->show(); #endif - } + } } void RemoteSharedFilesDialog::showProperColumns() { - if(model == tree_model) - { - ui.dirTreeView->setColumnHidden(COLUMN_FILENB, false) ; - ui.dirTreeView->setColumnHidden(COLUMN_FRIEND_ACCESS, true) ; - ui.dirTreeView->setColumnHidden(COLUMN_WN_VISU_DIR, true) ; + if(model == tree_model) + { + ui.dirTreeView->setColumnHidden(COLUMN_FILENB, false) ; + ui.dirTreeView->setColumnHidden(COLUMN_FRIEND_ACCESS, true) ; + ui.dirTreeView->setColumnHidden(COLUMN_WN_VISU_DIR, true) ; #ifdef DONT_USE_SEARCH_IN_TREE_VIEW - ui.filterLabel->hide(); - ui.filterPatternLineEdit->hide(); - ui.filterStartButton->hide(); - ui.filterClearButton->hide(); + ui.filterLabel->hide(); + ui.filterPatternLineEdit->hide(); + ui.filterStartButton->hide(); + ui.filterClearButton->hide(); #endif - } - else - { - ui.dirTreeView->setColumnHidden(COLUMN_FILENB, true) ; - ui.dirTreeView->setColumnHidden(COLUMN_FRIEND_ACCESS, false) ; - ui.dirTreeView->setColumnHidden(COLUMN_WN_VISU_DIR, false) ; + } + else + { + ui.dirTreeView->setColumnHidden(COLUMN_FILENB, true) ; + ui.dirTreeView->setColumnHidden(COLUMN_FRIEND_ACCESS, false) ; + ui.dirTreeView->setColumnHidden(COLUMN_WN_VISU_DIR, false) ; #ifdef DONT_USE_SEARCH_IN_TREE_VIEW - ui.filterLabel->show(); - ui.filterPatternLineEdit->show(); + ui.filterLabel->show(); + ui.filterPatternLineEdit->show(); #endif - } + } } void LocalSharedFilesDialog::checkUpdate() { - /* update */ - if (rsFiles->InDirectoryCheck()) - { - ui.checkButton->setText(tr("Checking...")); - } - else - { - ui.checkButton->setText(tr("Check files")); - ui.hashLabel->setPixmap(QPixmap(IMAGE_HASH_DONE)); - ui.hashLabel->setToolTip("") ; - } + /* update */ + if (rsFiles->InDirectoryCheck()) + { + ui.checkButton->setText(tr("Checking...")); + } + else + { + ui.checkButton->setText(tr("Check files")); + ui.hashLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(IMAGE_HASH_DONE)); + ui.hashLabel->setToolTip("") ; + } - return; + return; } void LocalSharedFilesDialog::forceCheck() { - rsFiles->ForceDirectoryCheck(); - return; + rsFiles->ForceDirectoryCheck(); + return; } void RemoteSharedFilesDialog::spawnCustomPopupMenu( QPoint point ) { - if (!rsPeers) return; /* not ready yet! */ + if (!rsPeers) return; /* not ready yet! */ - QMenu *contextMenu = new QMenu(this); + QMenu *contextMenu = new QMenu(this); - QModelIndex idx = ui.dirTreeView->indexAt(point) ; - if (idx.isValid()) - { + QModelIndex idx = ui.dirTreeView->indexAt(point) ; + if (idx.isValid()) + { - QModelIndex midx = proxyModel->mapToSource(idx) ; - if (midx.isValid()) - { + QModelIndex midx = proxyModel->mapToSource(idx) ; + if (midx.isValid()) + { - currentFile = model->data(midx, RetroshareDirModel::FileNameRole).toString() ; - int type = model->getType(midx) ; - if ( (type == DIR_TYPE_DIR) || (type == DIR_TYPE_FILE) ) - { - collCreateAct->setEnabled(true); - collOpenAct->setEnabled(true); + currentFile = model->data(midx, RetroshareDirModel::FileNameRole).toString() ; + int type = model->getType(midx) ; + if ( (type == DIR_TYPE_DIR) || (type == DIR_TYPE_FILE) ) + { + collCreateAct->setEnabled(true); + collOpenAct->setEnabled(true); - QModelIndexList list = ui.dirTreeView->selectionModel()->selectedRows() ; + QModelIndexList list = ui.dirTreeView->selectionModel()->selectedRows() ; - if(type == DIR_TYPE_DIR || list.size() > 1) - { - QAction *downloadActI = new QAction(QIcon(IMAGE_DOWNLOAD), tr( "Download..." ), contextMenu ) ; - connect( downloadActI , SIGNAL( triggered() ), this, SLOT( downloadRemoteSelectedInteractive() ) ) ; - contextMenu->addAction( downloadActI) ; - } - else - { - QAction *downloadAct = new QAction(QIcon(IMAGE_DOWNLOAD), tr( "Download" ), contextMenu ) ; - connect( downloadAct , SIGNAL( triggered() ), this, SLOT( downloadRemoteSelected() ) ) ; - contextMenu->addAction( downloadAct) ; - } + if(type == DIR_TYPE_DIR || list.size() > 1) + { + QAction *downloadActI = new QAction(QIcon(IMAGE_DOWNLOAD), tr( "Download..." ), contextMenu ) ; + connect( downloadActI , SIGNAL( triggered() ), this, SLOT( downloadRemoteSelectedInteractive() ) ) ; + contextMenu->addAction( downloadActI) ; + } + else + { + QAction *downloadAct = new QAction(QIcon(IMAGE_DOWNLOAD), tr( "Download" ), contextMenu ) ; + connect( downloadAct , SIGNAL( triggered() ), this, SLOT( downloadRemoteSelected() ) ) ; + contextMenu->addAction( downloadAct) ; + } - contextMenu->addSeparator() ;//------------------------------------ - contextMenu->addAction( copylinkAct) ; - contextMenu->addAction( sendlinkAct) ; - contextMenu->addSeparator() ;//------------------------------------ - contextMenu->addAction(QIcon(IMAGE_MSG), tr("Recommend in a message to..."), this, SLOT(recommendFilesToMsg())) ; + contextMenu->addSeparator() ;//------------------------------------ + contextMenu->addAction( copylinkAct) ; + contextMenu->addAction( sendlinkAct) ; + contextMenu->addSeparator() ;//------------------------------------ + contextMenu->addAction(QIcon(IMAGE_MSG), tr("Recommend in a message to..."), this, SLOT(recommendFilesToMsg())) ; - contextMenu->addSeparator() ;//------------------------------------ + contextMenu->addSeparator() ;//------------------------------------ - QMenu collectionMenu(tr("Collection"), this); - collectionMenu.setIcon(QIcon(IMAGE_LIBRARY)); - collectionMenu.addAction(collCreateAct); - collectionMenu.addAction(collOpenAct); - contextMenu->addMenu(&collectionMenu) ; + QMenu collectionMenu(tr("Collection"), this); + collectionMenu.setIcon(QIcon(IMAGE_LIBRARY)); + collectionMenu.addAction(collCreateAct); + collectionMenu.addAction(collOpenAct); + contextMenu->addMenu(&collectionMenu) ; - } + } - } - } + } + } - contextMenu = model->getContextMenu(contextMenu); + contextMenu = model->getContextMenu(contextMenu); - if (!contextMenu->children().isEmpty()) - contextMenu->exec(QCursor::pos()) ; + if (!contextMenu->children().isEmpty()) + contextMenu->exec(QCursor::pos()) ; - delete contextMenu; + delete contextMenu; } QModelIndexList SharedFilesDialog::getSelected() { - QModelIndexList list = ui.dirTreeView->selectionModel()->selectedIndexes() ; - QModelIndexList proxyList ; - for (QModelIndexList::iterator index = list.begin(); index != list.end(); ++index ) { - proxyList.append(proxyModel->mapToSource(*index)) ; - } + QModelIndexList list = ui.dirTreeView->selectionModel()->selectedIndexes() ; + QModelIndexList proxyList ; + for (QModelIndexList::iterator index = list.begin(); index != list.end(); ++index ) { + proxyList.append(proxyModel->mapToSource(*index)) ; + } - return proxyList ; + return proxyList ; } void RemoteSharedFilesDialog::expanded(const QModelIndex& indx) @@ -592,52 +597,47 @@ void RemoteSharedFilesDialog::expanded(const QModelIndex& indx) } void RemoteSharedFilesDialog::downloadRemoteSelectedInteractive() { - /* call back to the model (which does all the interfacing? */ + /* call back to the model (which does all the interfacing? */ - std::cerr << "Downloading Files" ; - std::cerr << std::endl ; + std::cerr << "Downloading Files" ; + std::cerr << std::endl ; - QModelIndexList lst = getSelected() ; - model -> downloadSelected(lst,true) ; + QModelIndexList lst = getSelected() ; + model -> downloadSelected(lst,true) ; } void RemoteSharedFilesDialog::downloadRemoteSelected() { - /* call back to the model (which does all the interfacing? */ + /* call back to the model (which does all the interfacing? */ - std::cerr << "Downloading Files" ; - std::cerr << std::endl ; + std::cerr << "Downloading Files" ; + std::cerr << std::endl ; - QModelIndexList lst = getSelected() ; - model -> downloadSelected(lst,false) ; + QModelIndexList lst = getSelected() ; + model -> downloadSelected(lst,false) ; } void SharedFilesDialog::copyLinks(const QModelIndexList& lst, bool remote,QList& urls,bool& has_unhashed_files) { - std::vector dirVec; + std::vector dirVec; - model->getDirDetailsFromSelect(lst, dirVec); + model->getDirDetailsFromSelect(lst, dirVec); - has_unhashed_files = false; + has_unhashed_files = false; - for (int i = 0, n = dirVec.size(); i < n; ++i) - { - const DirDetails& details = dirVec[i]; + for (int i = 0, n = dirVec.size(); i < n; ++i) + { + const DirDetails& details = dirVec[i]; - if (details.type == DIR_TYPE_DIR) - { - FileTree *ft = FileTree::create(details,remote) ; + if (details.type == DIR_TYPE_DIR) + { + auto ft = RsFileTree::fromDirDetails(details,remote); - std::cerr << "Created collection file tree:" << std::endl; - ft->print(); + QString dir_name = QDir(QString::fromUtf8(details.name.c_str())).dirName(); - QString dir_name = QDir(QString::fromUtf8(details.name.c_str())).dirName(); - - RetroShareLink link = RetroShareLink::createFileTree(dir_name,ft->mTotalSize,ft->mTotalFiles,QString::fromStdString(ft->toRadix64())) ; + RetroShareLink link = RetroShareLink::createFileTree(dir_name,ft->mTotalSize,ft->mTotalFiles,QString::fromStdString(ft->toRadix64())) ; if(link.valid()) urls.push_back(link) ; - - delete ft ; } else { @@ -646,7 +646,16 @@ void SharedFilesDialog::copyLinks(const QModelIndexList& lst, bool remote,QList< has_unhashed_files = true; continue; } - RetroShareLink link = RetroShareLink::createFile(QString::fromUtf8(details.name.c_str()), details.count, details.hash.toStdString().c_str()); + QString name; + if(details.type == DIR_TYPE_EXTRA_FILE) + { + std::string dir,file; + RsDirUtil::splitDirFromFile(details.name,dir,file) ; + name = QString::fromStdString(file); + } + else + name = QString::fromUtf8(details.name.c_str()); + RetroShareLink link = RetroShareLink::createFile(name, details.count, details.hash.toStdString().c_str()); if (link.valid()) { urls.push_back(link) ; } @@ -656,10 +665,10 @@ void SharedFilesDialog::copyLinks(const QModelIndexList& lst, bool remote,QList< void SharedFilesDialog::copyLink (const QModelIndexList& lst, bool remote) { - QList urls ; - bool has_unhashed_files = false; + QList urls ; + bool has_unhashed_files = false; - copyLinks(lst,remote,urls,has_unhashed_files) ; + copyLinks(lst,remote,urls,has_unhashed_files) ; RSLinkClipboard::copyLinks(urls) ; if(has_unhashed_files) @@ -706,108 +715,108 @@ void SharedFilesDialog::sendLinkTo() void SharedFilesDialog::collCreate() { - QModelIndexList lst = getSelected(); - model->createCollectionFile(this, lst); + QModelIndexList lst = getSelected(); + model->createCollectionFile(this, lst); } void SharedFilesDialog::collModif() { - std::list files_info ; + std::list files_info ; - model->getFileInfoFromIndexList(getSelected(),files_info); + model->getFileInfoFromIndexList(getSelected(),files_info); - if(files_info.size() != 1) return ; + if(files_info.size() != 1) return ; - /* make path for downloaded files */ - std::list::iterator it = files_info.begin(); - DirDetails details = (*it); - FileInfo info; - if (!rsFiles->FileDetails(details.hash, RS_FILE_HINTS_EXTRA | RS_FILE_HINTS_LOCAL - | RS_FILE_HINTS_BROWSABLE | RS_FILE_HINTS_NETWORK_WIDE - | RS_FILE_HINTS_SPEC_ONLY, info)) return; + /* make path for downloaded files */ + std::list::iterator it = files_info.begin(); + DirDetails details = (*it); + FileInfo info; + if (!rsFiles->FileDetails(details.hash, RS_FILE_HINTS_EXTRA | RS_FILE_HINTS_LOCAL + | RS_FILE_HINTS_BROWSABLE | RS_FILE_HINTS_NETWORK_WIDE + | RS_FILE_HINTS_SPEC_ONLY, info)) return; - std::string path; - path = info.path; + std::string path; + path = info.path; - /* open file with a suitable application */ - QFileInfo qinfo; - qinfo.setFile(QString::fromUtf8(path.c_str())); - if (qinfo.exists()) { - if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) { - RsCollection collection; - collection.openColl(qinfo.absoluteFilePath()); - } - } + /* open file with a suitable application */ + QFileInfo qinfo; + qinfo.setFile(QString::fromUtf8(path.c_str())); + if (qinfo.exists()) { + if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) { + RsCollection collection; + collection.openColl(qinfo.absoluteFilePath()); + } + } } void SharedFilesDialog::collView() { - std::list files_info ; + std::list files_info ; - model->getFileInfoFromIndexList(getSelected(),files_info); + model->getFileInfoFromIndexList(getSelected(),files_info); - if(files_info.size() != 1) return ; + if(files_info.size() != 1) return ; - /* make path for downloaded files */ - std::list::iterator it = files_info.begin(); - DirDetails details = (*it); - FileInfo info; - if (!rsFiles->FileDetails(details.hash, RS_FILE_HINTS_EXTRA | RS_FILE_HINTS_LOCAL - | RS_FILE_HINTS_BROWSABLE | RS_FILE_HINTS_NETWORK_WIDE - | RS_FILE_HINTS_SPEC_ONLY, info)) return; + /* make path for downloaded files */ + std::list::iterator it = files_info.begin(); + DirDetails details = (*it); + FileInfo info; + if (!rsFiles->FileDetails(details.hash, RS_FILE_HINTS_EXTRA | RS_FILE_HINTS_LOCAL + | RS_FILE_HINTS_BROWSABLE | RS_FILE_HINTS_NETWORK_WIDE + | RS_FILE_HINTS_SPEC_ONLY, info)) return; - std::string path; - path = info.path; + std::string path; + path = info.path; - /* open file with a suitable application */ - QFileInfo qinfo; - qinfo.setFile(QString::fromUtf8(path.c_str())); - if (qinfo.exists()) { - if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) { - RsCollection collection; - collection.openColl(qinfo.absoluteFilePath(), true); - } - } + /* open file with a suitable application */ + QFileInfo qinfo; + qinfo.setFile(QString::fromUtf8(path.c_str())); + if (qinfo.exists()) { + if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) { + RsCollection collection; + collection.openColl(qinfo.absoluteFilePath(), true); + } + } } void SharedFilesDialog::collOpen() { - std::list files_info ; + std::list files_info ; - model->getFileInfoFromIndexList(getSelected(),files_info); + model->getFileInfoFromIndexList(getSelected(),files_info); - if(files_info.size() == 1) { + if(files_info.size() == 1) { - /* make path for downloaded files */ - std::list::iterator it = files_info.begin(); - DirDetails details = (*it); - FileInfo info; - if (rsFiles->FileDetails(details.hash, RS_FILE_HINTS_EXTRA | RS_FILE_HINTS_LOCAL - | RS_FILE_HINTS_BROWSABLE | RS_FILE_HINTS_NETWORK_WIDE - | RS_FILE_HINTS_SPEC_ONLY, info)) { + /* make path for downloaded files */ + std::list::iterator it = files_info.begin(); + DirDetails details = (*it); + FileInfo info; + if (rsFiles->FileDetails(details.hash, RS_FILE_HINTS_EXTRA | RS_FILE_HINTS_LOCAL + | RS_FILE_HINTS_BROWSABLE | RS_FILE_HINTS_NETWORK_WIDE + | RS_FILE_HINTS_SPEC_ONLY, info)) { - std::string path; - path = info.path; + std::string path; + path = info.path; - /* open file with a suitable application */ - QFileInfo qinfo; - qinfo.setFile(QString::fromUtf8(path.c_str())); - if (qinfo.exists()) { - if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) { - RsCollection collection; - if (collection.load(qinfo.absoluteFilePath())) { - collection.downloadFiles(); - return; - } - } - } - } - } + /* open file with a suitable application */ + QFileInfo qinfo; + qinfo.setFile(QString::fromUtf8(path.c_str())); + if (qinfo.exists()) { + if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) { + RsCollection collection; + if (collection.load(qinfo.absoluteFilePath())) { + collection.downloadFiles(); + return; + } + } + } + } + } - RsCollection collection; - if (collection.load(this)) { - collection.downloadFiles(); - } + RsCollection collection; + if (collection.load(this)) { + collection.downloadFiles(); + } } void LocalSharedFilesDialog::playselectedfiles() @@ -824,12 +833,12 @@ void LocalSharedFilesDialog::playselectedfiles() QStringList fullpaths; for(it = paths.begin(); it != paths.end(); ++it) { - std::string fullpath; - rsFiles->ConvertSharedFilePath(*it, fullpath); - fullpaths.push_back(QString::fromStdString(fullpath)); + std::string fullpath; + rsFiles->ConvertSharedFilePath(*it, fullpath); + fullpaths.push_back(QString::fromStdString(fullpath)); - std::cerr << "Playing: " << fullpath; - std::cerr << std::endl; + std::cerr << "Playing: " << fullpath; + std::cerr << std::endl; } playFiles(fullpaths); @@ -864,27 +873,27 @@ void SharedFilesDialog::recommendFilesToMsg() void LocalSharedFilesDialog::openfile() { - /* call back to the model (which does all the interfacing? */ + /* call back to the model (which does all the interfacing? */ - std::cerr << "SharedFilesDialog::openfile" << std::endl; + std::cerr << "SharedFilesDialog::openfile" << std::endl; - QModelIndexList qmil = getSelected(); - model->openSelected(qmil); + QModelIndexList qmil = getSelected(); + model->openSelected(qmil); } void LocalSharedFilesDialog::openfolder() { - std::cerr << "SharedFilesDialog::openfolder" << std::endl; + std::cerr << "SharedFilesDialog::openfolder" << std::endl; QModelIndexList qmil = getSelected(); - model->openSelected(qmil); + model->openSelected(qmil, true); } void SharedFilesDialog::preModDirectories(bool local) { - if (isRemote() == local) - return; + if (isRemote() == local) + return; #ifdef DEBUG_SHARED_FILES_DIALOG std::cerr << "About to modify directories. Local=" << local << ". Temporarily disabling sorting" << std::endl; @@ -895,9 +904,9 @@ void SharedFilesDialog::preModDirectories(bool local) std::set expanded_indexes,hidden_indexes,selected_indexes; saveExpandedPathsAndSelection(expanded_indexes,hidden_indexes,selected_indexes) ; - /* Notify both models, only one is visible */ - tree_model->preMods(); - flat_model->preMods(); + /* Notify both models, only one is visible */ + tree_model->preMods(); + flat_model->preMods(); restoreExpandedPathsAndSelection(expanded_indexes,hidden_indexes,selected_indexes) ; } @@ -966,19 +975,20 @@ void SharedFilesDialog::expandAll() void SharedFilesDialog::recursExpandAll(const QModelIndex& index) { - ui.dirTreeView->setExpanded(index,true) ; + ui.dirTreeView->setExpanded(index,true) ; - for(int row=0;rowmodel()->rowCount(index);++row) - { - QModelIndex idx(index.child(row,0)) ; + for(int row=0;rowmodel()->rowCount(index);++row) + { + QModelIndex idx(index.child(row,0)) ; - if(ui.dirTreeView->model()->rowCount(idx) > 0) - recursExpandAll(idx) ; - } + if(ui.dirTreeView->model()->rowCount(idx) > 0) + recursExpandAll(idx) ; + } } -void SharedFilesDialog::recursSaveExpandedItems(const QModelIndex& index,const std::string& path,std::set& exp, - std::set& vis, +void SharedFilesDialog::recursSaveExpandedItems(const QModelIndex& index,const std::string& path, + std::set& exp, + std::set& hid, std::set& sel ) { @@ -990,11 +1000,12 @@ void SharedFilesDialog::recursSaveExpandedItems(const QModelIndex& index,const s if(ui.dirTreeView->selectionModel()->selection().contains(index)) sel.insert(local_path) ; - if(ui.dirTreeView->isRowHidden(index.row(),index.parent())) - { - vis.insert(local_path) ; - return ; - } + // Disable hidden check as we don't use it and Qt bug: https://bugreports.qt.io/browse/QTBUG-11438 + /*if(ui.dirTreeView->isRowHidden(index.row(),index.parent())) + { + hid.insert(local_path) ; + return ; + }*/ if(ui.dirTreeView->isExpanded(index)) { @@ -1005,7 +1016,11 @@ void SharedFilesDialog::recursSaveExpandedItems(const QModelIndex& index,const s exp.insert(local_path) ; for(int row=0;rowmodel()->rowCount(index);++row) - recursSaveExpandedItems(index.child(row,0),local_path,exp,vis,sel) ; +#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) + recursSaveExpandedItems(ui.dirTreeView->model()->index(row,0,index),local_path,exp,hid,sel) ; +#else + recursSaveExpandedItems(index.child(row,0),local_path,exp,hid,sel) ; +#endif } #ifdef DEBUG_SHARED_FILES_DIALOG else @@ -1015,23 +1030,25 @@ void SharedFilesDialog::recursSaveExpandedItems(const QModelIndex& index,const s void SharedFilesDialog::recursRestoreExpandedItems(const QModelIndex& index, const std::string &path, const std::set& exp, - const std::set& vis, + const std::set& hid, const std::set &sel) { std::string local_path = path+"/"+index.data(Qt::DisplayRole).toString().toStdString(); #ifdef DEBUG_SHARED_FILES_DIALOG std::cerr << "at index " << index.row() << ". data[1]=" << local_path << std::endl; #endif + if(sel.find(local_path) != sel.end()) ui.dirTreeView->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows); - bool invisible = vis.find(local_path) != vis.end(); - ui.dirTreeView->setRowHidden(index.row(),index.parent(),invisible ) ; + // Disable hidden check as we don't use it and Qt bug: https://bugreports.qt.io/browse/QTBUG-11438 + /*bool invisible = hid.find(local_path) != hid.end(); + ui.dirTreeView->setRowHidden(index.row(),index.parent(),invisible ) ;*/ -// if(invisible) -// mHiddenIndexes.push_back(proxyModel->mapToSource(index)); + // if(invisible) + // mHiddenIndexes.push_back(proxyModel->mapToSource(index)); - if(!invisible && exp.find(local_path) != exp.end()) + if(/*!invisible &&*/ exp.find(local_path) != exp.end()) { #ifdef DEBUG_SHARED_FILES_DIALOG std::cerr << "re expanding index " << local_path << std::endl; @@ -1039,15 +1056,19 @@ void SharedFilesDialog::recursRestoreExpandedItems(const QModelIndex& index, con ui.dirTreeView->setExpanded(index,true) ; for(int row=0;rowmodel()->rowCount(index);++row) - recursRestoreExpandedItems(index.child(row,0),local_path,exp,vis,sel) ; +#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) + recursRestoreExpandedItems(ui.dirTreeView->model()->index(row,0,index),local_path,exp,hid,sel) ; +#else + recursRestoreExpandedItems(index.child(row,0),local_path,exp,hid,sel) ; +#endif } } void SharedFilesDialog::postModDirectories(bool local) { - if (isRemote() == local) - return; + if (isRemote() == local) + return; std::set expanded_indexes,selected_indexes,hidden_indexes; @@ -1057,183 +1078,184 @@ void SharedFilesDialog::postModDirectories(bool local) #endif /* Notify both models, only one is visible */ - tree_model->postMods(); - flat_model->postMods(); - ui.dirTreeView->update() ; + tree_model->postMods(); + flat_model->postMods(); + ui.dirTreeView->update() ; - if (ui.filterPatternLineEdit->text().isEmpty() == false) - FilterItems(); + if (ui.filterPatternLineEdit->text().isEmpty() == false) + FilterItems(); - ui.dirTreeView->setSortingEnabled(true); + ui.dirTreeView->setSortingEnabled(true); - restoreExpandedPathsAndSelection(expanded_indexes,hidden_indexes,selected_indexes) ; + restoreExpandedPathsAndSelection(expanded_indexes,hidden_indexes,selected_indexes) ; #ifdef DEBUG_SHARED_FILES_DIALOG std::cerr << "****** updated directories! Re-enabling sorting ******" << std::endl; #endif - QCoreApplication::flush(); + QCoreApplication::flush(); } class ChannelCompare { public: - bool operator()(const std::pair& id1,const std::pair& id2) const - { - return id1.first < id2.first ; - } + bool operator()(const std::pair& id1,const std::pair& id2) const + { + return id1.first < id2.first ; + } }; void LocalSharedFilesDialog::spawnCustomPopupMenu( QPoint point ) { - if (!rsPeers) return; /* not ready yet! */ + if (!rsPeers) return; /* not ready yet! */ - QModelIndex idx = ui.dirTreeView->indexAt(point) ; - if (!idx.isValid()) return ; + QModelIndex idx = ui.dirTreeView->indexAt(point) ; + if (!idx.isValid()) return ; - QModelIndex midx = proxyModel->mapToSource(idx) ; - if (!midx.isValid()) return ; + QModelIndex midx = proxyModel->mapToSource(idx) ; + if (!midx.isValid()) return ; - currentFile = model->data(midx, RetroshareDirModel::FileNameRole).toString() ; - int type = model->getType(midx) ; - if (type != DIR_TYPE_DIR && type != DIR_TYPE_FILE && type != DIR_TYPE_EXTRA_FILE) return; + currentFile = model->data(midx, RetroshareDirModel::FileNameRole).toString() ; + int type = model->getType(midx) ; + if (type != DIR_TYPE_DIR && type != DIR_TYPE_FILE && type != DIR_TYPE_EXTRA_FILE) return; - QMenu contextMnu(this) ; + QMenu contextMnu(this) ; - bool bIsRsColl = currentFile.endsWith(RsCollection::ExtensionString); - collCreateAct->setEnabled(true); - collModifAct->setEnabled(bIsRsColl); - collViewAct->setEnabled(bIsRsColl); - collOpenAct->setEnabled(true); + bool bIsRsColl = currentFile.endsWith(RsCollection::ExtensionString); + collCreateAct->setEnabled(true); + collModifAct->setEnabled(bIsRsColl); + collViewAct->setEnabled(bIsRsColl); + collOpenAct->setEnabled(true); - QMenu collectionMenu(tr("Collection"), this); - collectionMenu.setIcon(QIcon(IMAGE_LIBRARY)); - collectionMenu.addAction(collCreateAct); - collectionMenu.addAction(collModifAct); - collectionMenu.addAction(collViewAct); - collectionMenu.addAction(collOpenAct); + QMenu collectionMenu(tr("Collection"), this); + collectionMenu.setIcon(QIcon(IMAGE_LIBRARY)); + collectionMenu.addAction(collCreateAct); + collectionMenu.addAction(collModifAct); + collectionMenu.addAction(collViewAct); + collectionMenu.addAction(collOpenAct); - switch (type) { - case DIR_TYPE_DIR : - contextMnu.addAction(openfolderAct) ; - contextMnu.addAction(copylinkAct) ; - contextMnu.addSeparator() ;//------------------------------------ - contextMnu.addMenu(&collectionMenu) ; - break ; + switch (type) { + case DIR_TYPE_DIR : + contextMnu.addAction(openfolderAct) ; + contextMnu.addAction(copylinkAct) ; + contextMnu.addSeparator() ;//------------------------------------ + contextMnu.addMenu(&collectionMenu) ; + break ; - case DIR_TYPE_FILE : - contextMnu.addAction(openfileAct) ; - contextMnu.addSeparator() ;//------------------------------------ - contextMnu.addAction(copylinkAct) ; - contextMnu.addAction(sendlinkAct) ; - contextMnu.addSeparator() ;//------------------------------------ - contextMnu.addMenu(&collectionMenu) ; - contextMnu.addSeparator() ;//------------------------------------ - contextMnu.addAction(QIcon(IMAGE_MSG), tr("Recommend in a message to..."), this, SLOT(recommendFilesToMsg())) ; + case DIR_TYPE_FILE : + contextMnu.addAction(openfileAct) ; + contextMnu.addSeparator() ;//------------------------------------ + contextMnu.addAction(copylinkAct) ; + contextMnu.addAction(sendlinkAct) ; + contextMnu.addSeparator() ;//------------------------------------ + contextMnu.addMenu(&collectionMenu) ; + contextMnu.addSeparator() ;//------------------------------------ + contextMnu.addAction(QIcon(IMAGE_MSG), tr("Recommend in a message to..."), this, SLOT(recommendFilesToMsg())) ; break; case DIR_TYPE_EXTRA_FILE: contextMnu.addAction(openfileAct) ; + contextMnu.addAction(openfolderAct) ; contextMnu.addSeparator() ;//------------------------------------ contextMnu.addAction(copylinkAct) ; contextMnu.addAction(sendlinkAct) ; contextMnu.addAction(removeExtraFileAct) ; - break ; + break ; - default : - return ; - } + default : + return ; + } - QMenu shareChannelMenu(tr("Share on channel...")) ; // added here because the shareChannelMenu QMenu object is deleted afterwards - QMenu shareForumMenu(tr("Share on forum...")) ; // added here because the shareChannelMenu QMenu object is deleted afterwards + QMenu shareChannelMenu(tr("Share on channel...")) ; // added here because the shareChannelMenu QMenu object is deleted afterwards + QMenu shareForumMenu(tr("Share on forum...")) ; // added here because the shareChannelMenu QMenu object is deleted afterwards if(type != DIR_TYPE_EXTRA_FILE) - { - GxsChannelDialog *channelDialog = dynamic_cast(MainWindow::getPage(MainWindow::Channels)); + { + GxsChannelDialog *channelDialog = dynamic_cast(MainWindow::getPage(MainWindow::Channels)); - if(channelDialog != NULL) - { - shareChannelMenu.setIcon(QIcon(IMAGE_CHANNEL)); + if(channelDialog != NULL) + { + shareChannelMenu.setIcon(QIcon(IMAGE_CHANNEL)); - std::map grp_metas ; - channelDialog->getGroupList(grp_metas) ; + std::map grp_metas ; + channelDialog->getGroupList(grp_metas) ; - std::vector > grplist ; // I dont use a std::map because two or more channels may have the same name. + std::vector > grplist ; // I dont use a std::map because two or more channels may have the same name. - for(auto it(grp_metas.begin());it!=grp_metas.end();++it) - if(IS_GROUP_PUBLISHER((*it).second.mSubscribeFlags) && IS_GROUP_SUBSCRIBED((*it).second.mSubscribeFlags)) - grplist.push_back(std::make_pair((*it).second.mGroupName, (*it).second.mGroupId)); + for(auto it(grp_metas.begin());it!=grp_metas.end();++it) + if(IS_GROUP_PUBLISHER((*it).second.mSubscribeFlags) && IS_GROUP_SUBSCRIBED((*it).second.mSubscribeFlags)) + grplist.push_back(std::make_pair((*it).second.mGroupName, (*it).second.mGroupId)); - std::sort(grplist.begin(),grplist.end(),ChannelCompare()) ; + std::sort(grplist.begin(),grplist.end(),ChannelCompare()) ; - for(auto it(grplist.begin());it!=grplist.end();++it) - shareChannelMenu.addAction(QString::fromUtf8((*it).first.c_str()), this, SLOT(shareOnChannel()))->setData(QString::fromStdString((*it).second.toStdString())) ; + for(auto it(grplist.begin());it!=grplist.end();++it) + shareChannelMenu.addAction(QString::fromUtf8((*it).first.c_str()), this, SLOT(shareOnChannel()))->setData(QString::fromStdString((*it).second.toStdString())) ; - contextMnu.addMenu(&shareChannelMenu) ; - } + contextMnu.addMenu(&shareChannelMenu) ; + } - GxsForumsDialog *forumsDialog = dynamic_cast(MainWindow::getPage(MainWindow::Forums)); + GxsForumsDialog *forumsDialog = dynamic_cast(MainWindow::getPage(MainWindow::Forums)); - if(forumsDialog != NULL) - { - shareForumMenu.setIcon(QIcon(IMAGE_FORUMS)); + if(forumsDialog != NULL) + { + shareForumMenu.setIcon(QIcon(IMAGE_FORUMS)); - std::map grp_metas ; - forumsDialog->getGroupList(grp_metas) ; + std::map grp_metas ; + forumsDialog->getGroupList(grp_metas) ; - std::vector > grplist ; // I dont use a std::map because two or more channels may have the same name. + std::vector > grplist ; // I dont use a std::map because two or more channels may have the same name. - for(auto it(grp_metas.begin());it!=grp_metas.end();++it) - if(IS_GROUP_SUBSCRIBED((*it).second.mSubscribeFlags)) - grplist.push_back(std::make_pair((*it).second.mGroupName, (*it).second.mGroupId)); + for(auto it(grp_metas.begin());it!=grp_metas.end();++it) + if(IS_GROUP_SUBSCRIBED((*it).second.mSubscribeFlags)) + grplist.push_back(std::make_pair((*it).second.mGroupName, (*it).second.mGroupId)); - std::sort(grplist.begin(),grplist.end(),ChannelCompare()) ; + std::sort(grplist.begin(),grplist.end(),ChannelCompare()) ; - for(auto it(grplist.begin());it!=grplist.end();++it) - shareForumMenu.addAction(QString::fromUtf8((*it).first.c_str()), this, SLOT(shareInForum()))->setData(QString::fromStdString((*it).second.toStdString())) ; + for(auto it(grplist.begin());it!=grplist.end();++it) + shareForumMenu.addAction(QString::fromUtf8((*it).first.c_str()), this, SLOT(shareInForum()))->setData(QString::fromStdString((*it).second.toStdString())) ; - contextMnu.addMenu(&shareForumMenu) ; - } - } + contextMnu.addMenu(&shareForumMenu) ; + } + } - contextMnu.exec(QCursor::pos()) ; + contextMnu.exec(QCursor::pos()) ; } void LocalSharedFilesDialog::shareOnChannel() { - RsGxsGroupId groupId(qobject_cast(sender())->data().toString().toStdString()); + RsGxsGroupId groupId(qobject_cast(sender())->data().toString().toStdString()); - GxsChannelDialog *channelDialog = dynamic_cast(MainWindow::getPage(MainWindow::Channels)); + GxsChannelDialog *channelDialog = dynamic_cast(MainWindow::getPage(MainWindow::Channels)); - if(channelDialog == NULL) - return ; + if(channelDialog == NULL) + return ; - std::list files_info ; + std::list files_info ; - QList file_links_list ; - bool has_unhashed_files ; + QList file_links_list ; + bool has_unhashed_files ; - copyLinks(getSelected(),false,file_links_list,has_unhashed_files) ; + copyLinks(getSelected(),false,file_links_list,has_unhashed_files) ; - channelDialog->shareOnChannel(groupId,file_links_list) ; + channelDialog->shareOnChannel(groupId,file_links_list) ; } void LocalSharedFilesDialog::shareInForum() { - RsGxsGroupId groupId(qobject_cast(sender())->data().toString().toStdString()); + RsGxsGroupId groupId(qobject_cast(sender())->data().toString().toStdString()); - GxsForumsDialog *forumsDialog = dynamic_cast(MainWindow::getPage(MainWindow::Forums)); + GxsForumsDialog *forumsDialog = dynamic_cast(MainWindow::getPage(MainWindow::Forums)); - if(forumsDialog == NULL) - return ; + if(forumsDialog == NULL) + return ; - std::list files_info ; + std::list files_info ; - QList file_links_list ; - bool has_unhashed_files ; + QList file_links_list ; + bool has_unhashed_files ; - copyLinks(getSelected(),false,file_links_list,has_unhashed_files) ; + copyLinks(getSelected(),false,file_links_list,has_unhashed_files) ; - forumsDialog->shareInMessage(groupId,file_links_list) ; + forumsDialog->shareInMessage(groupId,file_links_list) ; } //============================================================================ @@ -1299,90 +1321,90 @@ LocalSharedFilesDialog::tryToAddNewAssotiation() void SharedFilesDialog::indicatorChanged(int index) { - static uint32_t correct_indicator[4] = { IND_ALWAYS,IND_LAST_DAY,IND_LAST_WEEK,IND_LAST_MONTH } ; + static uint32_t correct_indicator[4] = { IND_ALWAYS,IND_LAST_DAY,IND_LAST_WEEK,IND_LAST_MONTH } ; - model->changeAgeIndicator(correct_indicator[index]); + model->changeAgeIndicator(correct_indicator[index]); - ui.dirTreeView->update(ui.dirTreeView->rootIndex()); + ui.dirTreeView->update(ui.dirTreeView->rootIndex()); - if (correct_indicator[index] != IND_ALWAYS) - ui.dirTreeView->sortByColumn(COLUMN_AGE, Qt::AscendingOrder); - else - ui.dirTreeView->sortByColumn(COLUMN_NAME, Qt::AscendingOrder); + if (correct_indicator[index] != IND_ALWAYS) + ui.dirTreeView->sortByColumn(COLUMN_AGE, Qt::AscendingOrder); + else + ui.dirTreeView->sortByColumn(COLUMN_NAME, Qt::AscendingOrder); - updateDisplay() ; + updateDisplay() ; } void SharedFilesDialog::onFilterTextEdited() { - QString text = ui.filterPatternLineEdit->text(); + QString text = ui.filterPatternLineEdit->text(); - if (text.isEmpty()) { - ui.filterClearButton->hide(); - } else { - ui.filterClearButton->show(); - } + if (text.isEmpty()) { + ui.filterClearButton->hide(); + } else { + ui.filterClearButton->show(); + } - if (text == lastFilterString) { - ui.filterStartButton->hide(); - } else { - ui.filterStartButton->show(); - } + if (text == lastFilterString) { + ui.filterStartButton->hide(); + } else { + ui.filterStartButton->show(); + } - if(text.length() > 0 && text.length() < 3) - { - ui.filterStartButton->setEnabled(false) ; - ui.filterPatternFrame->setToolTip(tr("Search string should be at least 3 characters long.")) ; - return ; - } + if(text.length() > 0 && text.length() < 3) + { + ui.filterStartButton->setEnabled(false) ; + ui.filterPatternFrame->setToolTip(tr("Search string should be at least 3 characters long.")) ; + return ; + } - ui.filterStartButton->setEnabled(true) ; - ui.filterPatternFrame->setToolTip(QString()); + ui.filterStartButton->setEnabled(true) ; + ui.filterPatternFrame->setToolTip(QString()); - //FilterItems(); + //FilterItems(); #ifndef DISABLE_SEARCH_WHILE_TYPING - mFilterTimer->start( 500 ); // This will fire filterRegExpChanged after 500 ms. - // If the user types something before it fires, the timer restarts counting + mFilterTimer->start( 500 ); // This will fire filterRegExpChanged after 500 ms. + // If the user types something before it fires, the timer restarts counting #endif } #ifdef DEPRECATED_CODE void SharedFilesDialog::filterRegExpChanged() { - QString text = ui.filterPatternLineEdit->text(); + QString text = ui.filterPatternLineEdit->text(); - if(text.length() > 0 && proxyModel == tree_proxyModel) - { - std::list result_list ; - std::list keywords; + if(text.length() > 0 && proxyModel == tree_proxyModel) + { + std::list result_list ; + std::list keywords; - QStringList lst = text.split(" ",QString::SkipEmptyParts) ; + QStringList lst = text.split(" ",QString::SkipEmptyParts) ; - for(auto it(lst.begin());it!=lst.end();++it) - keywords.push_back((*it).toStdString()); + for(auto it(lst.begin());it!=lst.end();++it) + keywords.push_back((*it).toStdString()); - FileSearchFlags flags = isRemote()?RS_FILE_HINTS_REMOTE:RS_FILE_HINTS_LOCAL; + FileSearchFlags flags = isRemote()?RS_FILE_HINTS_REMOTE:RS_FILE_HINTS_LOCAL; - if(keywords.size() > 1) - { - RsRegularExpression::NameExpression exp(RsRegularExpression::ContainsAllStrings,keywords,true); - rsFiles->SearchBoolExp(&exp,result_list, flags) ; - } - else - rsFiles->SearchKeywords(keywords,result_list, flags) ; + if(keywords.size() > 1) + { + RsRegularExpression::NameExpression exp(RsRegularExpression::ContainsAllStrings,keywords,true); + rsFiles->SearchBoolExp(&exp,result_list, flags) ; + } + else + rsFiles->SearchKeywords(keywords,result_list, flags) ; - uint32_t nb_results = result_list.size(); + uint32_t nb_results = result_list.size(); - if(nb_results > MAX_SEARCH_RESULTS) - { - ui.filterStartButton->setEnabled(false) ; - ui.filterPatternFrame->setToolTip(tr("More than 3000 results. Add more/longer search words to select less.")) ; - return ; - } - } + if(nb_results > MAX_SEARCH_RESULTS) + { + ui.filterStartButton->setEnabled(false) ; + ui.filterPatternFrame->setToolTip(tr("More than 3000 results. Add more/longer search words to select less.")) ; + return ; + } + } - ui.filterStartButton->setEnabled(true) ; - ui.filterPatternFrame->setToolTip(QString()); + ui.filterStartButton->setEnabled(true) ; + ui.filterPatternFrame->setToolTip(QString()); } #endif @@ -1398,25 +1420,25 @@ void SharedFilesDialog::clearFilter() /* clear Filter */ void SharedFilesDialog::startFilter() { - ui.filterStartButton->hide(); - lastFilterString = ui.filterPatternLineEdit->text(); + ui.filterStartButton->hide(); + lastFilterString = ui.filterPatternLineEdit->text(); - FilterItems(); + FilterItems(); } void SharedFilesDialog::updateDirTreeView() { - if (model == flat_model) - { - size_t maxSize = 0; - FlatStyle_RDM* flat = dynamic_cast(flat_model); - if (flat && flat->isMaxRefsTableSize(&maxSize)) - { - ui.dirTreeView->setToolTip(tr("Warning: You reach max (%1) files in flat list. No more will be added.").arg(maxSize)); - return; - } - } - ui.dirTreeView->setToolTip(""); + if (model == flat_model) + { + size_t maxSize = 0; + FlatStyle_RDM* flat = dynamic_cast(flat_model); + if (flat && flat->isMaxRefsTableSize(&maxSize)) + { + ui.dirTreeView->setToolTip(tr("Warning: You reach max (%1) files in flat list. No more will be added.").arg(maxSize)); + return; + } + } + ui.dirTreeView->setToolTip(""); } //#define DEBUG_SHARED_FILES_DIALOG @@ -1430,90 +1452,90 @@ void SharedFilesDialog::updateDirTreeView() void recursMakeVisible(QTreeView *tree,const QSortFilterProxyModel *proxyModel,const QModelIndex& indx,uint32_t depth,const std::vector >& pointers,QList& hidden_list) { #ifdef DEBUG_SHARED_FILES_DIALOG - for(uint32_t i=0;isetRowHidden(indx.row(), indx.parent(), false) ; + tree->setRowHidden(indx.row(), indx.parent(), false) ; #ifdef EXPAND_WHILE_SEARCHING - tree->setExpanded(indx,true) ; + tree->setExpanded(indx,true) ; #endif - bool found = false ; + bool found = false ; for (int row = 0; row < rowCount; ++row) - { - QModelIndex child_index = indx.child(row,0); + { + QModelIndex child_index = indx.child(row,0); - if(ptrs.find(proxyModel->mapToSource(child_index).internalPointer()) != ptrs.end()) - { + if(ptrs.find(proxyModel->mapToSource(child_index).internalPointer()) != ptrs.end()) + { #ifdef DEBUG_SHARED_FILES_DIALOG - for(uint32_t i=0;imapToSource(child_index).internalPointer() << " visible" << std::endl; + for(uint32_t i=0;imapToSource(child_index).internalPointer() << " visible" << std::endl; #endif - recursMakeVisible(tree,proxyModel,child_index,depth+1,pointers,hidden_list) ; - found = true ; - } - else - { - tree->setRowHidden(child_index.row(), indx, true) ; - hidden_list.push_back(proxyModel->mapToSource(child_index)) ; + recursMakeVisible(tree,proxyModel,child_index,depth+1,pointers,hidden_list) ; + found = true ; + } + else + { + tree->setRowHidden(child_index.row(), indx, true) ; + hidden_list.push_back(proxyModel->mapToSource(child_index)) ; #ifdef EXPAND_WHILE_SEARCHING - tree->setExpanded(child_index,false) ; + tree->setExpanded(child_index,false) ; #endif #ifdef DEBUG_SHARED_FILES_DIALOG - for(uint32_t i=0;imapToSource(child_index).internalPointer() << " hidden" << std::endl; + for(uint32_t i=0;imapToSource(child_index).internalPointer() << " hidden" << std::endl; #endif - } - } + } + } - if(!found && depth == 0) - { - tree->setRowHidden(indx.row(), indx.parent(), true) ; - hidden_list.push_back(proxyModel->mapToSource(indx)) ; - } + if(!found && depth == 0) + { + tree->setRowHidden(indx.row(), indx.parent(), true) ; + hidden_list.push_back(proxyModel->mapToSource(indx)) ; + } } void SharedFilesDialog::restoreInvisibleItems() { - std::cerr << "Restoring " << mHiddenIndexes.size() << " invisible indexes" << std::endl; + std::cerr << "Restoring " << mHiddenIndexes.size() << " invisible indexes" << std::endl; - for(QList::const_iterator it(mHiddenIndexes.begin());it!=mHiddenIndexes.end();++it) - { - QModelIndex indx = proxyModel->mapFromSource(*it); + for(QList::const_iterator it(mHiddenIndexes.begin());it!=mHiddenIndexes.end();++it) + { + QModelIndex indx = proxyModel->mapFromSource(*it); - if(indx.isValid()) - ui.dirTreeView->setRowHidden(indx.row(), indx.parent(), false) ; - } + if(indx.isValid()) + ui.dirTreeView->setRowHidden(indx.row(), indx.parent(), false) ; + } - mHiddenIndexes.clear(); + mHiddenIndexes.clear(); } #endif class QCursorContextBlocker { - public: - QCursorContextBlocker(QWidget *w) - : mW(w) - { - mW->setCursor(Qt::WaitCursor); - mW->blockSignals(true) ; - } + public: + QCursorContextBlocker(QWidget *w) + : mW(w) + { + mW->setCursor(Qt::WaitCursor); + mW->blockSignals(true) ; + } - ~QCursorContextBlocker() - { - mW->setCursor(Qt::ArrowCursor); - mW->blockSignals(false) ; - } + ~QCursorContextBlocker() + { + mW->setCursor(Qt::ArrowCursor); + mW->blockSignals(false) ; + } - private: - QWidget *mW ; + private: + QWidget *mW ; }; void SharedFilesDialog::FilterItems() @@ -1523,68 +1545,68 @@ void SharedFilesDialog::FilterItems() return; #endif - QString text = ui.filterPatternLineEdit->text(); + QString text = ui.filterPatternLineEdit->text(); - if(mLastFilterText == text) // do not filter again if we already did. This is an optimization - { + if(mLastFilterText == text) // do not filter again if we already did. This is an optimization + { #ifdef DEBUG_SHARED_FILES_DIALOG - std::cerr << "Last text is equal to text. skipping" << std::endl; + std::cerr << "Last text is equal to text. skipping" << std::endl; #endif - return ; - } + return ; + } #ifdef DEBUG_SHARED_FILES_DIALOG - std::cerr << "New last text. Performing the filter on string \"" << text.toStdString() << "\"" << std::endl; + std::cerr << "New last text. Performing the filter on string \"" << text.toStdString() << "\"" << std::endl; #endif - mLastFilterText = text ; + mLastFilterText = text ; - QCursorContextBlocker q(ui.dirTreeView) ; + QCursorContextBlocker q(ui.dirTreeView) ; - QCoreApplication::processEvents() ; + QCoreApplication::processEvents() ; - std::list result_list ; - uint32_t found = 0 ; + std::list result_list ; + uint32_t found = 0 ; - if(text == "") - { - model->filterItems(std::list(),found) ; - model->update() ; - return ; - } + if(text == "") + { + model->filterItems(std::list(),found) ; + model->update() ; + return ; + } - if(text.length() < 3) - return ; + if(text.length() < 3) + return ; - //FileSearchFlags flags = isRemote()?RS_FILE_HINTS_REMOTE:RS_FILE_HINTS_LOCAL; - QStringList lst = text.split(" ",QString::SkipEmptyParts) ; - std::list keywords ; + //FileSearchFlags flags = isRemote()?RS_FILE_HINTS_REMOTE:RS_FILE_HINTS_LOCAL; + QStringList lst = text.split(" ",QString::SkipEmptyParts) ; + std::list keywords ; - for(auto it(lst.begin());it!=lst.end();++it) - keywords.push_back((*it).toStdString()); + for(auto it(lst.begin());it!=lst.end();++it) + keywords.push_back((*it).toStdString()); - model->filterItems(keywords,found) ; - model->update() ; + model->filterItems(keywords,found) ; + model->update() ; - if(found > 0) - expandAll(); + if(found > 0) + expandAll(); - if(found == 0) - ui.filterPatternFrame->setToolTip(tr("No result.")) ; - else if(found > MAX_SEARCH_RESULTS) - ui.filterPatternFrame->setToolTip(tr("More than %1 results. Add more/longer search words to select less.").arg(MAX_SEARCH_RESULTS)) ; - else - ui.filterPatternFrame->setToolTip(tr("Found %1 results.").arg(found)) ; + if(found == 0) + ui.filterPatternFrame->setToolTip(tr("No result.")) ; + else if(found > MAX_SEARCH_RESULTS) + ui.filterPatternFrame->setToolTip(tr("More than %1 results. Add more/longer search words to select less.").arg(MAX_SEARCH_RESULTS)) ; + else + ui.filterPatternFrame->setToolTip(tr("Found %1 results.").arg(found)) ; #ifdef DEBUG_SHARED_FILES_DIALOG - std::cerr << found << " results found by search." << std::endl; + std::cerr << found << " results found by search." << std::endl; #endif } void SharedFilesDialog::removeExtraFile() { - std::list files_info ; + std::list files_info ; - model->getFileInfoFromIndexList(getSelected(),files_info); + model->getFileInfoFromIndexList(getSelected(),files_info); for(auto it(files_info.begin());it!=files_info.end();++it) { @@ -1597,17 +1619,17 @@ void SharedFilesDialog::removeExtraFile() #ifdef DEPRECATED_CODE bool SharedFilesDialog::flat_FilterItem(const QModelIndex &index, const QString &text, int /*level*/) { - if(index.data(RetroshareDirModel::FileNameRole).toString().contains(text, Qt::CaseInsensitive)) - { - ui.dirTreeView->setRowHidden(index.row(), index.parent(), false); - return false ; - } - else - { - ui.dirTreeView->setRowHidden(index.row(), index.parent(), true); - mHiddenIndexes.push_back(proxyModel->mapToSource(index)); - return true ; - } + if(index.data(RetroshareDirModel::FileNameRole).toString().contains(text, Qt::CaseInsensitive)) + { + ui.dirTreeView->setRowHidden(index.row(), index.parent(), false); + return false ; + } + else + { + ui.dirTreeView->setRowHidden(index.row(), index.parent(), true); + mHiddenIndexes.push_back(proxyModel->mapToSource(index)); + return true ; + } } bool SharedFilesDialog::tree_FilterItem(const QModelIndex &index, const QString &text, int level) @@ -1636,10 +1658,10 @@ bool SharedFilesDialog::tree_FilterItem(const QModelIndex &index, const QString if (visible || visibleChildCount) { ui.dirTreeView->setRowHidden(index.row(), index.parent(), false); } else { - { + { ui.dirTreeView->setRowHidden(index.row(), index.parent(), true); - mHiddenIndexes.push_back(proxyModel->mapToSource(index)); - } + mHiddenIndexes.push_back(proxyModel->mapToSource(index)); + } } return (visible || visibleChildCount); diff --git a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h index 5206e1d06..c406821c9 100644 --- a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h +++ b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h @@ -23,7 +23,7 @@ #include "ui_SharedFilesDialog.h" -#include "RsAutoUpdatePage.h" +#include #include "gui/RetroShareLink.h" #include "util/RsProtectedTimer.h" @@ -38,10 +38,10 @@ class SharedFilesDialog : public RsAutoUpdatePage public: /** Default Constructor */ - SharedFilesDialog(RetroshareDirModel *tree_model,RetroshareDirModel *flat_model,QWidget *parent = 0); + SharedFilesDialog(bool remote_mode,QWidget *parent = 0); /** Default Destructor */ - ~SharedFilesDialog() {} + ~SharedFilesDialog() ; virtual void hideEvent(QHideEvent *) ; virtual void showEvent(QShowEvent *) ; diff --git a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.ui b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.ui index 68e3446b6..b1f4ceb9b 100644 --- a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.ui +++ b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.ui @@ -88,10 +88,6 @@ Configure shared directories - - - :/images/add-share24.png:/images/add-share24.png - diff --git a/retroshare-gui/src/gui/FileTransfer/TransferUserNotify.h b/retroshare-gui/src/gui/FileTransfer/TransferUserNotify.h index f883ed33c..06d352487 100644 --- a/retroshare-gui/src/gui/FileTransfer/TransferUserNotify.h +++ b/retroshare-gui/src/gui/FileTransfer/TransferUserNotify.h @@ -31,6 +31,7 @@ public: TransferUserNotify(QObject *parent = 0); virtual bool hasSetting(QString *name, QString *group); + virtual QString textInfo() const override { return tr("completed transfer(s)"); } private: virtual QIcon getIcon(); diff --git a/retroshare-gui/src/gui/FileTransfer/TransfersDialog.cpp b/retroshare-gui/src/gui/FileTransfer/TransfersDialog.cpp index 22486fd2c..5b0f0c0d4 100644 --- a/retroshare-gui/src/gui/FileTransfer/TransfersDialog.cpp +++ b/retroshare-gui/src/gui/FileTransfer/TransfersDialog.cpp @@ -81,11 +81,11 @@ #define IMAGE_SEARCH ":/icons/svg/magnifying-glass.svg" #define IMAGE_EXPAND ":/images/edit_add24.png" #define IMAGE_COLLAPSE ":/images/edit_remove24.png" -#define IMAGE_LIBRARY ":/images/library.png" -#define IMAGE_COLLCREATE ":/images/library_add.png" -#define IMAGE_COLLMODIF ":/images/library_edit.png" -#define IMAGE_COLLVIEW ":/images/library_view.png" -#define IMAGE_COLLOPEN ":/images/library.png" +#define IMAGE_LIBRARY ":/icons/collections.png" +#define IMAGE_COLLCREATE ":/iconss/png/add.png" +#define IMAGE_COLLMODIF ":/icons/png/pencil-edit-button.png" +#define IMAGE_COLLVIEW ":/images/find.png" +#define IMAGE_COLLOPEN ":/icons/collections.png" #define IMAGE_FRIENDSFILES ":/icons/svg/folders.svg" #define IMAGE_MYFILES ":icons/svg/folders1.svg" #define IMAGE_RENAMEFILE ":images/filecomments.png" @@ -594,7 +594,7 @@ public: if(col == COLUMN_NAME) { if(source_id == -1) - return QVariant(FilesDefs::getIconFromFilename(QString::fromUtf8(fileInfo.fname.c_str()))); + return QVariant(FilesDefs::getIconFromFileType(QString::fromUtf8(fileInfo.fname.c_str()))); else { QString iconName,tooltip; @@ -919,10 +919,10 @@ TransfersDialog::TransfersDialog(QWidget *parent) QObject::connect(ui.downloadList->selectionModel(),SIGNAL(selectionChanged (const QItemSelection&, const QItemSelection&)),this,SLOT(showFileDetails())) ; - ui.tabWidget->insertTab(2,searchDialog = new SearchDialog(), QIcon(IMAGE_SEARCH), tr("Search")) ; - ui.tabWidget->insertTab(3,remoteSharedFiles = new RemoteSharedFilesDialog(), QIcon(IMAGE_FRIENDSFILES), tr("Friends files")) ; + ui.tabWidget->insertTab(2,searchDialog = new SearchDialog(), FilesDefs::getIconFromQtResourcePath(IMAGE_SEARCH), tr("Search")) ; + ui.tabWidget->insertTab(3,remoteSharedFiles = new RemoteSharedFilesDialog(), FilesDefs::getIconFromQtResourcePath(IMAGE_FRIENDSFILES), tr("Friends files")) ; - ui.tabWidget->addTab(localSharedFiles = new LocalSharedFilesDialog(), QIcon(IMAGE_MYFILES), tr("My files")) ; + ui.tabWidget->addTab(localSharedFiles = new LocalSharedFilesDialog(), FilesDefs::getIconFromQtResourcePath(IMAGE_MYFILES), tr("My files")) ; for(int i=0;inbPlugins();++i) if(rsPlugins->plugin(i) != NULL && rsPlugins->plugin(i)->qt_transfers_tab() != NULL) @@ -933,79 +933,79 @@ TransfersDialog::TransfersDialog(QWidget *parent) /** Setup the actions for the context menu */ // Actions. Only need to be defined once. - pauseAct = new QAction(QIcon(IMAGE_PAUSE), tr("Pause"), this); + pauseAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_PAUSE), tr("Pause"), this); connect(pauseAct, SIGNAL(triggered()), this, SLOT(pauseFileTransfer())); - resumeAct = new QAction(QIcon(IMAGE_RESUME), tr("Resume"), this); + resumeAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_RESUME), tr("Resume"), this); connect(resumeAct, SIGNAL(triggered()), this, SLOT(resumeFileTransfer())); - forceCheckAct = new QAction(QIcon(IMAGE_CANCEL), tr( "Force Check" ), this ); + forceCheckAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_CANCEL), tr( "Force Check" ), this ); connect( forceCheckAct , SIGNAL( triggered() ), this, SLOT( forceCheck() ) ); - cancelAct = new QAction(QIcon(IMAGE_CANCEL), tr( "Cancel" ), this ); + cancelAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_CANCEL), tr( "Cancel" ), this ); connect( cancelAct , SIGNAL( triggered() ), this, SLOT( cancel() ) ); - openFolderAct = new QAction(QIcon(IMAGE_OPENFOLDER), tr("Open Folder"), this); + openFolderAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_OPENFOLDER), tr("Open Folder"), this); connect(openFolderAct, SIGNAL(triggered()), this, SLOT(dlOpenFolder())); - openFileAct = new QAction(QIcon(IMAGE_OPENFILE), tr("Open File"), this); + openFileAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_OPENFILE), tr("Open File"), this); connect(openFileAct, SIGNAL(triggered()), this, SLOT(dlOpenFile())); - previewFileAct = new QAction(QIcon(IMAGE_PREVIEW), tr("Preview File"), this); + previewFileAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_PREVIEW), tr("Preview File"), this); connect(previewFileAct, SIGNAL(triggered()), this, SLOT(dlPreviewFile())); - detailsFileAct = new QAction(QIcon(IMAGE_INFO), tr("Details..."), this); + detailsFileAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_INFO), tr("Details..."), this); connect(detailsFileAct, SIGNAL(triggered()), this, SLOT(showDetailsDialog())); - clearCompletedAct = new QAction(QIcon(IMAGE_CLEARCOMPLETED), tr( "Clear Completed" ), this ); + clearCompletedAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_CLEARCOMPLETED), tr( "Clear Completed" ), this ); connect( clearCompletedAct , SIGNAL( triggered() ), this, SLOT( clearcompleted() ) ); - copyLinkAct = new QAction(QIcon(IMAGE_COPYLINK), tr( "Copy RetroShare Link" ), this ); + copyLinkAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COPYLINK), tr( "Copy RetroShare Link" ), this ); connect( copyLinkAct , SIGNAL( triggered() ), this, SLOT( dlCopyLink() ) ); - pasteLinkAct = new QAction(QIcon(IMAGE_PASTELINK), tr( "Paste RetroShare Link" ), this ); + pasteLinkAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_PASTELINK), tr( "Paste RetroShare Link" ), this ); connect( pasteLinkAct , SIGNAL( triggered() ), this, SLOT( pasteLink() ) ); - queueDownAct = new QAction(QIcon(":/images/go-down.png"), tr("Down"), this); + queueDownAct = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/go-down.png"), tr("Down"), this); connect(queueDownAct, SIGNAL(triggered()), this, SLOT(priorityQueueDown())); - queueUpAct = new QAction(QIcon(":/images/go-up.png"), tr("Up"), this); + queueUpAct = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/go-up.png"), tr("Up"), this); connect(queueUpAct, SIGNAL(triggered()), this, SLOT(priorityQueueUp())); - queueTopAct = new QAction(QIcon(":/images/go-top.png"), tr("Top"), this); + queueTopAct = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/go-top.png"), tr("Top"), this); connect(queueTopAct, SIGNAL(triggered()), this, SLOT(priorityQueueTop())); - queueBottomAct = new QAction(QIcon(":/images/go-bottom.png"), tr("Bottom"), this); + queueBottomAct = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/go-bottom.png"), tr("Bottom"), this); connect(queueBottomAct, SIGNAL(triggered()), this, SLOT(priorityQueueBottom())); - chunkStreamingAct = new QAction(QIcon(IMAGE_STREAMING), tr("Streaming"), this); + chunkStreamingAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_STREAMING), tr("Streaming"), this); connect(chunkStreamingAct, SIGNAL(triggered()), this, SLOT(chunkStreaming())); - prioritySlowAct = new QAction(QIcon(IMAGE_PRIORITYLOW), tr("Slower"), this); + prioritySlowAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_PRIORITYLOW), tr("Slower"), this); connect(prioritySlowAct, SIGNAL(triggered()), this, SLOT(speedSlow())); - priorityMediumAct = new QAction(QIcon(IMAGE_PRIORITYNORMAL), tr("Average"), this); + priorityMediumAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_PRIORITYNORMAL), tr("Average"), this); connect(priorityMediumAct, SIGNAL(triggered()), this, SLOT(speedAverage())); - priorityFastAct = new QAction(QIcon(IMAGE_PRIORITYHIGH), tr("Faster"), this); + priorityFastAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_PRIORITYHIGH), tr("Faster"), this); connect(priorityFastAct, SIGNAL(triggered()), this, SLOT(speedFast())); - chunkRandomAct = new QAction(QIcon(IMAGE_PRIORITYAUTO), tr("Random"), this); + chunkRandomAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_PRIORITYAUTO), tr("Random"), this); connect(chunkRandomAct, SIGNAL(triggered()), this, SLOT(chunkRandom())); - chunkProgressiveAct = new QAction(QIcon(IMAGE_PRIORITYAUTO), tr("Progressive"), this); + chunkProgressiveAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_PRIORITYAUTO), tr("Progressive"), this); connect(chunkProgressiveAct, SIGNAL(triggered()), this, SLOT(chunkProgressive())); - playAct = new QAction(QIcon(IMAGE_PLAY), tr( "Play" ), this ); + playAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_PLAY), tr( "Play" ), this ); connect( playAct , SIGNAL( triggered() ), this, SLOT( dlOpenFile() ) ); - renameFileAct = new QAction(QIcon(IMAGE_RENAMEFILE), tr("Rename file..."), this); + renameFileAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_RENAMEFILE), tr("Rename file..."), this); connect(renameFileAct, SIGNAL(triggered()), this, SLOT(renameFile())); - specifyDestinationDirectoryAct = new QAction(QIcon(IMAGE_SEARCH),tr("Specify..."),this) ; + specifyDestinationDirectoryAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_SEARCH),tr("Specify..."),this) ; connect(specifyDestinationDirectoryAct,SIGNAL(triggered()),this,SLOT(chooseDestinationDirectory())); - expandAllDLAct= new QAction(QIcon(IMAGE_EXPAND),tr("Expand all"),this); + expandAllDLAct= new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_EXPAND),tr("Expand all"),this); connect(expandAllDLAct,SIGNAL(triggered()),this,SLOT(expandAllDL())); - collapseAllDLAct= new QAction(QIcon(IMAGE_COLLAPSE),tr("Collapse all"),this); + collapseAllDLAct= new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COLLAPSE),tr("Collapse all"),this); connect(collapseAllDLAct,SIGNAL(triggered()),this,SLOT(collapseAllDL())); - expandAllULAct= new QAction(QIcon(IMAGE_EXPAND),tr("Expand all"),this); + expandAllULAct= new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_EXPAND),tr("Expand all"),this); connect(expandAllULAct,SIGNAL(triggered()),this,SLOT(expandAllUL())); - collapseAllULAct= new QAction(QIcon(IMAGE_COLLAPSE),tr("Collapse all"),this); + collapseAllULAct= new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COLLAPSE),tr("Collapse all"),this); connect(collapseAllULAct,SIGNAL(triggered()),this,SLOT(collapseAllUL())); - collCreateAct= new QAction(QIcon(IMAGE_COLLCREATE), tr("Create Collection..."), this); + collCreateAct= new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COLLCREATE), tr("Create Collection..."), this); connect(collCreateAct,SIGNAL(triggered()),this,SLOT(collCreate())); - collModifAct= new QAction(QIcon(IMAGE_COLLMODIF), tr("Modify Collection..."), this); + collModifAct= new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COLLMODIF), tr("Modify Collection..."), this); connect(collModifAct,SIGNAL(triggered()),this,SLOT(collModif())); - collViewAct= new QAction(QIcon(IMAGE_COLLVIEW), tr("View Collection..."), this); + collViewAct= new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COLLVIEW), tr("View Collection..."), this); connect(collViewAct,SIGNAL(triggered()),this,SLOT(collView())); - collOpenAct = new QAction(QIcon(IMAGE_COLLOPEN), tr( "Download from collection file..." ), this ); + collOpenAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COLLOPEN), tr( "Download from collection file..." ), this ); connect(collOpenAct, SIGNAL(triggered()), this, SLOT(collOpen())); connect(NotifyQt::getInstance(), SIGNAL(downloadComplete(QString)), this, SLOT(collAutoOpen(QString))); @@ -1068,38 +1068,44 @@ TransfersDialog::TransfersDialog(QWidget *parent) connect(showULHashAct,SIGNAL(triggered(bool)),this,SLOT(setShowULHashColumn(bool))) ; /** Setup the actions for the upload context menu */ - ulOpenFolderAct = new QAction(QIcon(IMAGE_OPENFOLDER), tr("Open Folder"), this); + ulOpenFolderAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_OPENFOLDER), tr("Open Folder"), this); connect(ulOpenFolderAct, SIGNAL(triggered()), this, SLOT(ulOpenFolder())); - ulCopyLinkAct = new QAction(QIcon(IMAGE_COPYLINK), tr( "Copy RetroShare Link" ), this ); + ulCopyLinkAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COPYLINK), tr( "Copy RetroShare Link" ), this ); connect( ulCopyLinkAct , SIGNAL( triggered() ), this, SLOT( ulCopyLink() ) ); // load settings processSettings(true); - int S = QFontMetricsF(font()).height(); - QString help_str = tr( - "

  File Transfer

\ -

Retroshare brings two ways of transferring files: direct transfers from your friends, and \ - distant anonymous tunnelled transfers. In addition, file transfer is multi-source and allows swarming \ - (you can be a source while downloading)

\ -

You can share files using the icon from the left side bar. \ - These files will be listed in the My Files tab. You can decide for each friend group whether they can or not see these files \ - in their Friends Files tab

\ -

The search tab reports files from your friends' file lists, and distant files that can be reached \ - anonymously using the multi-hop tunnelling system.

\ - ").arg(QString::number(2*S)).arg(QString::number(S)) ; + int S = static_cast(QFontMetricsF(font()).height()); + QString help_str = tr( + "

  " + "File Transfer

" + "

Retroshare brings two ways of transferring files: direct " + "transfers from your friends, and distant anonymous tunnelled " + "transfers. In addition, file transfer is multi-source and " + "allows swarming (you can be a source while downloading)

" + "

You can share files using the " + "" + " icon from the left side bar. These files will be listed in " + "the My Files tab. You can decide for each friend group whether" + " they can or not see these files in their Friends Files tab

" + "

The search tab reports files from your friends' file lists," + " and distant files that can be reached anonymously using the " + "multi-hop tunnelling system.

") + .arg(QString::number(2*S)).arg(QString::number(S)) ; - registerHelpButton(ui.helpButton,help_str,"TransfersDialog") ; + registerHelpButton(ui.helpButton,help_str,"TransfersDialog") ; - mEventHandlerId=0; - rsEvents->registerEventsHandler(RsEventType::FILE_TRANSFER, [this](std::shared_ptr event) { handleEvent(event); }, mEventHandlerId ); + mEventHandlerId=0; + rsEvents->registerEventsHandler( + [this](std::shared_ptr event) { handleEvent(event); }, + mEventHandlerId, RsEventType::FILE_TRANSFER ); } void TransfersDialog::handleEvent(std::shared_ptr event) { - if(event->mType != RsEventType::FILE_TRANSFER) - return; + if(event->mType != RsEventType::FILE_TRANSFER) return; const RsFileTransferEvent *fe = dynamic_cast(event.get()); if(!fe) @@ -1231,26 +1237,26 @@ void TransfersDialog::downloadListCustomPopupMenu( QPoint /*point*/ ) FileInfo info; QMenu priorityQueueMenu(tr("Move in Queue..."), this); - priorityQueueMenu.setIcon(QIcon(IMAGE_PRIORITY)); + priorityQueueMenu.setIcon(FilesDefs::getIconFromQtResourcePath(IMAGE_PRIORITY)); priorityQueueMenu.addAction(queueTopAct); priorityQueueMenu.addAction(queueUpAct); priorityQueueMenu.addAction(queueDownAct); priorityQueueMenu.addAction(queueBottomAct); QMenu prioritySpeedMenu(tr("Priority (Speed)..."), this); - prioritySpeedMenu.setIcon(QIcon(IMAGE_PRIORITY)); + prioritySpeedMenu.setIcon(FilesDefs::getIconFromQtResourcePath(IMAGE_PRIORITY)); prioritySpeedMenu.addAction(prioritySlowAct); prioritySpeedMenu.addAction(priorityMediumAct); prioritySpeedMenu.addAction(priorityFastAct); QMenu chunkMenu(tr("Chunk strategy"), this); - chunkMenu.setIcon(QIcon(IMAGE_PRIORITY)); + chunkMenu.setIcon(FilesDefs::getIconFromQtResourcePath(IMAGE_PRIORITY)); chunkMenu.addAction(chunkStreamingAct); chunkMenu.addAction(chunkProgressiveAct); chunkMenu.addAction(chunkRandomAct); QMenu collectionMenu(tr("Collection"), this); - collectionMenu.setIcon(QIcon(IMAGE_LIBRARY)); + collectionMenu.setIcon(FilesDefs::getIconFromQtResourcePath(IMAGE_LIBRARY)); collectionMenu.addAction(collCreateAct); collectionMenu.addAction(collModifAct); collectionMenu.addAction(collViewAct); @@ -1331,7 +1337,7 @@ void TransfersDialog::downloadListCustomPopupMenu( QPoint /*point*/ ) contextMnu.addAction( renameFileAct) ; } - QMenu *directoryMenu = contextMnu.addMenu(QIcon(IMAGE_OPENFOLDER), tr("Set destination directory")) ; + QMenu *directoryMenu = contextMnu.addMenu(FilesDefs::getIconFromQtResourcePath(IMAGE_OPENFOLDER), tr("Set destination directory")) ; directoryMenu->addAction(specifyDestinationDirectoryAct) ; // Now get the list of existing directories. @@ -1522,285 +1528,6 @@ void TransfersDialog::setDestinationDirectory() } } -/* -int TransfersDialog::addDLItem(int row, const FileInfo &fileInfo) -{ - QString fileHash = QString::fromStdString(fileInfo.hash.toStdString()); - double fileDlspeed = (fileInfo.downloadStatus == FT_STATE_DOWNLOADING) ? (fileInfo.tfRate * 1024.0) : 0.0; - - QString status; - switch (fileInfo.downloadStatus) { - case FT_STATE_FAILED: status = tr("Failed"); break; - case FT_STATE_OKAY: status = tr("Okay"); break; - case FT_STATE_WAITING: status = tr("Waiting"); break; - case FT_STATE_DOWNLOADING: status = tr("Downloading"); break; - case FT_STATE_COMPLETE: status = tr("Complete"); break; - case FT_STATE_QUEUED: status = tr("Queued"); break; - case FT_STATE_PAUSED: status = tr("Paused"); break; - case FT_STATE_CHECKING_HASH:status = tr("Checking..."); break; - default: status = tr("Unknown"); break; - } - - double priority = PRIORITY_NULL; - - if (fileInfo.downloadStatus == FT_STATE_QUEUED) { - priority = fileInfo.queue_position; - } else if (fileInfo.downloadStatus == FT_STATE_COMPLETE) { - priority = 0; - } else { - switch (fileInfo.priority) { - case SPEED_LOW: priority = PRIORITY_SLOWER; break; - case SPEED_NORMAL: priority = PRIORITY_AVERAGE; break; - case SPEED_HIGH: priority = PRIORITY_FASTER; break; - default: priority = PRIORITY_AVERAGE; break; - } - } - - qlonglong completed = fileInfo.transfered; - qlonglong remaining = fileInfo.size - fileInfo.transfered; - - qlonglong downloadtime = (fileInfo.tfRate > 0)?( (fileInfo.size - fileInfo.transfered) / (fileInfo.tfRate * 1024.0) ) : 0 ; - qint64 qi64LastDL = fileInfo.lastTS ; //std::numeric_limits::max(); - - if (qi64LastDL == 0) // file is complete, or any raison why the time has not been set properly - { - QFileInfo file; - - if (fileInfo.downloadStatus == FT_STATE_COMPLETE) - file = QFileInfo(QString::fromUtf8(fileInfo.path.c_str()), QString::fromUtf8(fileInfo.fname.c_str())); - else - file = QFileInfo(QString::fromUtf8(rsFiles->getPartialsDirectory().c_str()), QString::fromUtf8(fileInfo.hash.toStdString().c_str())); - - //Get Last Access on File - if (file.exists()) - qi64LastDL = file.lastModified().toTime_t(); - } - QString strPath = QString::fromUtf8(fileInfo.path.c_str()); - QString strPathAfterDL = strPath; - strPathAfterDL.replace(QString::fromUtf8(rsFiles->getDownloadDirectory().c_str()),""); - - FileChunksInfo fcinfo; - if (!rsFiles->FileDownloadChunksDetails(fileInfo.hash, fcinfo)) { - return -1; - } - - FileProgressInfo pinfo; - pinfo.cmap = fcinfo.chunks; - pinfo.type = FileProgressInfo::DOWNLOAD_LINE; - pinfo.progress = (fileInfo.size == 0) ? 0 : (completed * 100.0 / fileInfo.size); - pinfo.nb_chunks = pinfo.cmap._map.empty() ? 0 : fcinfo.chunks.size(); - - for (uint32_t i = 0; i < fcinfo.chunks.size(); ++i) - switch(fcinfo.chunks[i]) - { - case FileChunksInfo::CHUNK_CHECKING: pinfo.chunks_in_checking.push_back(i); - break ; - case FileChunksInfo::CHUNK_ACTIVE: pinfo.chunks_in_progress.push_back(i); - break ; - case FileChunksInfo::CHUNK_DONE: - case FileChunksInfo::CHUNK_OUTSTANDING: - break ; - } - - QString tooltip; - - if (fileInfo.downloadStatus == FT_STATE_CHECKING_HASH) { - tooltip = tr("If the hash of the downloaded data does\nnot correspond to the hash announced\nby the file source. The data is likely \nto be corrupted.\n\nRetroShare will ask the source a detailed \nmap of the data; it will compare and invalidate\nbad blocks, and download them again\n\nTry to be patient!") ; - } - - if (row < 0) { - row = DLListModel->rowCount(); - DLListModel->insertRow(row); - - // change progress column to own class for sorting - DLListModel->setItem(row, COLUMN_PROGRESS, new ProgressItem(NULL)); - - DLListModel->setData(DLListModel->index(row, COLUMN_SIZE), QVariant((qlonglong) fileInfo.size)); - DLListModel->setData(DLListModel->index(row, COLUMN_ID), fileHash, Qt::DisplayRole); - DLListModel->setData(DLListModel->index(row, COLUMN_ID), fileHash, Qt::UserRole); - } - QString fileName = QString::fromUtf8(fileInfo.fname.c_str()); - - DLListModel->setData(DLListModel->index(row, COLUMN_NAME), fileName); - DLListModel->setData(DLListModel->index(row, COLUMN_NAME), FilesDefs::getIconFromFilename(fileName), Qt::DecorationRole); - - DLListModel->setData(DLListModel->index(row, COLUMN_COMPLETED), QVariant((qlonglong)completed)); - DLListModel->setData(DLListModel->index(row, COLUMN_DLSPEED), QVariant((double)fileDlspeed)); - DLListModel->setData(DLListModel->index(row, COLUMN_PROGRESS), QVariant((float)pinfo.progress)); - DLListModel->setData(DLListModel->index(row, COLUMN_PROGRESS), QVariant::fromValue(pinfo), Qt::UserRole); - DLListModel->setData(DLListModel->index(row, COLUMN_STATUS), QVariant(status)); - DLListModel->setData(DLListModel->index(row, COLUMN_PRIORITY), QVariant(priority)); - DLListModel->setData(DLListModel->index(row, COLUMN_REMAINING), QVariant((qlonglong)remaining)); - DLListModel->setData(DLListModel->index(row, COLUMN_DOWNLOADTIME), QVariant((qlonglong)downloadtime)); - DLListModel->setData(DLListModel->index(row, COLUMN_LASTDL), QVariant(qi64LastDL)); - DLListModel->setData(DLListModel->index(row, COLUMN_PATH), QVariant(strPathAfterDL)); - DLListModel->item(row,COLUMN_PATH)->setToolTip(strPath); - DLListModel->item(row,COLUMN_STATUS)->setToolTip(tooltip); - - QStandardItem *dlItem = DLListModel->item(row); - - std::set used_rows ; - int active = 0; - - if (fileInfo.downloadStatus != FT_STATE_COMPLETE) { - for (std::vector::const_iterator pit = fileInfo.peers.begin() ; pit != fileInfo.peers.end(); ++pit) - { - const TransferInfo &transferInfo = *pit; - - //unique combination: fileHash + peerId, variant: hash + peerName (too long) - QString hashFileAndPeerId = fileHash + QString::fromStdString(transferInfo.peerId.toStdString()); - - double peerDlspeed = 0; - if ((uint32_t)transferInfo.status == FT_STATE_DOWNLOADING && fileInfo.downloadStatus != FT_STATE_PAUSED && fileInfo.downloadStatus != FT_STATE_COMPLETE) - peerDlspeed = transferInfo.tfRate * 1024.0; - - FileProgressInfo peerpinfo; - peerpinfo.cmap = fcinfo.compressed_peer_availability_maps[transferInfo.peerId]; - peerpinfo.type = FileProgressInfo::DOWNLOAD_SOURCE ; - peerpinfo.progress = 0.0; // we don't display completion for sources. - peerpinfo.nb_chunks = peerpinfo.cmap._map.empty() ? 0 : fcinfo.chunks.size(); - - int row_id = addPeerToDLItem(dlItem, transferInfo.peerId, hashFileAndPeerId, peerDlspeed, transferInfo.status, peerpinfo); - - used_rows.insert(row_id); - - // get the sources (number of online peers) - if (transferInfo.tfRate > 0 && fileInfo.downloadStatus == FT_STATE_DOWNLOADING) - ++active; - } - } - - float fltSources = active + (float)fileInfo.peers.size()/1000; - DLListModel->setData(DLListModel->index(row, COLUMN_SOURCES), fltSources); - - // This is not optimal, but we deal with a small number of elements. The reverse order is really important, - // because rows after the deleted rows change positions ! - // - for (int r = dlItem->rowCount() - 1; r >= 0; --r) { - if (used_rows.find(r) == used_rows.end()) { - dlItem->removeRow(r); - } - } - - return row; -} - -int TransfersDialog::addPeerToDLItem(QStandardItem *dlItem, const RsPeerId& peer_ID, const QString& coreID, double dlspeed, uint32_t status, const FileProgressInfo& peerInfo) -{ - // try to find the item - int childRow = -1; - - QStandardItem *childId = NULL; - for (int count = 0; (childId = dlItem->child(count, COLUMN_ID)) != NULL; ++count) { - if (childId->data(Qt::UserRole).toString() == coreID) { - childRow = count; - break; - } - } - - QStandardItem *siName = NULL; - QStandardItem *siStatus = NULL; - - if (childRow == -1) { - // set this false if you want to expand on double click - dlItem->setEditable(false); - - QHeaderView *header = ui.downloadList->header(); - - QStandardItem *iName = new QStandardItem(); //COLUMN_NAME - QStandardItem *iSize = new SortByNameItem(header); //COLUMN_SIZE - QStandardItem *iCompleted = new SortByNameItem(header); //COLUMN_COMPLETED - QStandardItem *iDlSpeed = new SortByNameItem(header); //COLUMN_DLSPEED - QStandardItem *iProgress = new ProgressItem(header); //COLUMN_PROGRESS - QStandardItem *iSource = new SortByNameItem(header); //COLUMN_SOURCES - QStandardItem *iStatus = new SortByNameItem(header); //COLUMN_STATUS - QStandardItem *iPriority = new SortByNameItem(header); //COLUMN_PRIORITY - QStandardItem *iRemaining = new SortByNameItem(header); //COLUMN_REMAINING - QStandardItem *iDownloadTime = new SortByNameItem(header); //COLUMN_DOWNLOADTIME - QStandardItem *iID = new SortByNameItem(header); //COLUMN_ID - QStandardItem *iLastDL = new SortByNameItem(header); //COLUMN_LASTDL - QStandardItem *iPath = new SortByNameItem(header); //COLUMN_PATH - - siName = iName; - siStatus = iStatus; - - QList items; - QString iconName; - QString tooltip; - iName->setData(QVariant(getPeerName(peer_ID, iconName, tooltip)), Qt::DisplayRole); - iName->setData(QIcon(iconName), Qt::DecorationRole); - iName->setData(QVariant(tooltip), Qt::ToolTipRole); - iSize->setData(QVariant(QString()), Qt::DisplayRole); - iCompleted->setData(QVariant(QString()), Qt::DisplayRole); - iDlSpeed->setData(QVariant((double)dlspeed), Qt::DisplayRole); - iProgress->setData(QVariant((float)peerInfo.progress), Qt::DisplayRole); - iProgress->setData(QVariant::fromValue(peerInfo), Qt::UserRole); - iSource->setData(QVariant(QString()), Qt::DisplayRole); - - iPriority->setData(QVariant((double)PRIORITY_NULL), Qt::DisplayRole); // blank field for priority - iRemaining->setData(QVariant(QString()), Qt::DisplayRole); - iDownloadTime->setData(QVariant(QString()), Qt::DisplayRole); - iID->setData(QVariant() , Qt::DisplayRole); - iID->setData(QVariant(coreID), Qt::UserRole); - iLastDL->setData(QVariant(QString()), Qt::DisplayRole); - iPath->setData(QVariant(QString()), Qt::DisplayRole); - - items.append(iName); - items.append(iSize); - items.append(iCompleted); - items.append(iDlSpeed); - items.append(iProgress); - items.append(iSource); - items.append(iStatus); - items.append(iPriority); - items.append(iRemaining); - items.append(iDownloadTime); - items.append(iID); - items.append(iLastDL); - items.append(iPath); - dlItem->appendRow(items); - - childRow = dlItem->rowCount() - 1; - } else { - // just update the child (peer) - dlItem->child(childRow, COLUMN_DLSPEED)->setData(QVariant((double)dlspeed), Qt::DisplayRole); - dlItem->child(childRow, COLUMN_PROGRESS)->setData(QVariant((float)peerInfo.progress), Qt::DisplayRole); - dlItem->child(childRow, COLUMN_PROGRESS)->setData(QVariant::fromValue(peerInfo), Qt::UserRole); - - siName = dlItem->child(childRow,COLUMN_NAME); - siStatus = dlItem->child(childRow, COLUMN_STATUS); - } - - switch (status) { - case FT_STATE_FAILED: - siStatus->setData(QVariant(tr("Failed"))) ; - siName->setData(QIcon(":/images/Client1.png"), Qt::StatusTipRole); - break ; - case FT_STATE_OKAY: - siStatus->setData(QVariant(tr("Okay"))); - siName->setData(QIcon(":/images/Client2.png"), Qt::StatusTipRole); - break ; - case FT_STATE_WAITING: - siStatus->setData(QVariant(tr(""))); - siName->setData(QIcon(":/images/Client3.png"), Qt::StatusTipRole); - break ; - case FT_STATE_DOWNLOADING: - siStatus->setData(QVariant(tr("Transferring"))); - siName->setData(QIcon(":/images/Client0.png"), Qt::StatusTipRole); - break ; - case FT_STATE_COMPLETE: - siStatus->setData(QVariant(tr("Complete"))); - siName->setData(QIcon(":/images/Client0.png"), Qt::StatusTipRole); - break ; - default: - siStatus->setData(QVariant(tr(""))); - siName->setData(QIcon(":/images/Client4.png"), Qt::StatusTipRole); - } - - return childRow; -} -*/ - int TransfersDialog::addULItem(int row, const FileInfo &fileInfo) { if (fileInfo.peers.empty()) @@ -1822,7 +1549,7 @@ int TransfersDialog::addULItem(int row, const FileInfo &fileInfo) //ULListModel->setItem(row, COLUMN_UPROGRESS, new ProgressItem(NULL)); ULListModel->setData(ULListModel->index(row, COLUMN_UNAME), fileName); - ULListModel->setData(ULListModel->index(row, COLUMN_UNAME), FilesDefs::getIconFromFilename(fileName), Qt::DecorationRole); + ULListModel->setData(ULListModel->index(row, COLUMN_UNAME), FilesDefs::getIconFromFileType(fileName), Qt::DecorationRole); ULListModel->setData(ULListModel->index(row, COLUMN_UHASH), fileHash); ULListModel->setData(ULListModel->index(row, COLUMN_UHASH), fileHash, Qt::UserRole); } @@ -2225,9 +1952,8 @@ void TransfersDialog::pasteLink() for(QList::const_iterator it(links.begin());it!=links.end();++it) { - FileTree *ft = FileTree::create((*it).radix().toStdString()) ; - - col.merge_in(*ft) ; + auto ft = RsFileTree::fromRadix64((*it).radix().toStdString()); + col.merge_in(*ft); } links.clear(); RSLinkClipboard::pasteLinks(links,RetroShareLink::TYPE_FILE); diff --git a/retroshare-gui/src/gui/FileTransfer/TransfersDialog.h b/retroshare-gui/src/gui/FileTransfer/TransfersDialog.h index 21f642399..e11f6c232 100644 --- a/retroshare-gui/src/gui/FileTransfer/TransfersDialog.h +++ b/retroshare-gui/src/gui/FileTransfer/TransfersDialog.h @@ -25,14 +25,14 @@ #include #include -#include "RsAutoUpdatePage.h" +#include #include "ui_TransfersDialog.h" #include #include -#define IMAGE_TRANSFERS ":/icons/ktorrent_128.png" +#define IMAGE_TRANSFERS ":/icons/png/fileshare.png" class QShortcut; class DLListDelegate; diff --git a/retroshare-gui/src/gui/GenCertDialog.cpp b/retroshare-gui/src/gui/GenCertDialog.cpp index 4fdb9f714..71691f07e 100644 --- a/retroshare-gui/src/gui/GenCertDialog.cpp +++ b/retroshare-gui/src/gui/GenCertDialog.cpp @@ -35,6 +35,7 @@ #include "gui/settings/rsharesettings.h" #include "TorControl/TorManager.h" #include "util/misc.h" +#include "gui/common/FilesDefs.h" #include #include @@ -132,7 +133,7 @@ GenCertDialog::GenCertDialog(bool onlyGenerateIdentity, QWidget *parent) /* Invoke Qt Designer generated QObject setup routine */ ui.setupUi(this); - //ui.headerFrame->setHeaderImage(QPixmap(":/icons/svg/profile.svg")); + //ui.headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/icons/svg/profile.svg")); //ui.headerFrame->setHeaderText(tr("Create a new profile")); connect(ui.reuse_existing_node_CB, SIGNAL(triggered()), this, SLOT(switchReuseExistingNode())); @@ -340,6 +341,10 @@ void GenCertDialog::setupState() ui.hiddenport_spinBox->setVisible(hidden_state && !tor_auto); ui.cbUseBob->setVisible(hidden_state && !tor_auto); +#ifndef RS_USE_I2P_BOB + ui.cbUseBob->setDisabled(true); + ui.cbUseBob->setToolTip(tr("BOB support is not available")); +#endif if(!mAllFieldsOk) { @@ -428,9 +433,9 @@ void GenCertDialog::updateCheckLabels() } if(mEntropyOk) - ui.randomness_check_LB->setPixmap(QPixmap(IMAGE_GOOD)) ; + ui.randomness_check_LB->setPixmap(FilesDefs::getPixmapFromQtResourcePath(IMAGE_GOOD)) ; else - ui.randomness_check_LB->setPixmap(QPixmap(IMAGE_BAD)) ; + ui.randomness_check_LB->setPixmap(FilesDefs::getPixmapFromQtResourcePath(IMAGE_BAD)) ; setupState(); } diff --git a/retroshare-gui/src/gui/GetStartedDialog.h b/retroshare-gui/src/gui/GetStartedDialog.h index 8705d0290..6d943958a 100644 --- a/retroshare-gui/src/gui/GetStartedDialog.h +++ b/retroshare-gui/src/gui/GetStartedDialog.h @@ -21,9 +21,9 @@ #ifndef _GETTING_STARTED_DIALOG_H #define _GETTING_STARTED_DIALOG_H -//#include +#include + #include "ui_GetStartedDialog.h" -#include "mainpage.h" #include diff --git a/retroshare-gui/src/gui/Identity/IdDetailsDialog.cpp b/retroshare-gui/src/gui/Identity/IdDetailsDialog.cpp index ad0c261c9..9056b5f31 100644 --- a/retroshare-gui/src/gui/Identity/IdDetailsDialog.cpp +++ b/retroshare-gui/src/gui/Identity/IdDetailsDialog.cpp @@ -22,8 +22,10 @@ #include "IdDetailsDialog.h" #include "ui_IdDetailsDialog.h" #include "gui/gxs/GxsIdDetails.h" +#include "util/qtthreadsutils.h" #include "gui/settings/rsharesettings.h" #include "gui/common/UIStateHelper.h" +#include "gui/common/FilesDefs.h" #include "gui/msgs/MessageComposer.h" #include "gui/RetroShareLink.h" @@ -79,12 +81,9 @@ IdDetailsDialog::IdDetailsDialog(const RsGxsGroupId& id, QWidget *parent) : mStateHelper->setActive(IDDETAILSDIALOG_REPLIST, false); - /* Create token queue */ - mIdQueue = new TokenQueue(rsIdentity->getTokenService(), this); - Settings->loadWidgetInformation(this); - ui->headerFrame->setHeaderImage(QPixmap(":/icons/png/person.png")); + ui->headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/person.png")); ui->headerFrame->setHeaderText(tr("Person Details")); //connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(changeGroup())); @@ -94,7 +93,7 @@ IdDetailsDialog::IdDetailsDialog(const RsGxsGroupId& id, QWidget *parent) : connect(ui->inviteButton, SIGNAL(clicked()), this, SLOT(sendInvite())); - requestIdDetails(); + loadIdentity(); } /** Destructor. */ @@ -103,7 +102,6 @@ IdDetailsDialog::~IdDetailsDialog() Settings->saveWidgetInformation(this); delete(ui); - delete(mIdQueue); } void IdDetailsDialog::toggleAutoBanIdentities(bool b) @@ -135,40 +133,8 @@ static QString getHumanReadableDuration(uint32_t seconds) return QString(QObject::tr("%1 days ago")).arg(seconds/86400) ; } -void IdDetailsDialog::insertIdDetails(uint32_t token) +void IdDetailsDialog::loadIdentity(RsGxsIdGroup data) { - mStateHelper->setLoading(IDDETAILSDIALOG_IDDETAILS, false); - - /* get details from libretroshare */ - std::vector datavector; - if (!rsIdentity->getGroupData(token, datavector)) - { - mStateHelper->setActive(IDDETAILSDIALOG_IDDETAILS, false); - mStateHelper->clear(IDDETAILSDIALOG_REPLIST); - - ui->lineEdit_KeyId->setText("ERROR GETTING KEY!"); - - return; - } - - if (datavector.size() != 1) - { -#ifdef ID_DEBUG - std::cerr << "IdDetailsDialog::insertIdDetails() Invalid datavector size"; -#endif - - mStateHelper->setActive(IDDETAILSDIALOG_IDDETAILS, false); - mStateHelper->clear(IDDETAILSDIALOG_IDDETAILS); - - ui->lineEdit_KeyId->setText("INVALID DV SIZE"); - - return; - } - - mStateHelper->setActive(IDDETAILSDIALOG_IDDETAILS, true); - - RsGxsIdGroup &data = datavector[0]; - /* get GPG Details from rsPeers */ RsPgpId ownPgpId = rsPeers->getGPGOwnId(); @@ -357,46 +323,20 @@ void IdDetailsDialog::modifyReputation() case 2: op = RsOpinion::POSITIVE; break; default: std::cerr << "Wrong value from opinion combobox. Bug??" << std::endl; - break; + return; } rsReputations->setOwnOpinion(id,op); -#ifdef ID_DEBUG - std::cerr << "IdDialog::modifyReputation() ID: " << id << " Mod: " << mod; - std::cerr << std::endl; -#endif - -#ifdef SUSPENDED - // Cyril: apparently the old reputation system was in used here. It's based on GXS data exchange, and probably not - // very efficient because of this. - - uint32_t token; - if (!rsIdentity->submitOpinion(token, id, false, op)) - { -#ifdef ID_DEBUG - std::cerr << "IdDialog::modifyReputation() Error submitting Opinion"; - std::cerr << std::endl; -#endif - } -#endif - -#ifdef ID_DEBUG - std::cerr << "IdDialog::modifyReputation() queuingRequest(), token: " << token; - std::cerr << std::endl; -#endif - // trigger refresh when finished. // basic / anstype are not needed. - requestIdDetails(); + loadIdentity(); return; } -void IdDetailsDialog::requestIdDetails() +void IdDetailsDialog::loadIdentity() { - mIdQueue->cancelActiveRequestTokens(IDDETAILSDIALOG_IDDETAILS); - - if (mId.isNull()) + if (mId.isNull()) { mStateHelper->setActive(IDDETAILSDIALOG_IDDETAILS, false); mStateHelper->setLoading(IDDETAILSDIALOG_IDDETAILS, false); @@ -407,69 +347,42 @@ void IdDetailsDialog::requestIdDetails() mStateHelper->setLoading(IDDETAILSDIALOG_IDDETAILS, true); - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; - - uint32_t token; - std::list groupIds; - groupIds.push_back(mId); - - mIdQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, groupIds, IDDETAILSDIALOG_IDDETAILS); -} - -void IdDetailsDialog::requestRepList() -{ - // Removing this for the moment. - return; - - mStateHelper->setLoading(IDDETAILSDIALOG_REPLIST, true); - - mIdQueue->cancelActiveRequestTokens(IDDETAILSDIALOG_REPLIST); - - std::list groupIds; - groupIds.push_back(mId); - - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; - - uint32_t token; - mIdQueue->requestMsgInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, groupIds, IDDETAILSDIALOG_REPLIST); -} - -void IdDetailsDialog::insertRepList(uint32_t token) -{ - Q_UNUSED(token) - mStateHelper->setLoading(IDDETAILSDIALOG_REPLIST, false); - mStateHelper->setActive(IDDETAILSDIALOG_REPLIST, true); -} - -void IdDetailsDialog::loadRequest(const TokenQueue *queue, const TokenRequest &req) -{ - if (queue != mIdQueue) { - return; - } - + RsThread::async([this]() + { #ifdef ID_DEBUG - std::cerr << "IdDetailsDialog::loadRequest() UserType: " << req.mUserType; - std::cerr << std::endl; + std::cerr << "Retrieving post data for identity " << mThreadId << std::endl; #endif - switch (req.mUserType) - { - case IDDETAILSDIALOG_IDDETAILS: - insertIdDetails(req.mToken); - break; - - case IDDETAILSDIALOG_REPLIST: - insertRepList(req.mToken); - break; - - default: - std::cerr << "IdDetailsDialog::loadRequest() ERROR"; - std::cerr << std::endl; - } + std::set ids( { RsGxsId(mId) } ) ; + std::vector ids_data; + + if(!rsIdentity->getIdentitiesInfo(ids,ids_data)) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve identities group info for id " << mId << std::endl; + return; + } + + if(ids_data.size() != 1) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve exactly one group info for id " << mId << std::endl; + return; + } + RsGxsIdGroup group(ids_data[0]); + + RsQThreadUtils::postToObject( [group,this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ + + loadIdentity(group); + + }, this ); + }); + } + QString IdDetailsDialog::inviteMessage() { return tr("Hi,
I want to be friends with you on RetroShare.
"); diff --git a/retroshare-gui/src/gui/Identity/IdDetailsDialog.h b/retroshare-gui/src/gui/Identity/IdDetailsDialog.h index 672cb764f..ba53b2bba 100644 --- a/retroshare-gui/src/gui/Identity/IdDetailsDialog.h +++ b/retroshare-gui/src/gui/Identity/IdDetailsDialog.h @@ -32,7 +32,7 @@ class IdDetailsDialog; class UIStateHelper; -class IdDetailsDialog : public QDialog, public TokenResponse +class IdDetailsDialog : public QDialog { Q_OBJECT @@ -42,9 +42,6 @@ public: /** Default destructor */ ~IdDetailsDialog(); - /* TokenResponse */ - void loadRequest(const TokenQueue *queue, const TokenRequest &req); - private slots: void modifyReputation(); void toggleAutoBanIdentities(bool b); @@ -52,15 +49,11 @@ private slots: static QString inviteMessage(); void sendInvite(); private : - void requestIdDetails(); - void insertIdDetails(uint32_t token); - - void requestRepList(); - void insertRepList(uint32_t token); + void loadIdentity(); + void loadIdentity(RsGxsIdGroup data); private: RsGxsGroupId mId; - TokenQueue *mIdQueue; UIStateHelper *mStateHelper; /** Qt Designer generated object */ diff --git a/retroshare-gui/src/gui/Identity/IdDetailsDialog.ui b/retroshare-gui/src/gui/Identity/IdDetailsDialog.ui index 3faad4076..6a681f142 100644 --- a/retroshare-gui/src/gui/Identity/IdDetailsDialog.ui +++ b/retroshare-gui/src/gui/Identity/IdDetailsDialog.ui @@ -1,7 +1,7 @@ IdDetailsDialog - + 0 @@ -10,14 +10,7 @@ 475 - - Person Details - - - - :/images/logo/logo_32.png:/images/logo/logo_32.png - - + 0 @@ -500,16 +493,12 @@ p, li { white-space: pre-wrap; } - - HeaderFrame - QFrame -
gui/common/HeaderFrame.h
- 1 + + HeaderFrame + QFrame +
gui/common/HeaderFrame.h
+ 1
-
- - - - - + +
diff --git a/retroshare-gui/src/gui/Identity/IdDialog.cpp b/retroshare-gui/src/gui/Identity/IdDialog.cpp index cb9a2242d..ad35ed3ff 100644 --- a/retroshare-gui/src/gui/Identity/IdDialog.cpp +++ b/retroshare-gui/src/gui/Identity/IdDialog.cpp @@ -34,10 +34,12 @@ #include "gui/RetroShareLink.h" #include "gui/chat/ChatDialog.h" #include "gui/Circles/CreateCircleDialog.h" +#include "gui/common/FilesDefs.h" #include "gui/common/UIStateHelper.h" #include "gui/common/UserNotify.h" #include "gui/gxs/GxsIdDetails.h" -#include "gui/gxs/RsGxsUpdateBroadcastBase.h" +#include "gui/gxs/GxsIdTreeWidgetItem.h" +//#include "gui/gxs/RsGxsUpdateBroadcastBase.h" #include "gui/msgs/MessageComposer.h" #include "gui/settings/rsharesettings.h" #include "util/qtthreadsutils.h" @@ -45,6 +47,7 @@ #include "util/misc.h" #include "util/QtVersion.h" #include "util/rstime.h" +#include "util/rsdebug.h" #include "retroshare/rsgxsflags.h" #include "retroshare/rsmsgs.h" @@ -53,11 +56,14 @@ #include #include +#include /****** * #define ID_DEBUG 1 *****/ +#define QT_BUG_CRASH_IN_TAKECHILD_WORKAROUND 1 + // Data Requests. #define IDDIALOG_IDLIST 1 #define IDDIALOG_IDDETAILS 2 @@ -104,7 +110,7 @@ #define RSID_FILTER_BANNED 0x0020 #define RSID_FILTER_ALL 0xffff -#define IMAGE_EDIT ":/images/edit_16.png" +#define IMAGE_EDIT ":/icons/png/pencil-edit-button.png" #define IMAGE_CREATE ":/icons/circle_new_128.png" #define IMAGE_INVITED ":/icons/bullet_yellow_128.png" #define IMAGE_MEMBER ":/icons/bullet_green_128.png" @@ -142,23 +148,25 @@ class TreeWidgetItem : public QTreeWidgetItem }; /** Constructor */ -IdDialog::IdDialog(QWidget *parent) : - RsGxsUpdateBroadcastPage(rsIdentity, parent), - ui(new Ui::IdDialog) +IdDialog::IdDialog(QWidget *parent) : MainPage(parent), ui(new Ui::IdDialog) { ui->setupUi(this); - mIdQueue = NULL; - - mEventHandlerId_identity = 0; - rsEvents->registerEventsHandler(RsEventType::GXS_IDENTITY, [this](std::shared_ptr event) { RsQThreadUtils::postToObject( [=]() { handleEvent_main_thread(event); }, this ); }, mEventHandlerId_identity ); + mEventHandlerId_identity = 0; + rsEvents->registerEventsHandler( + [this](std::shared_ptr event) + { RsQThreadUtils::postToObject([=](){ handleEvent_main_thread(event); }, this); }, + mEventHandlerId_identity, RsEventType::GXS_IDENTITY ); - mEventHandlerId_circles = 0; - rsEvents->registerEventsHandler(RsEventType::GXS_CIRCLES, [this](std::shared_ptr event) { RsQThreadUtils::postToObject( [=]() { handleEvent_main_thread(event); }, this ); }, mEventHandlerId_circles ); + mEventHandlerId_circles = 0; + rsEvents->registerEventsHandler( + [this](std::shared_ptr event) + { RsQThreadUtils::postToObject([=](){ handleEvent_main_thread(event); }, this); }, + mEventHandlerId_circles, RsEventType::GXS_CIRCLES ); - // This is used to grab the broadcast of changes from p3GxsCircles, which is discarded by the current dialog, since it expects data for p3Identity only. - mCirclesBroadcastBase = new RsGxsUpdateBroadcastBase(rsGxsCircles, this); - connect(mCirclesBroadcastBase, SIGNAL(fillDisplay(bool)), this, SLOT(updateCirclesDisplay(bool))); + // This is used to grab the broadcast of changes from p3GxsCircles, which is discarded by the current dialog, since it expects data for p3Identity only. + //mCirclesBroadcastBase = new RsGxsUpdateBroadcastBase(rsGxsCircles, this); + //connect(mCirclesBroadcastBase, SIGNAL(fillDisplay(bool)), this, SLOT(updateCirclesDisplay(bool))); ownItem = new QTreeWidgetItem(); ownItem->setText(0, tr("My own identities")); @@ -173,9 +181,11 @@ IdDialog::IdDialog(QWidget *parent) : contactsItem->setData(RSID_COL_VOTES, Qt::DecorationRole,0xff); ui->treeWidget_membership->clear(); - - mExternalOtherCircleItem = NULL ; - mExternalBelongingCircleItem = NULL ; + ui->treeWidget_membership->setItemDelegateForColumn(CIRCLEGROUP_CIRCLE_COL_GROUPNAME,new GxsIdTreeItemDelegate()); + + mExternalOtherCircleItem = NULL ; + mExternalBelongingCircleItem = NULL ; + mMyCircleItem = NULL ; /* Setup UI helper */ mStateHelper = new UIStateHelper(this); @@ -245,7 +255,7 @@ IdDialog::IdDialog(QWidget *parent) : this, &IdDialog::chatIdentityItem ); - ui->avlabel_Circles->setPixmap(QPixmap(":/icons/png/circles.png")); + ui->avlabel_Circles->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/circles.png")); ui->headerTextLabel_Circles->setText(tr("Circles")); @@ -311,10 +321,10 @@ IdDialog::IdDialog(QWidget *parent) : connect(idTWHAction, SIGNAL(toggled(bool)), this, SLOT(filterToggled(bool))); idTWHMenu->addAction(idTWHAction); - QAction *CreateIDAction = new QAction(QIcon(":/icons/png/person.png"),tr("Create new Identity"), this); + QAction *CreateIDAction = new QAction(FilesDefs::getIconFromQtResourcePath(":/icons/png/person.png"),tr("Create new Identity"), this); connect(CreateIDAction, SIGNAL(triggered()), this, SLOT(addIdentity())); - QAction *CreateCircleAction = new QAction(QIcon(":/icons/png/circles.png"),tr("Create new circle"), this); + QAction *CreateCircleAction = new QAction(FilesDefs::getIconFromQtResourcePath(":/icons/png/circles.png"),tr("Create new circle"), this); connect(CreateCircleAction, SIGNAL(triggered()), this, SLOT(createExternalCircle())); QMenu *menu = new QMenu(); @@ -352,6 +362,9 @@ IdDialog::IdDialog(QWidget *parent) : ui->idTreeWidget->setColumnWidth(RSID_COL_IDTYPE, 18 * fontWidth); ui->idTreeWidget->setColumnWidth(RSID_COL_VOTES, 2 * fontWidth); + ui->idTreeWidget->setItemDelegateForColumn( + RSID_COL_NICKNAME, + new GxsIdTreeItemDelegate()); ui->idTreeWidget->setItemDelegateForColumn( RSID_COL_VOTES, new ReputationItemDelegate(RsReputationLevel(0xff))); @@ -360,8 +373,6 @@ IdDialog::IdDialog(QWidget *parent) : QHeaderView * idheader = ui->idTreeWidget->header(); QHeaderView_setSectionResizeModeColumn(idheader, RSID_COL_VOTES, QHeaderView::ResizeToContents); - mIdQueue = new TokenQueue(rsIdentity->getTokenService(), this); - mStateHelper->setActive(IDDIALOG_IDDETAILS, false); mStateHelper->setActive(IDDIALOG_REPLIST, false); @@ -391,19 +402,8 @@ IdDialog::IdDialog(QWidget *parent) : connect(ui->treeWidget_membership, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(CircleListCustomPopupMenu(QPoint))); connect(ui->autoBanIdentities_CB, SIGNAL(toggled(bool)), this, SLOT(toggleAutoBanIdentities(bool))); - - /* Setup TokenQueue */ - mCircleQueue = new TokenQueue(rsGxsCircles->getTokenService(), this); - - requestCircleGroupMeta(); - - // This timer shouldn't be needed, but it is now, because the update of subscribe status and appartenance to the - // circle doesn't trigger a proper GUI update. - - QTimer *tmer = new QTimer(this) ; - connect(tmer,SIGNAL(timeout()),this,SLOT(updateCirclesDisplay())) ; - - tmer->start(10000) ; // update every 10 secs. + updateCircles(); + updateIdList(); } void IdDialog::handleEvent_main_thread(std::shared_ptr event) @@ -419,8 +419,14 @@ void IdDialog::handleEvent_main_thread(std::shared_ptr event) { case RsGxsIdentityEventCode::DELETED_IDENTITY: case RsGxsIdentityEventCode::NEW_IDENTITY: + case RsGxsIdentityEventCode::UPDATED_IDENTITY: - requestIdList(); + updateIdList(); + + if(!mId.isNull() && mId == e->mIdentityId) + updateIdentity(); + + break; default: break; } @@ -434,14 +440,14 @@ void IdDialog::handleEvent_main_thread(std::shared_ptr event) switch(e->mCircleEventType) { - case RsGxsCircleEventCode::NEW_CIRCLE: case RsGxsCircleEventCode::CIRCLE_MEMBERSHIP_REQUEST: - case RsGxsCircleEventCode::CIRCLE_MEMBERSHIP_INVITE: + case RsGxsCircleEventCode::CIRCLE_MEMBERSHIP_ID_ADDED_TO_INVITEE_LIST: case RsGxsCircleEventCode::CIRCLE_MEMBERSHIP_LEAVE: - case RsGxsCircleEventCode::CIRCLE_MEMBERSHIP_JOIN: - case RsGxsCircleEventCode::CIRCLE_MEMBERSHIP_REVOQUED: + case RsGxsCircleEventCode::CIRCLE_MEMBERSHIP_ID_REMOVED_FROM_INVITEE_LIST: + case RsGxsCircleEventCode::NEW_CIRCLE: + case RsGxsCircleEventCode::CACHE_DATA_UPDATED: - requestCircleGroupMeta(); + updateCircles(); default: break; } @@ -453,7 +459,7 @@ void IdDialog::clearPerson() { QFontMetricsF f(ui->avLabel_Person->font()) ; - ui->avLabel_Person->setPixmap(QPixmap(":/icons/png/people.png").scaled(f.height()*4,f.height()*4,Qt::IgnoreAspectRatio,Qt::SmoothTransformation)); + ui->avLabel_Person->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/people.png").scaled(f.height()*4,f.height()*4,Qt::IgnoreAspectRatio,Qt::SmoothTransformation)); ui->headerTextLabel_Person->setText(tr("People")); ui->inviteFrame->hide(); @@ -471,7 +477,7 @@ void IdDialog::toggleAutoBanIdentities(bool b) if(!id.isNull()) { rsReputations->banNode(id,b) ; - requestIdList(); + updateIdList(); } } @@ -486,76 +492,132 @@ void IdDialog::updateCirclesDisplay() #ifdef ID_DEBUG std::cerr << "!!Updating circles display!" << std::endl; #endif - requestCircleGroupMeta() ; + updateCircles() ; } /************************** Request / Response *************************/ /*** Loading Main Index ***/ -void IdDialog::requestCircleGroupMeta() +void IdDialog::updateCircles() { - mStateHelper->setLoading(CIRCLESDIALOG_GROUPMETA, true); + RsThread::async([this]() + { + // 1 - get message data from p3GxsForums -#ifdef ID_DEBUG - std::cerr << "CirclesDialog::requestGroupMeta()"; - std::cerr << std::endl; +#ifdef DEBUG_FORUMS + std::cerr << "Retrieving post data for post " << mThreadId << std::endl; #endif - mCircleQueue->cancelActiveRequestTokens(CIRCLESDIALOG_GROUPMETA); + /* This can be big so use a smart pointer to just copy the pointer + * instead of copying the whole list accross the lambdas */ + auto circle_metas = std::make_unique>(); - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_GROUP_META; + if(!rsGxsCircles->getCirclesSummaries(*circle_metas)) + { + RS_ERR("failed to retrieve circles group info list"); + return; + } - uint32_t token; - mCircleQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_SUMMARY, opts, CIRCLESDIALOG_GROUPMETA); + RsQThreadUtils::postToObject( + [circle_metas = std::move(circle_metas), this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ + + loadCircles(*circle_metas); + + }, this ); + + }); } -// should update this code to be called and modify the tree widget accordingly -#ifdef SUSPENDED -void IdDialog::requestCircleGroupData(const RsGxsCircleId& circle_id) +static QTreeWidgetItem *setChildItem(QTreeWidgetItem *item, const RsGroupMetaData& circle_group) { - mStateHelper->setLoading(CIRCLESDIALOG_GROUPDATA, true); + QString test_str = QString::fromStdString(circle_group.mGroupId.toStdString()); + // 1 - check if the item already exists and remove possible duplicates + + std::vector found_indices; + + for(uint32_t k=0; k < (uint32_t)item->childCount(); ++k) + if( item->child(k)->data(CIRCLEGROUP_CIRCLE_COL_GROUPID,Qt::UserRole).toString() == test_str) + found_indices.push_back(k); + + while(found_indices.size() > 1) // delete duplicates, starting from the end in order that deletion preserves indices + { + delete item->takeChild(found_indices.back()); + found_indices.pop_back(); + } + + if(!found_indices.empty()) + { + QTreeWidgetItem *subitem = item->child(found_indices[0]); + + if(subitem->text(CIRCLEGROUP_CIRCLE_COL_GROUPNAME) != QString::fromUtf8(circle_group.mGroupName.c_str())) + { #ifdef ID_DEBUG - std::cerr << "CirclesDialog::requestGroupData()"; - std::cerr << std::endl; + std::cerr << " Existing circle has a new name. Updating it in the tree." << std::endl; #endif + subitem->setText(CIRCLEGROUP_CIRCLE_COL_GROUPNAME, QString::fromUtf8(circle_group.mGroupName.c_str())); + } - mCircleQueue->cancelActiveRequestTokens(CIRCLESDIALOG_GROUPDATA); + return subitem; + } - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; + // 2 - if not, create - std::list grps ; - grps.push_back(RsGxsGroupId(circle_id)); + QTreeWidgetItem *subitem = new QTreeWidgetItem(); - uint32_t token; - mCircleQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, grps, CIRCLESDIALOG_GROUPDATA); + subitem->setText(CIRCLEGROUP_CIRCLE_COL_GROUPNAME, QString::fromUtf8(circle_group.mGroupName.c_str())); + subitem->setData(CIRCLEGROUP_CIRCLE_COL_GROUPID,Qt::UserRole, QString::fromStdString(circle_group.mGroupId.toStdString())); + subitem->setData(CIRCLEGROUP_CIRCLE_COL_GROUPFLAGS, Qt::UserRole, QVariant(circle_group.mSubscribeFlags)); + + item->addChild(subitem); + + return subitem; } -#endif -void IdDialog::loadCircleGroupMeta(const uint32_t &token) +static void removeChildItem(QTreeWidgetItem *item, const RsGroupMetaData& circle_group) { - mStateHelper->setLoading(CIRCLESDIALOG_GROUPMETA, false); + QString test_str = QString::fromStdString(circle_group.mGroupId.toStdString()); + // 1 - check if the item already exists and remove possible duplicates + + std::list found_indices; + + for(uint32_t k=0; k < (uint32_t)item->childCount(); ++k) + if( item->child(k)->data(CIRCLEGROUP_CIRCLE_COL_GROUPID,Qt::UserRole).toString() == test_str) + found_indices.push_front(k); + + for(auto k:found_indices) + delete item->takeChild(k); // delete items in the reverse order (because of the push_front()), so that indices are preserved +} + +void IdDialog::loadCircles(const std::list& groupInfo) +{ #ifdef ID_DEBUG std::cerr << "CirclesDialog::loadCircleGroupMeta()"; std::cerr << std::endl; #endif - std::list groupInfo; - std::list::iterator vit; - - if (!rsGxsCircles->getGroupSummary(token,groupInfo)) - { - std::cerr << "CirclesDialog::loadCircleGroupMeta() Error getting GroupMeta"; - std::cerr << std::endl; - mStateHelper->setActive(CIRCLESDIALOG_GROUPMETA, false); - return; - } - mStateHelper->setActive(CIRCLESDIALOG_GROUPMETA, true); + std::vector expanded_top_level_items; + std::set expanded_circle_items; + saveExpandedCircleItems(expanded_top_level_items,expanded_circle_items); + +#ifdef QT_BUG_CRASH_IN_TAKECHILD_WORKAROUND + // These 3 lines are normally not needed. But apparently a bug (in Qt ??) causes Qt to crash when takeChild() is called. If we remove everything from the + // tree widget before updating it, takeChild() is never called, but the all tree is filled again from scratch. This is less efficient obviously, and + // also collapses the tree. Because it is a *temporary* fix, I dont take the effort to save open/collapsed items yet. If we cannot find a proper way to fix + // this, then we'll need to implement the two missing functions to save open/collapsed items. + + ui->treeWidget_membership->clear(); + mExternalOtherCircleItem = NULL ; + mExternalBelongingCircleItem = NULL ; +#endif + /* add the top level item */ //QTreeWidgetItem *personalCirclesItem = new QTreeWidgetItem(); //personalCirclesItem->setText(0, tr("Personal Circles")); @@ -581,7 +643,7 @@ void IdDialog::loadCircleGroupMeta(const uint32_t &token) std::list own_identities ; rsIdentity->getOwnIds(own_identities) ; - for(vit = groupInfo.begin(); vit != groupInfo.end();++vit) + for(auto vit = groupInfo.begin(); vit != groupInfo.end();++vit) { #ifdef ID_DEBUG std::cerr << "CirclesDialog::loadCircleGroupMeta() GroupId: " << vit->mGroupId << " Group: " << vit->mGroupName << std::endl; @@ -589,105 +651,30 @@ void IdDialog::loadCircleGroupMeta(const uint32_t &token) RsGxsCircleDetails details; rsGxsCircles->getCircleDetails(RsGxsCircleId(vit->mGroupId), details) ; - bool should_re_add = true ; bool am_I_in_circle = details.mAmIAllowed ; bool am_I_admin (vit->mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN) ; bool am_I_subscribed (vit->mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_SUBSCRIBED) ; - QTreeWidgetItem *item = NULL ; - #ifdef ID_DEBUG std::cerr << "Loaded info for circle " << vit->mGroupId << ". am_I_in_circle=" << am_I_in_circle << std::endl; #endif - // find already existing items for this circle - - // implement the search manually, because there's no find based on user role. - //QList clist = ui->treeWidget_membership->findItems( QString::fromStdString(vit->mGroupId.toStdString()), Qt::MatchExactly|Qt::MatchRecursive, CIRCLEGROUP_CIRCLE_COL_GROUPID); - QList clist ; - QString test_str = QString::fromStdString(vit->mGroupId.toStdString()) ; - for(QTreeWidgetItemIterator itt(ui->treeWidget_membership);*itt;++itt) - if( (*itt)->data(CIRCLEGROUP_CIRCLE_COL_GROUPID,Qt::UserRole).toString() == test_str) - clist.push_back(*itt) ; - - if(!clist.empty()) - { - // delete all duplicate items. This should not happen, but just in case it does. - - while(clist.size() > 1) - { -#ifdef ID_DEBUG - std::cerr << " more than 1 item correspond to this ID. Removing!" << std::endl; -#endif - delete clist.front() ; - } - - item = clist.front() ; - -#ifdef CIRCLE_MEMBERSHIP_CATEGORIES - if(am_I_in_circle && item->parent() != mExternalBelongingCircleItem) - { -#ifdef ID_DEBUG - std::cerr << " Existing circle is not in subscribed items although it is subscribed. Removing." << std::endl; -#endif - delete item ; - item = NULL ; - } - else if(!am_I_in_circle && item->parent() != mExternalOtherCircleItem) - { -#ifdef ID_DEBUG - std::cerr << " Existing circle is not in subscribed items although it is subscribed. Removing." << std::endl; -#endif - delete item ; - item = NULL ; - } - else -#endif - should_re_add = false ; // item already exists - } - - /* Add Widget, and request Pages */ - - if(should_re_add) - { - item = new QTreeWidgetItem(); - - item->setText(CIRCLEGROUP_CIRCLE_COL_GROUPNAME, QString::fromUtf8(vit->mGroupName.c_str())); - item->setData(CIRCLEGROUP_CIRCLE_COL_GROUPID,Qt::UserRole, QString::fromStdString(vit->mGroupId.toStdString())); - item->setData(CIRCLEGROUP_CIRCLE_COL_GROUPFLAGS, Qt::UserRole, QVariant(vit->mSubscribeFlags)); - -#ifdef CIRCLE_MEMBERSHIP_CATEGORIES - if(am_I_in_circle) - { -#ifdef ID_DEBUG - std::cerr << " adding item for circle " << vit->mGroupId << " to own circles"<< std::endl; -#endif - mExternalBelongingCircleItem->addChild(item); - } - else - { -#ifdef ID_DEBUG - std::cerr << " adding item for circle " << vit->mGroupId << " to others"<< std::endl; -#endif - mExternalOtherCircleItem->addChild(item); - } -#else - ui->treeWidget_membership->addTopLevelItem(item) ; -#endif - } - else if(item->text(CIRCLEGROUP_CIRCLE_COL_GROUPNAME) != QString::fromUtf8(vit->mGroupName.c_str())) - { -#ifdef ID_DEBUG - std::cerr << " Existing circle has a new name. Updating it in the tree." << std::endl; -#endif - item->setText(CIRCLEGROUP_CIRCLE_COL_GROUPNAME, QString::fromUtf8(vit->mGroupName.c_str())); - } - // just in case. + // Find already existing items for this circle, or create one. + QTreeWidgetItem *item = NULL ; + if(am_I_in_circle) + { + item = setChildItem(mExternalBelongingCircleItem,*vit); + removeChildItem(mExternalOtherCircleItem,*vit); + } + else + { + item = setChildItem(mExternalOtherCircleItem,*vit); + removeChildItem(mExternalBelongingCircleItem,*vit); + } item->setData(CIRCLEGROUP_CIRCLE_COL_GROUPFLAGS, Qt::UserRole, QVariant(vit->mSubscribeFlags)); QString tooltip ; tooltip += tr("Circle ID: ")+QString::fromStdString(vit->mGroupId.toStdString()) ; - tooltip += "\n"+tr("Visibility: "); if(details.mRestrictedCircleId == details.mCircleId) @@ -740,14 +727,14 @@ void IdDialog::loadCircleGroupMeta(const uint32_t &token) std::cerr << " updating status of all identities for this circle:" << std::endl; #endif // remove any identity that has an item, but no subscription flag entry - std::vector to_delete ; + std::list to_delete ; for(uint32_t k=0; k < (uint32_t)item->childCount(); ++k) if(details.mSubscriptionFlags.find(RsGxsId(item->child(k)->data(CIRCLEGROUP_CIRCLE_COL_GROUPID,Qt::UserRole).toString().toStdString())) == details.mSubscriptionFlags.end()) - to_delete.push_back(item->child(k)); + to_delete.push_front(k); // front, so that we delete starting from the last one - for(uint32_t k=0;ktakeChild(index); // delete items starting from the largest index, because otherwise the count changes while deleting... for(std::map::const_iterator it(details.mSubscriptionFlags.begin());it!=details.mSubscriptionFlags.end();++it) { @@ -761,13 +748,13 @@ void IdDialog::loadCircleGroupMeta(const uint32_t &token) #ifdef ID_DEBUG std::cerr << "invited: " << invited << ", subscription: " << subscrb ; #endif - QTreeWidgetItem *subitem = NULL ; + int subitem_index = -1; // see if the item already exists for(uint32_t k=0; k < (uint32_t)item->childCount(); ++k) if(item->child(k)->data(CIRCLEGROUP_CIRCLE_COL_GROUPID,Qt::UserRole).toString().toStdString() == it->first.toStdString()) { - subitem = item->child(k); + subitem_index = k; #ifdef ID_DEBUG std::cerr << " found existing sub item." << std::endl; #endif @@ -776,8 +763,8 @@ void IdDialog::loadCircleGroupMeta(const uint32_t &token) if(!(invited || subscrb)) { - if(subitem != NULL) - delete subitem ; + if(subitem_index >= 0) + delete item->takeChild(subitem_index) ; #ifdef ID_DEBUG std::cerr << ". not relevant. Skipping." << std::endl; #endif @@ -785,31 +772,37 @@ void IdDialog::loadCircleGroupMeta(const uint32_t &token) } // remove item if flags are not ok. - if(subitem && subitem->data(CIRCLEGROUP_CIRCLE_COL_GROUPFLAGS, Qt::UserRole).toUInt() != it->second) + if(subitem_index >= 0 && item->child(subitem_index)->data(CIRCLEGROUP_CIRCLE_COL_GROUPFLAGS, Qt::UserRole).toUInt() != it->second) { - delete subitem ; - subitem = NULL ; + delete item->takeChild(subitem_index) ; + subitem_index = -1 ; } - if(!subitem) + QTreeWidgetItem *subitem(NULL); + + if(subitem_index == -1) { #ifdef ID_DEBUG std::cerr << " no existing sub item. Creating new one." << std::endl; #endif - subitem = new QTreeWidgetItem(item); + subitem = new RSTreeWidgetItem(NULL); + subitem->setData(CIRCLEGROUP_CIRCLE_COL_GROUPNAME,Qt::UserRole,QString::fromStdString(it->first.toStdString())); + //Icon PlaceHolder + subitem->setIcon(CIRCLEGROUP_CIRCLE_COL_GROUPNAME,FilesDefs::getIconFromQtResourcePath(":/icons/png/anonymous.png")); RsIdentityDetails idd ; - bool has_id = rsIdentity->getIdDetails(it->first,idd) ; + //bool has_id = + rsIdentity->getIdDetails(it->first,idd) ; - QPixmap pixmap ; + // QPixmap pixmap ; - if(idd.mAvatar.mSize == 0 || !GxsIdDetails::loadPixmapFromData(idd.mAvatar.mData, idd.mAvatar.mSize, pixmap,GxsIdDetails::SMALL)) - pixmap = GxsIdDetails::makeDefaultIcon(it->first,GxsIdDetails::SMALL) ; + // if(idd.mAvatar.mSize == 0 || !GxsIdDetails::loadPixmapFromData(idd.mAvatar.mData, idd.mAvatar.mSize, pixmap,GxsIdDetails::SMALL)) + // pixmap = GxsIdDetails::makeDefaultIcon(it->first,GxsIdDetails::SMALL) ; - if(has_id) - subitem->setText(CIRCLEGROUP_CIRCLE_COL_GROUPNAME, QString::fromUtf8(idd.mNickname.c_str())) ; - else - subitem->setText(CIRCLEGROUP_CIRCLE_COL_GROUPNAME, tr("Unknown ID:")+QString::fromStdString(it->first.toStdString())) ; + // if(has_id) + // subitem->setText(CIRCLEGROUP_CIRCLE_COL_GROUPNAME, QString::fromUtf8(idd.mNickname.c_str())) ; + // else + // subitem->setText(CIRCLEGROUP_CIRCLE_COL_GROUPNAME, tr("Unknown ID:")+QString::fromStdString(it->first.toStdString())) ; QString tooltip ; tooltip += tr("Identity ID: ")+QString::fromStdString(it->first.toStdString()) ; @@ -830,10 +823,12 @@ void IdDialog::loadCircleGroupMeta(const uint32_t &token) subitem->setData(CIRCLEGROUP_CIRCLE_COL_GROUPFLAGS, Qt::UserRole, QVariant(it->second)) ; subitem->setData(CIRCLEGROUP_CIRCLE_COL_GROUPID, Qt::UserRole, QString::fromStdString(it->first.toStdString())) ; - subitem->setIcon(RSID_COL_NICKNAME, QIcon(pixmap)); + //subitem->setIcon(RSID_COL_NICKNAME, QIcon(pixmap)); item->addChild(subitem) ; } + else + subitem = item->child(subitem_index); if(invited && !subscrb) { @@ -862,127 +857,31 @@ void IdDialog::loadCircleGroupMeta(const uint32_t &token) } } - // The bullet colors below are for the *Membership*. This is independent from admin rights, which cannot be shown as a color. - // Admin/non admin is shows using Bold font. - + // The bullet colors below are for the *Membership*. This is independent from admin rights, which cannot be shown as a color. + // Admin/non admin is shows using Bold font. + if(am_I_in_circle) - item->setIcon(CIRCLEGROUP_CIRCLE_COL_GROUPNAME,QIcon(IMAGE_MEMBER)) ; + item->setIcon(CIRCLEGROUP_CIRCLE_COL_GROUPNAME,FilesDefs::getIconFromQtResourcePath(IMAGE_MEMBER)) ; else if(am_I_invited || am_I_pending) - item->setIcon(CIRCLEGROUP_CIRCLE_COL_GROUPNAME,QIcon(IMAGE_INVITED)) ; + item->setIcon(CIRCLEGROUP_CIRCLE_COL_GROUPNAME,FilesDefs::getIconFromQtResourcePath(IMAGE_INVITED)) ; else - item->setIcon(CIRCLEGROUP_CIRCLE_COL_GROUPNAME,QIcon(IMAGE_UNKNOWN)) ; + item->setIcon(CIRCLEGROUP_CIRCLE_COL_GROUPNAME,FilesDefs::getIconFromQtResourcePath(IMAGE_UNKNOWN)) ; } + restoreExpandedCircleItems(expanded_top_level_items,expanded_circle_items); } -static void mark_matching_tree(QTreeWidget *w, const std::set& members, int col) -{ - w->selectionModel()->clearSelection() ; - - for(std::set::const_iterator it(members.begin());it!=members.end();++it) - { - QList clist = w->findItems( QString::fromStdString((*it).toStdString()), Qt::MatchExactly|Qt::MatchRecursive, col); - - foreach(QTreeWidgetItem* item, clist) - item->setSelected(true) ; - } -} - -void IdDialog::loadCircleGroupData(const uint32_t& token) -{ -#ifdef ID_DEBUG - std::cerr << "Loading circle info" << std::endl; -#endif - - std::vector circle_grp_v ; - rsGxsCircles->getGroupData(token, circle_grp_v); - - if (circle_grp_v.empty()) - { - std::cerr << "(EE) unexpected empty result from getGroupData. Cannot process circle now!" << std::endl; - return ; - } - - if (circle_grp_v.size() != 1) - { - std::cerr << "(EE) very weird result from getGroupData. Should get exactly one circle" << std::endl; - return ; - } - - RsGxsCircleGroup cg = circle_grp_v.front(); - RsGxsCircleId requested_cid(cg.mMeta.mGroupId) ; - - QTreeWidgetItem *item = ui->treeWidget_membership->currentItem(); - - RsGxsCircleId id ; - if(!getItemCircleId(item,id)) - return ; - - if(requested_cid != id) - { - std::cerr << "(WW) not the same circle. Dropping request." << std::endl; - return ; - } - - /* now mark all the members */ - - std::set members = cg.mInvitedMembers; - - mark_matching_tree(ui->idTreeWidget, members, RSID_COL_KEYID) ; - - mStateHelper->setLoading(CIRCLESDIALOG_GROUPDATA, false); -} - -void IdDialog::updateCircleGroup(const uint32_t& token) -{ -#ifdef ID_DEBUG - std::cerr << "Loading circle info" << std::endl; -#endif - - std::vector circle_grp_v ; - rsGxsCircles->getGroupData(token, circle_grp_v); - - if (circle_grp_v.empty()) - { - std::cerr << "(EE) unexpected empty result from getGroupData. Cannot process circle now!" << std::endl; - return ; - } - - if (circle_grp_v.size() != 1) - { - std::cerr << "(EE) very weird result from getGroupData. Should get exactly one circle" << std::endl; - return ; - } - - RsGxsCircleGroup cg = circle_grp_v.front(); - - /* now mark all the members */ - - std::set members = cg.mInvitedMembers; - - std::map::iterator it = mCircleUpdates.find(token) ; - - if(it == mCircleUpdates.end()) - { - std::cerr << "(EE) Cannot find token " << token << " to perform group update!" << std::endl; - return ; - } - - if(it->second.action == CircleUpdateOrder::GRANT_MEMBERSHIP) - cg.mInvitedMembers.insert(it->second.gxs_id) ; - else if(it->second.action == CircleUpdateOrder::REVOKE_MEMBERSHIP) - cg.mInvitedMembers.erase(it->second.gxs_id) ; - else - { - std::cerr << "(EE) unrecognised membership action to perform: " << it->second.action << "!" << std::endl; - return ; - } - - uint32_t token2 ; - rsGxsCircles->updateGroup(token2,cg) ; - - mCircleUpdates.erase(it) ; - requestCircleGroupMeta(); -} +//static void mark_matching_tree(QTreeWidget *w, const std::set& members, int col) +//{ +// w->selectionModel()->clearSelection() ; +// +// for(std::set::const_iterator it(members.begin());it!=members.end();++it) +// { +// QList clist = w->findItems( QString::fromStdString((*it).toStdString()), Qt::MatchExactly|Qt::MatchRecursive, col); +// +// foreach(QTreeWidgetItem* item, clist) +// item->setSelected(true) ; +// } +//} bool IdDialog::getItemCircleId(QTreeWidgetItem *item,RsGxsCircleId& id) { @@ -1007,8 +906,6 @@ void IdDialog::createExternalCircle() CreateCircleDialog dlg; dlg.editNewId(true); dlg.exec(); - - requestCircleGroupMeta(); // update GUI } void IdDialog::showEditExistingCircle() { @@ -1023,60 +920,52 @@ void IdDialog::showEditExistingCircle() dlg.editExistingId(RsGxsGroupId(id),true,!(subscribe_flags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN)) ; dlg.exec(); - - requestCircleGroupMeta(); // update GUI } void IdDialog::grantCircleMembership() { - RsGxsCircleId circle_id ; + RsGxsCircleId circle_id ; if(!getItemCircleId(ui->treeWidget_membership->currentItem(),circle_id)) return; - RsGxsId gxs_id_to_revoke(qobject_cast(sender())->data().toString().toStdString()); + RsGxsId gxs_id_to_grant(qobject_cast(sender())->data().toString().toStdString()); - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; + RsThread::async([circle_id,gxs_id_to_grant]() + { + // 1 - set message data in p3GxsCircles - std::list grps ; - grps.push_back(RsGxsGroupId(circle_id)); - - uint32_t token; - mCircleQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, grps, CIRCLESDIALOG_GROUPUPDATE); - - CircleUpdateOrder c ; - c.token = token ; - c.gxs_id = gxs_id_to_revoke ; - c.action = CircleUpdateOrder::GRANT_MEMBERSHIP ; - - mCircleUpdates[token] = c ; + rsGxsCircles->inviteIdsToCircle(std::set( { gxs_id_to_grant } ),circle_id); + }); } void IdDialog::revokeCircleMembership() { - RsGxsCircleId circle_id ; + RsGxsCircleId circle_id ; if(!getItemCircleId(ui->treeWidget_membership->currentItem(),circle_id)) return; + if(circle_id.isNull()) + { + RsErr() << __PRETTY_FUNCTION__ << " : got a null circle ID. Cannot revoke an identity from that circle!" << std::endl; + return ; + } + RsGxsId gxs_id_to_revoke(qobject_cast(sender())->data().toString().toStdString()); - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; + if(gxs_id_to_revoke.isNull()) + RsErr() << __PRETTY_FUNCTION__ << " : got a null ID. Cannot revoke it from circle " << circle_id << "!" << std::endl; + else + RsThread::async([circle_id,gxs_id_to_revoke]() + { + // 1 - get message data from p3GxsForums - std::list grps ; - grps.push_back(RsGxsGroupId(circle_id)); + std::set ids; + ids.insert(gxs_id_to_revoke); - uint32_t token; - mCircleQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, grps, CIRCLESDIALOG_GROUPUPDATE); - - CircleUpdateOrder c ; - c.token = token ; - c.gxs_id = gxs_id_to_revoke ; - c.action = CircleUpdateOrder::REVOKE_MEMBERSHIP ; - - mCircleUpdates[token] = c ; + rsGxsCircles->revokeIdsFromCircle(ids,circle_id); + }); } void IdDialog::acceptCircleSubscription() @@ -1128,11 +1017,11 @@ void IdDialog::CircleListCustomPopupMenu( QPoint ) #endif if(group_flags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN) { - contextMnu.addAction(QIcon(IMAGE_EDIT), tr("Edit Circle"), this, SLOT(showEditExistingCircle())); + contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_EDIT), tr("Edit Circle"), this, SLOT(showEditExistingCircle())); am_I_circle_admin = true ; } else - contextMnu.addAction(QIcon(IMAGE_INFO), tr("See details"), this, SLOT(showEditExistingCircle())); + contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_INFO), tr("See details"), this, SLOT(showEditExistingCircle())); #ifdef CIRCLE_MEMBERSHIP_CATEGORIES } #endif @@ -1174,7 +1063,7 @@ void IdDialog::CircleListCustomPopupMenu( QPoint ) static const int CANCEL = 3 ; // Admin list: no Subscription request: yes const QString menu_titles[4] = { tr("Request subscription"), tr("Accept circle invitation"), tr("Quit this circle"),tr("Cancel subscribe request")} ; - const QString image_names[4] = { ":/images/edit_add24.png",":/images/accepted16.png",":/images/door_in.png",":/images/cancel.png" } ; + const QString image_names[4] = { ":/images/edit_add24.png",":/images/accepted16.png",":/icons/png/enter.png",":/images/cancel.png" } ; std::vector< std::vector > ids(4) ; @@ -1220,9 +1109,9 @@ void IdDialog::CircleListCustomPopupMenu( QPoint ) QAction *action ; if(is_circle) - action = new QAction(QIcon(image_names[i]), menu_titles[i] + " " + id_name,this) ; + action = new QAction(FilesDefs::getIconFromQtResourcePath(image_names[i]), menu_titles[i] + " " + id_name,this) ; else - action = new QAction(QIcon(image_names[i]), menu_titles[i],this) ; + action = new QAction(FilesDefs::getIconFromQtResourcePath(image_names[i]), menu_titles[i],this) ; if(i <2) QObject::connect(action,SIGNAL(triggered()), this, SLOT(acceptCircleSubscription())); @@ -1245,7 +1134,7 @@ void IdDialog::CircleListCustomPopupMenu( QPoint ) else id_name = tr("for identity ")+QString::fromStdString(ids[i][j].toStdString()) ; - QAction *action = new QAction(QIcon(image_names[i]), id_name,this) ; + QAction *action = new QAction(FilesDefs::getIconFromQtResourcePath(image_names[i]), id_name,this) ; if(i <2) QObject::connect(action,SIGNAL(triggered()), this, SLOT(acceptCircleSubscription())); @@ -1289,92 +1178,6 @@ void IdDialog::CircleListCustomPopupMenu( QPoint ) contextMnu.exec(QCursor::pos()); } -#ifdef SUSPENDED -static void set_item_background(QTreeWidgetItem *item, uint32_t type) -{ - QBrush brush; - switch(type) - { - default: - case CLEAR_BACKGROUND: - brush = QBrush(Qt::white); - break; - case GREEN_BACKGROUND: - brush = QBrush(Qt::green); - break; - case BLUE_BACKGROUND: - brush = QBrush(Qt::blue); - break; - case RED_BACKGROUND: - brush = QBrush(Qt::red); - break; - case GRAY_BACKGROUND: - brush = QBrush(Qt::gray); - break; - } - item->setBackground (0, brush); -} - -static void update_children_background(QTreeWidgetItem *item, uint32_t type) -{ - int count = item->childCount(); - for(int i = 0; i < count; ++i) - { - QTreeWidgetItem *child = item->child(i); - - if (child->childCount() > 0) - { - update_children_background(child, type); - } - set_item_background(child, type); - } -} - -static void set_tree_background(QTreeWidget *tree, uint32_t type) -{ - std::cerr << "CirclesDialog set_tree_background()"; - std::cerr << std::endl; - - /* grab all toplevel */ - int count = tree->topLevelItemCount(); - for(int i = 0; i < count; ++i) - { - QTreeWidgetItem *item = tree->topLevelItem(i); - /* resursively clear child backgrounds */ - update_children_background(item, type); - set_item_background(item, type); - } -} - -static void check_mark_item(QTreeWidgetItem *item, const std::set &names, uint32_t col, uint32_t type) -{ - QString coltext = item->text(col); - RsPgpId colstr ( coltext.toStdString()); - if (names.end() != names.find(colstr)) - { - set_item_background(item, type); - std::cerr << "CirclesDialog check_mark_item: found match: " << colstr; - std::cerr << std::endl; - } -} - -void IdDialog::circle_selected() -{ - QTreeWidgetItem *item = ui->treeWidget_membership->currentItem(); - -#ifdef ID_DEBUG - std::cerr << "CirclesDialog::circle_selected() valid circle chosen"; - std::cerr << std::endl; -#endif - //set_item_background(item, BLUE_BACKGROUND); - - QString coltext = (item->parent()->parent())? (item->parent()->data(CIRCLEGROUP_CIRCLE_COL_GROUPID,Qt::UserRole).toString()) : (item->data(CIRCLEGROUP_CIRCLE_COL_GROUPID,Qt::UserRole).toString()); - RsGxsCircleId id ( coltext.toStdString()) ; - - requestCircleGroupData(id) ; -} -#endif - IdDialog::~IdDialog() { rsEvents->unregisterEventsHandler(mEventHandlerId_identity); @@ -1384,7 +1187,6 @@ IdDialog::~IdDialog() processSettings(false); delete(ui); - delete(mIdQueue); } static QString getHumanReadableDuration(uint32_t seconds) @@ -1449,7 +1251,7 @@ void IdDialog::filterToggled(const bool &value) QAction *source = qobject_cast(QObject::sender()); if (source) { filter = source->data().toInt(); - requestIdList(); + updateIdList(); } } } @@ -1465,32 +1267,63 @@ void IdDialog::updateSelection() if (id != mId) { mId = id; - requestIdDetails(); - requestRepList(); + updateIdentity(); + //updateRepList(); } } -void IdDialog::requestIdList() +void IdDialog::updateIdList() { //Disable by default, will be enable by insertIdDetails() ui->removeIdentity->setEnabled(false); ui->editIdentity->setEnabled(false); - if (!mIdQueue) - return; + //int accept = filter; - mStateHelper->setLoading(IDDIALOG_IDLIST, true); + RsThread::async([this]() + { + // 1 - get message data from p3GxsForums - mIdQueue->cancelActiveRequestTokens(IDDIALOG_IDLIST); +#ifdef DEBUG_FORUMS + std::cerr << "Retrieving post data for post " << mThreadId << std::endl; +#endif - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; + std::list identity_metas ; - uint32_t token; + if (!rsIdentity->getIdentitiesSummaries(identity_metas)) + { + std::cerr << "IdDialog::insertIdList() Error getting GroupData" << std::endl; + return; + } - mIdQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, IDDIALOG_IDLIST); + std::set ids; + for(auto it(identity_metas.begin());it!=identity_metas.end();++it) + ids.insert(RsGxsId((*it).mGroupId)); + + std::vector groups; + + if(!rsIdentity->getIdentitiesInfo(ids,groups)) + { + std::cerr << "IdDialog::insertIdList() Error getting identities info" << std::endl; + return; + } + + auto ids_set = std::make_unique>(); + for(auto it(groups.begin()); it!=groups.end(); ++it) + (*ids_set)[(*it).mMeta.mGroupId] = *it; + + RsQThreadUtils::postToObject( + [ids_set = std::move(ids_set), this] () + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ + loadIdentities(*ids_set); + }, this ); + + }); } bool IdDialog::fillIdListItem(const RsGxsIdGroup& data, QTreeWidgetItem *&item, const RsPgpId &ownPgpId, int accept) @@ -1500,8 +1333,7 @@ bool IdDialog::fillIdListItem(const RsGxsIdGroup& data, QTreeWidgetItem *&item, RsIdentityDetails idd ; rsIdentity->getIdDetails(RsGxsId(data.mMeta.mGroupId),idd) ; - bool isBanned = idd.mReputation.mOverallReputationLevel == - RsReputationLevel::LOCALLY_NEGATIVE; + bool isBanned = idd.mReputation.mOverallReputationLevel == RsReputationLevel::LOCALLY_NEGATIVE; uint32_t item_flags = 0; /* do filtering */ @@ -1548,29 +1380,33 @@ bool IdDialog::fillIdListItem(const RsGxsIdGroup& data, QTreeWidgetItem *&item, return false; if (!item) + { item = new TreeWidgetItem(); + } - item->setText(RSID_COL_NICKNAME, QString::fromUtf8(data.mMeta.mGroupName.c_str()).left(RSID_MAXIMUM_NICKNAME_SIZE)); - item->setText(RSID_COL_KEYID, QString::fromStdString(data.mMeta.mGroupId.toStdString())); - - if(isBanned) - { - item->setForeground(RSID_COL_NICKNAME,QBrush(Qt::red)); - item->setForeground(RSID_COL_KEYID,QBrush(Qt::red)); - item->setForeground(RSID_COL_IDTYPE,QBrush(Qt::red)); - item->setForeground(RSID_COL_VOTES,QBrush(Qt::red)); - } - else - { - item->setForeground(RSID_COL_NICKNAME,QBrush(Qt::black)); - item->setForeground(RSID_COL_KEYID,QBrush(Qt::black)); - item->setForeground(RSID_COL_IDTYPE,QBrush(Qt::black)); - item->setForeground(RSID_COL_VOTES,QBrush(Qt::black)); - } + item->setText(RSID_COL_NICKNAME, QString::fromUtf8(data.mMeta.mGroupName.c_str()).left(RSID_MAXIMUM_NICKNAME_SIZE)); + item->setData(RSID_COL_NICKNAME, Qt::UserRole, QString::fromStdString(data.mMeta.mGroupId.toStdString())); + item->setText(RSID_COL_KEYID, QString::fromStdString(data.mMeta.mGroupId.toStdString())); - item->setData(RSID_COL_KEYID, Qt::UserRole,QVariant(item_flags)) ; - item->setTextAlignment(RSID_COL_VOTES, Qt::AlignRight | Qt::AlignVCenter); + if(isBanned) + { + //TODO (Phenom): Add qproperty for these text colors in stylesheets + item->setData(RSID_COL_NICKNAME, Qt::ForegroundRole, QColor(Qt::red)); + item->setData(RSID_COL_KEYID , Qt::ForegroundRole, QColor(Qt::red)); + item->setData(RSID_COL_IDTYPE , Qt::ForegroundRole, QColor(Qt::red)); + item->setData(RSID_COL_VOTES , Qt::ForegroundRole, QColor(Qt::red)); + } + else + { + item->setData(RSID_COL_NICKNAME, Qt::ForegroundRole, QVariant()); + item->setData(RSID_COL_KEYID , Qt::ForegroundRole, QVariant()); + item->setData(RSID_COL_IDTYPE , Qt::ForegroundRole, QVariant()); + item->setData(RSID_COL_VOTES , Qt::ForegroundRole, QVariant()); + } + + item->setData(RSID_COL_KEYID, Qt::UserRole,QVariant(item_flags)) ; + item->setTextAlignment(RSID_COL_VOTES, Qt::AlignRight | Qt::AlignVCenter); item->setData( RSID_COL_VOTES,Qt::DecorationRole, static_cast(idd.mReputation.mOverallReputationLevel)); @@ -1589,28 +1425,31 @@ bool IdDialog::fillIdListItem(const RsGxsIdGroup& data, QTreeWidgetItem *&item, QString tooltip = tr("This identity is owned by you"); - if(idd.mFlags & RS_IDENTITY_FLAGS_IS_DEPRECATED) - { - item->setForeground(RSID_COL_NICKNAME,QBrush(Qt::red)); - item->setForeground(RSID_COL_KEYID,QBrush(Qt::red)); - item->setForeground(RSID_COL_IDTYPE,QBrush(Qt::red)); + if(idd.mFlags & RS_IDENTITY_FLAGS_IS_DEPRECATED) + { + //TODO (Phenom): Add qproperty for these text colors in stylesheets + item->setData(RSID_COL_NICKNAME, Qt::ForegroundRole, QColor(Qt::red)); + item->setData(RSID_COL_KEYID , Qt::ForegroundRole, QColor(Qt::red)); + item->setData(RSID_COL_IDTYPE , Qt::ForegroundRole, QColor(Qt::red)); - tooltip += tr("\nThis identity has a unsecure fingerprint (It's probably quite old).\nYou should get rid of it now and use a new one.\nThese identities will soon be not supported anymore.") ; - } + tooltip += tr("\nThis identity has a unsecure fingerprint (It's probably quite old).\nYou should get rid of it now and use a new one.\nThese identities will soon be not supported anymore.") ; + } item->setToolTip(RSID_COL_NICKNAME, tooltip) ; item->setToolTip(RSID_COL_KEYID, tooltip) ; item->setToolTip(RSID_COL_IDTYPE, tooltip) ; } - QPixmap pixmap ; + //QPixmap pixmap ; + // + //if(data.mImage.mSize == 0 || !GxsIdDetails::loadPixmapFromData(data.mImage.mData, data.mImage.mSize, pixmap,GxsIdDetails::SMALL)) + // pixmap = GxsIdDetails::makeDefaultIcon(RsGxsId(data.mMeta.mGroupId),GxsIdDetails::SMALL) ; + // + //item->setIcon(RSID_COL_NICKNAME, QIcon(pixmap)); + // Icon Place Holder + item->setIcon(RSID_COL_NICKNAME,FilesDefs::getIconFromQtResourcePath(":/icons/png/anonymous.png")); - if(data.mImage.mSize == 0 || !GxsIdDetails::loadPixmapFromData(data.mImage.mData, data.mImage.mSize, pixmap,GxsIdDetails::SMALL)) - pixmap = GxsIdDetails::makeDefaultIcon(RsGxsId(data.mMeta.mGroupId),GxsIdDetails::SMALL) ; - - item->setIcon(RSID_COL_NICKNAME, QIcon(pixmap)); - - QString tooltip; + QString tooltip; if (data.mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID_kept_for_compatibility) { @@ -1652,8 +1491,10 @@ bool IdDialog::fillIdListItem(const RsGxsIdGroup& data, QTreeWidgetItem *&item, return true; } -void IdDialog::insertIdList(uint32_t token) +void IdDialog::loadIdentities(const std::map& ids_set_const) { + auto ids_set(ids_set_const); + //First: Get current item to restore after RsGxsGroupId oldCurrentId = mIdToNavigate; { @@ -1662,42 +1503,14 @@ void IdDialog::insertIdList(uint32_t token) oldCurrentId = RsGxsGroupId(oldCurrent->text(RSID_COL_KEYID).toStdString()); } } - - mStateHelper->setLoading(IDDIALOG_IDLIST, false); - - int accept = filter; - - //RsGxsIdGroup data; - std::vector datavector; - //std::vector::iterator vit; - - if (!rsIdentity->getGroupData(token, datavector)) - { -#ifdef ID_DEBUG - std::cerr << "IdDialog::insertIdList() Error getting GroupData"; - std::cerr << std::endl; -#endif - - mStateHelper->setActive(IDDIALOG_IDLIST, false); - mStateHelper->clear(IDDIALOG_IDLIST); - clearPerson(); - - return; - } - - // turn that vector into a std::set, to avoid a linear search - - std::map ids_set ; - - for(uint32_t i=0;isetActive(IDDIALOG_IDLIST, true); RsPgpId ownPgpId = rsPeers->getGPGOwnId(); // Update existing and remove not existing items - // Also remove items that do not have the correct parent + // Also remove items that do not have the correct parent QTreeWidgetItemIterator itemIterator(ui->idTreeWidget); QTreeWidgetItem *item = NULL; @@ -1705,27 +1518,30 @@ void IdDialog::insertIdList(uint32_t token) while ((item = *itemIterator) != NULL) { ++itemIterator; - std::map::iterator it = ids_set.find(RsGxsGroupId(item->text(RSID_COL_KEYID).toStdString())) ; + auto it = ids_set.find(RsGxsGroupId(item->text(RSID_COL_KEYID).toStdString())) ; if(it == ids_set.end()) { if(item != allItem && item != contactsItem && item != ownItem) delete(item); - - continue ; - } - - QTreeWidgetItem *parent_item = item->parent() ; - - if( (parent_item == allItem && it->second.mIsAContact) || (parent_item == contactsItem && !it->second.mIsAContact)) - { - delete item ; // do not remove from the list, so that it is added again in the correct place. - continue ; - } - + + continue ; + } + + QTreeWidgetItem *parent_item = item->parent() ; + +// if(it->second.mMeta.mPublishTs > time(NULL) - 20 || it->second.mMeta.mGroupId == RsGxsGroupId("3de2172503675206b3a23c997e5ee688")) +// std::cerr << "Captured ID " <second.mMeta.mGroupId << std::endl; + + if( (parent_item == allItem && it->second.mIsAContact) || (parent_item == contactsItem && !it->second.mIsAContact)) + { + delete item ; // do not remove from the list, so that it is added again in the correct place. + continue ; + } + if (!fillIdListItem(it->second, item, ownPgpId, accept)) delete(item); - + ids_set.erase(it); // erase, so it is not considered to be a new item } @@ -1755,22 +1571,29 @@ void IdDialog::insertIdList(uint32_t token) contactsItem->addChild(item); else allItem->addChild(item); + } } /* count items */ int itemCount = contactsItem->childCount() + allItem->childCount() + ownItem->childCount(); ui->label_count->setText( "(" + QString::number( itemCount ) + ")" ); + + int contactsCount = contactsItem->childCount() ; + int allCount = allItem->childCount() ; + int ownCount = ownItem->childCount(); + + contactsItem->setText(0, tr("My contacts") + " (" + QString::number( contactsCount ) + ")" ); + allItem->setText(0, tr("All") + " (" + QString::number( allCount ) + ")" ); + ownItem->setText(0, tr("My own identities") + " (" + QString::number( ownCount ) + ")" ); navigate(RsGxsId(oldCurrentId)); filterIds(); updateSelection(); } -void IdDialog::requestIdDetails() +void IdDialog::updateIdentity() { - mIdQueue->cancelActiveRequestTokens(IDDIALOG_IDDETAILS); - if (mId.isNull()) { mStateHelper->setActive(IDDIALOG_IDDETAILS, false); @@ -1783,53 +1606,48 @@ void IdDialog::requestIdDetails() mStateHelper->setLoading(IDDIALOG_IDDETAILS, true); - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; + RsThread::async([this]() + { +#ifdef ID_DEBUG + std::cerr << "Retrieving post data for identity " << mThreadId << std::endl; +#endif - uint32_t token; - std::list groupIds; - groupIds.push_back(mId); + std::set ids( { RsGxsId(mId) } ) ; + std::vector ids_data; - mIdQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, groupIds, IDDIALOG_IDDETAILS); + if(!rsIdentity->getIdentitiesInfo(ids,ids_data)) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve identities group info for id " << mId << std::endl; + return; + } + + if(ids_data.size() != 1) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve exactly one group info for id " << mId << std::endl; + return; + } + RsGxsIdGroup group(ids_data[0]); + + RsQThreadUtils::postToObject( [group,this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ + + loadIdentity(group); + + }, this ); + }); } -void IdDialog::insertIdDetails(uint32_t token) +void IdDialog::loadIdentity(RsGxsIdGroup data) { mStateHelper->setLoading(IDDIALOG_IDDETAILS, false); /* get details from libretroshare */ - RsGxsIdGroup data; - std::vector datavector; - if (!rsIdentity->getGroupData(token, datavector)) - { - mStateHelper->setActive(IDDIALOG_IDDETAILS, false); - mStateHelper->clear(IDDIALOG_REPLIST); - clearPerson(); - - ui->lineEdit_KeyId->setText("ERROR GETTING KEY!"); - - return; - } - - if (datavector.size() != 1) - { -#ifdef ID_DEBUG - std::cerr << "IdDialog::insertIdDetails() Invalid datavector size"; -#endif - - mStateHelper->setActive(IDDIALOG_IDDETAILS, false); - mStateHelper->clear(IDDIALOG_IDDETAILS); - clearPerson(); - - ui->lineEdit_KeyId->setText("INVALID DV SIZE"); - - return; - } mStateHelper->setActive(IDDIALOG_IDDETAILS, true); - data = datavector[0]; - /* get GPG Details from rsPeers */ RsPgpId ownPgpId = rsPeers->getGPGOwnId(); @@ -2044,10 +1862,17 @@ QString IdDialog::createUsageString(const RsIdentityUsage& u) const { case RsServiceType::CHANNELS: service_name = tr("Channels") ;service_type = RetroShareLink::TYPE_CHANNEL ; break ; case RsServiceType::FORUMS: service_name = tr("Forums") ; service_type = RetroShareLink::TYPE_FORUM ; break ; - case RsServiceType::POSTED: service_name = tr("Posted") ; service_type = RetroShareLink::TYPE_POSTED ; break ; + case RsServiceType::POSTED: service_name = tr("Boards") ; service_type = RetroShareLink::TYPE_POSTED ; break ; case RsServiceType::CHAT: service_name = tr("Chat") ; service_type = RetroShareLink::TYPE_CHAT_ROOM ; break ; + + case RsServiceType::GXS_TRANS: return tr("GxsMail author "); +#ifdef TODO + // We need a RS link for circles if we want to do that. + // + case RsServiceType::GXSCIRCLE: service_name = tr("GxsCircles"); service_type = RetroShareLink::TYPE_CIRCLES; break ; +#endif default: - service_name = tr("Unknown"); service_type = RetroShareLink::TYPE_UNKNOWN ; + service_name = tr("Unknown (service=")+QString::number((int)u.mServiceId,16)+")"; service_type = RetroShareLink::TYPE_UNKNOWN ; } switch(u.mUsageCode) @@ -2067,10 +1892,25 @@ QString IdDialog::createUsageString(const RsIdentityUsage& u) const return tr("Group author for group %1 in service %2").arg(QString::fromStdString(u.mGrpId.toStdString())).arg(service_name); break ; case RsIdentityUsage::MESSAGE_AUTHOR_SIGNATURE_VALIDATION: - case RsIdentityUsage::MESSAGE_AUTHOR_KEEP_ALIVE: // Identities are stamped regularly by crawlign the set of messages for all groups. That helps keepign the useful identities in hand. + case RsIdentityUsage::MESSAGE_AUTHOR_KEEP_ALIVE: // Identities are stamped regularly by crawling the set of messages for all groups. That helps keepign the useful identities in hand. { - RetroShareLink l = RetroShareLink::createGxsMessageLink(service_type,u.mGrpId,u.mMsgId,tr("Message/vote/comment")); - return tr("%1 in %2 tab").arg(l.toHtml()).arg(service_name) ; + RetroShareLink l; + + std::cerr << "Signature validation/keep alive signature:" << std::endl; + std::cerr << " service ID = " << std::hex << (uint16_t)u.mServiceId << std::dec << std::endl; + std::cerr << " u.mGrpId = " << u.mGrpId << std::endl; + std::cerr << " u.mMsgId = " << u.mMsgId << std::endl; + std::cerr << " u.mParentId = " << u.mParentId << std::endl; + std::cerr << " u.mThreadId = " << u.mThreadId << std::endl; + + if(service_type == RetroShareLink::TYPE_CHANNEL && !u.mThreadId.isNull()) + l = RetroShareLink::createGxsMessageLink(service_type,u.mGrpId,u.mThreadId,tr("Vote/comment")); + else if(service_type == RetroShareLink::TYPE_POSTED && !u.mThreadId.isNull()) + l = RetroShareLink::createGxsMessageLink(service_type,u.mGrpId,u.mThreadId,tr("Vote")); + else + l = RetroShareLink::createGxsMessageLink(service_type,u.mGrpId,u.mMsgId,tr("Message")); + + return tr("%1 in %2 service").arg(l.toHtml()).arg(service_name) ; } case RsIdentityUsage::CHAT_LOBBY_MSG_VALIDATION: // Chat lobby msgs are signed, so each time one comes, or a chat lobby event comes, a signature verificaiton happens. { @@ -2096,9 +1936,13 @@ QString IdDialog::createUsageString(const RsIdentityUsage& u) const { return tr("Signature in distant tunnel system."); } - case RsIdentityUsage::IDENTITY_DATA_UPDATE: // Group update on that identity data. Can be avatar, name, etc. + case RsIdentityUsage::IDENTITY_NEW_FROM_GXS_SYNC: // Group update on that identity data. Can be avatar, name, etc. { - return tr("Update of identity data."); + return tr("Received from GXS sync."); + } + case RsIdentityUsage::IDENTITY_NEW_FROM_DISCOVERY: // Own friend sended his own ids + { + return tr("Friend node identity received through discovery."); } case RsIdentityUsage::IDENTITY_GENERIC_SIGNATURE_CHECK: // Any signature verified for that identity { @@ -2106,11 +1950,18 @@ QString IdDialog::createUsageString(const RsIdentityUsage& u) const } case RsIdentityUsage::IDENTITY_GENERIC_SIGNATURE_CREATION: // Any signature made by that identity { - return tr("Generic signature."); + return tr("Generic signature creation (e.g. chat room message, global router,...)."); } case RsIdentityUsage::IDENTITY_GENERIC_ENCRYPTION: return tr("Generic encryption."); case RsIdentityUsage::IDENTITY_GENERIC_DECRYPTION: return tr("Generic decryption."); - case RsIdentityUsage::CIRCLE_MEMBERSHIP_CHECK: return tr("Membership verification in circle %1.").arg(QString::fromStdString(u.mGrpId.toStdString())); + case RsIdentityUsage::CIRCLE_MEMBERSHIP_CHECK: + { + RsGxsCircleDetails det; + if(rsGxsCircles->getCircleDetails(RsGxsCircleId(u.mGrpId),det)) + return tr("Membership verification in circle \"%1\" (%2).").arg(QString::fromUtf8(det.mCircleName.c_str())).arg(QString::fromStdString(u.mGrpId.toStdString())); + else + return tr("Membership verification in circle (ID=%1).").arg(QString::fromStdString(u.mGrpId.toStdString())); + } #warning TODO! csoler 2017-01-03: Add the different strings and translations here. default: @@ -2146,24 +1997,10 @@ void IdDialog::modifyReputation() std::cerr << std::endl; #endif -#ifdef SUSPENDED - // Cyril: apparently the old reputation system was in used here. It's based on GXS data exchange, and probably not - // very efficient because of this. - - uint32_t token; - if (!rsIdentity->submitOpinion(token, id, false, op)) - { -#ifdef ID_DEBUG - std::cerr << "IdDialog::modifyReputation() Error submitting Opinion"; - std::cerr << std::endl; -#endif - } -#endif - // trigger refresh when finished. // basic / anstype are not needed. - requestIdDetails(); - requestIdList(); + updateIdentity(); + updateIdList(); return; } @@ -2197,24 +2034,13 @@ void IdDialog::updateDisplay(bool complete) if (complete) { /* Fill complete */ - requestIdList(); + updateIdList(); //requestIdDetails(); - requestRepList(); + //requestRepList(); + updateCircles(); return; } - requestCircleGroupMeta(); - - std::set grpIds; - getAllGrpIds(grpIds); - if (!getGrpIds().empty()) { - requestIdList(); - - if (!mId.isNull() && grpIds.find(mId)!=grpIds.end()) { - requestIdDetails(); - requestRepList(); - } - } } void IdDialog::addIdentity() @@ -2277,138 +2103,6 @@ void IdDialog::filterIds() ui->idTreeWidget->filterItems(filterColumn, text); } -void IdDialog::requestRepList() -{ - // Removing this for the moment. - return; - - mStateHelper->setLoading(IDDIALOG_REPLIST, true); - - mIdQueue->cancelActiveRequestTokens(IDDIALOG_REPLIST); - - std::list groupIds; - groupIds.push_back(mId); - - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; - - uint32_t token; - mIdQueue->requestMsgInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, groupIds, IDDIALOG_REPLIST); -} - -void IdDialog::insertRepList(uint32_t token) -{ - Q_UNUSED(token) - mStateHelper->setLoading(IDDIALOG_REPLIST, false); - mStateHelper->setActive(IDDIALOG_REPLIST, true); -} - -void IdDialog::handleSerializedGroupData(uint32_t token) -{ - std::map serialized_group_map ; - - rsIdentity->getGroupSerializedData(token, serialized_group_map); - - if(serialized_group_map.size() < 1) - { - std::cerr << "(EE) Cannot get radix data " << std::endl; - return; - } - if(serialized_group_map.size() > 1) - { - std::cerr << "(EE) Too many results for serialized data" << std::endl; - return; - } - - RsGxsId gxs_id = serialized_group_map.begin()->first ; - std::string radix = serialized_group_map.begin()->second ; - - RsIdentityDetails details ; - - if(!rsIdentity->getIdDetails(gxs_id,details)) - { - std::cerr << "(EE) Cannot get id details for key " << gxs_id << std::endl; - return; - } - - QList urls ; - - RetroShareLink link = RetroShareLink::createIdentity(gxs_id,QString::fromUtf8(details.mNickname.c_str()),QString::fromStdString(radix)) ; - urls.push_back(link); - - RSLinkClipboard::copyLinks(urls) ; - - QMessageBox::information(NULL,tr("information"),tr("This identity link was copied to your clipboard. Paste it in a mail, or a message to transmit the identity to someone.")) ; -} - -void IdDialog::loadRequest(const TokenQueue * queue, const TokenRequest &req) -{ -#ifdef ID_DEBUG - std::cerr << "IdDialog::loadRequest() UserType: " << req.mUserType; - std::cerr << std::endl; -#endif - - if(queue == mIdQueue) - { - switch(req.mUserType) - { - case IDDIALOG_IDLIST: - insertIdList(req.mToken); - break; - - case IDDIALOG_IDDETAILS: - insertIdDetails(req.mToken); - break; - - case IDDIALOG_REPLIST: - insertRepList(req.mToken); - break; - - case IDDIALOG_SERIALIZED_GROUP: - handleSerializedGroupData(req.mToken); - break; - - case IDDIALOG_REFRESH: - // replaced by RsGxsUpdateBroadcastPage - // updateDisplay(true); - break; - default: - std::cerr << "IdDialog::loadRequest() ERROR"; - std::cerr << std::endl; - break; - } - } - - if(queue == mCircleQueue) - { -#ifdef ID_DEBUG - std::cerr << "CirclesDialog::loadRequest() UserType: " << req.mUserType; - std::cerr << std::endl; -#endif - - /* now switch on req */ - switch(req.mUserType) - { - case CIRCLESDIALOG_GROUPMETA: - loadCircleGroupMeta(req.mToken); - break; - - case CIRCLESDIALOG_GROUPDATA: - loadCircleGroupData(req.mToken); - break; - - case CIRCLESDIALOG_GROUPUPDATE: - updateCircleGroup(req.mToken); - break; - - default: - std::cerr << "CirclesDialog::loadRequest() ERROR: INVALID TYPE"; - std::cerr << std::endl; - break; - } - } -} - void IdDialog::IdListCustomPopupMenu( QPoint ) { QMenu *contextMenu = new QMenu(this); @@ -2480,7 +2174,7 @@ void IdDialog::IdListCustomPopupMenu( QPoint ) hbox->setSpacing(6); QLabel *iconLabel = new QLabel(widget); - QPixmap pix = QPixmap(":/images/user/friends24.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5); + QPixmap pix = FilesDefs::getPixmapFromQtResourcePath(":/images/user/friends24.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5); iconLabel->setPixmap(pix); iconLabel->setMaximumSize(iconLabel->frameSize().height() + pix.height(), pix.width()); hbox->addWidget(iconLabel); @@ -2501,7 +2195,7 @@ void IdDialog::IdListCustomPopupMenu( QPoint ) { if(own_identities.size() <= 1) { - QAction *action = contextMenu->addAction(QIcon(":/images/chat_24.png"), tr("Chat with this person"), this, SLOT(chatIdentity())); + QAction *action = contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(":/icons/png/chats.png"), tr("Chat with this person"), this, SLOT(chatIdentity())); if(own_identities.empty()) action->setEnabled(false) ; @@ -2510,7 +2204,7 @@ void IdDialog::IdListCustomPopupMenu( QPoint ) } else { - QMenu *mnu = contextMenu->addMenu(QIcon(":/images/chat_24.png"),tr("Chat with this person as...")) ; + QMenu *mnu = contextMenu->addMenu(FilesDefs::getIconFromQtResourcePath(":/icons/png/chats.png"),tr("Chat with this person as...")) ; for(std::list::const_iterator it=own_identities.begin();it!=own_identities.end();++it) { @@ -2527,38 +2221,37 @@ void IdDialog::IdListCustomPopupMenu( QPoint ) } } } - - if (n_selected_items==1) - contextMenu->addAction(QIcon(":/images/chat_24.png"),tr("Copy identity to clipboard"),this,SLOT(copyRetroshareLink())) ; - // always allow to send messages - contextMenu->addAction(QIcon(":/images/mail_new.png"), tr("Send message"), this, SLOT(sendMsg())); + contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(":/icons/mail/write-mail.png"), tr("Send message"), this, SLOT(sendMsg())); contextMenu->addSeparator(); if(n_is_a_contact == 0) contextMenu->addAction(QIcon(), tr("Add to Contacts"), this, SLOT(addtoContacts())); + if (n_selected_items==1) + contextMenu->addAction(QIcon(""),tr("Copy identity to clipboard"),this,SLOT(copyRetroshareLink())) ; + if(n_is_not_a_contact == 0) - contextMenu->addAction(QIcon(":/images/cancel.png"), tr("Remove from Contacts"), this, SLOT(removefromContacts())); + contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(":/images/cancel.png"), tr("Remove from Contacts"), this, SLOT(removefromContacts())); contextMenu->addSeparator(); if(n_positive_reputations == 0) // only unban when all items are banned - contextMenu->addAction(QIcon(":/icons/png/thumbs-up.png"), tr("Set positive opinion"), this, SLOT(positivePerson())); + contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(":/icons/png/thumbs-up.png"), tr("Set positive opinion"), this, SLOT(positivePerson())); if(n_neutral_reputations == 0) // only unban when all items are banned - contextMenu->addAction(QIcon(":/icons/png/thumbs-neutral.png"), tr("Set neutral opinion"), this, SLOT(neutralPerson())); + contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(":/icons/png/thumbs-neutral.png"), tr("Set neutral opinion"), this, SLOT(neutralPerson())); if(n_negative_reputations == 0) - contextMenu->addAction(QIcon(":/icons/png/thumbs-down.png"), tr("Set negative opinion"), this, SLOT(negativePerson())); + contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(":/icons/png/thumbs-down.png"), tr("Set negative opinion"), this, SLOT(negativePerson())); } if(one_item_owned_by_you && n_selected_items==1) { contextMenu->addSeparator(); - contextMenu->addAction(QIcon(":/images/chat_24.png"),tr("Copy identity to clipboard"),this,SLOT(copyRetroshareLink())) ; + contextMenu->addAction(QIcon(""),tr("Copy identity to clipboard"),this,SLOT(copyRetroshareLink())) ; contextMenu->addAction(ui->editIdentity); contextMenu->addAction(ui->removeIdentity); } @@ -2595,22 +2288,36 @@ void IdDialog::copyRetroshareLink() if(! rsIdentity->getIdDetails(gxs_id,details)) return ; - if (!mIdQueue) - return; + RsThread::async([gxs_id,details,this]() + { +#ifdef ID_DEBUG + std::cerr << "Retrieving post data for identity " << mThreadId << std::endl; +#endif + std::string radix,errMsg; - mStateHelper->setLoading(IDDIALOG_SERIALIZED_GROUP, true); + if(!rsIdentity->exportIdentityLink( radix, gxs_id, true, std::string(), errMsg)) + { + std::cerr << "Cannot retrieve identity data " << mId << " to create a link. Error:" << errMsg << std::endl; + return ; + } - mIdQueue->cancelActiveRequestTokens(IDDIALOG_SERIALIZED_GROUP); + RsQThreadUtils::postToObject( [radix,details,this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ - std::list ids ; - ids.push_back(RsGxsGroupId(gxs_id)) ; + QList urls ; - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_GROUP_SERIALIZED_DATA; + RetroShareLink link = RetroShareLink::createIdentity(details.mId,QString::fromUtf8(details.mNickname.c_str()),QString::fromStdString(radix)) ; + urls.push_back(link); - uint32_t token; + RSLinkClipboard::copyLinks(urls) ; - mIdQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, ids, IDDIALOG_SERIALIZED_GROUP); + QMessageBox::information(NULL,tr("information"),tr("This identity link was copied to your clipboard. Paste it in a mail, or a message to transmit the identity to someone.")) ; + + }, this ); + }); } void IdDialog::chatIdentity() @@ -2739,8 +2446,8 @@ void IdDialog::negativePerson() rsReputations->setOwnOpinion(RsGxsId(Id), RsOpinion::NEGATIVE); } - requestIdDetails(); - requestIdList(); + updateIdentity(); + updateIdList(); } void IdDialog::neutralPerson() @@ -2755,8 +2462,8 @@ void IdDialog::neutralPerson() rsReputations->setOwnOpinion(RsGxsId(Id), RsOpinion::NEUTRAL); } - requestIdDetails(); - requestIdList(); + updateIdentity(); + updateIdList(); } void IdDialog::positivePerson() { @@ -2770,8 +2477,8 @@ void IdDialog::positivePerson() rsReputations->setOwnOpinion(RsGxsId(Id), RsOpinion::POSITIVE); } - requestIdDetails(); - requestIdList(); + updateIdentity(); + updateIdList(); } void IdDialog::addtoContacts() @@ -2785,7 +2492,7 @@ void IdDialog::addtoContacts() rsIdentity->setAsRegularContact(RsGxsId(Id),true); } - requestIdList(); + updateIdList(); } void IdDialog::removefromContacts() @@ -2799,10 +2506,60 @@ QList selected_items = ui->idTreeWidget->selectedItems(); rsIdentity->setAsRegularContact(RsGxsId(Id),false); } - requestIdList(); + updateIdList(); } void IdDialog::on_closeInfoFrameButton_clicked() { ui->inviteFrame->setVisible(false); } + +// We need to use indexes here because saving items is not possible since they can be re-created. + +void IdDialog::saveExpandedCircleItems(std::vector& expanded_root_items, std::set& expanded_circle_items) const +{ + expanded_root_items.clear(); + expanded_root_items.resize(3,false); + expanded_circle_items.clear(); + + auto saveTopLevel = [&](const QTreeWidgetItem* top_level_item,uint32_t index){ + if(!top_level_item) + return; + + if(top_level_item->isExpanded()) + { + expanded_root_items[index] = true; + + for(int row=0;rowchildCount();++row) + if(top_level_item->child(row)->isExpanded()) + expanded_circle_items.insert(RsGxsCircleId(top_level_item->child(row)->data(CIRCLEGROUP_CIRCLE_COL_GROUPID,Qt::UserRole).toString().toStdString())); + } + }; + + saveTopLevel(mExternalBelongingCircleItem,0); + saveTopLevel(mExternalOtherCircleItem,1); + saveTopLevel(mMyCircleItem,2); +} + +void IdDialog::restoreExpandedCircleItems(const std::vector& expanded_root_items,const std::set& expanded_circle_items) +{ + auto restoreTopLevel = [=](QTreeWidgetItem* top_level_item,uint32_t index){ + if(!top_level_item) + return; + + top_level_item->setExpanded(expanded_root_items[index]); + + for(int row=0;rowchildCount();++row) + { + RsGxsCircleId circle_id(RsGxsCircleId(top_level_item->child(row)->data(CIRCLEGROUP_CIRCLE_COL_GROUPID,Qt::UserRole).toString().toStdString())); + bool expanded = expanded_circle_items.find(circle_id) != expanded_circle_items.end(); + + top_level_item->child(row)->setExpanded(expanded_circle_items.find(circle_id) != expanded_circle_items.end()); + } + }; + + restoreTopLevel(mExternalBelongingCircleItem,0); + restoreTopLevel(mExternalOtherCircleItem,1); + restoreTopLevel(mMyCircleItem,2); +} + diff --git a/retroshare-gui/src/gui/Identity/IdDialog.h b/retroshare-gui/src/gui/Identity/IdDialog.h index e4c73229d..dd3669f4c 100644 --- a/retroshare-gui/src/gui/Identity/IdDialog.h +++ b/retroshare-gui/src/gui/Identity/IdDialog.h @@ -25,8 +25,6 @@ #include -#include "util/TokenQueue.h" - #define IMAGE_IDDIALOG ":/icons/png/people.png" namespace Ui { @@ -45,7 +43,7 @@ struct CircleUpdateOrder uint32_t action ; }; -class IdDialog : public RsGxsUpdateBroadcastPage, public TokenResponse +class IdDialog : public MainPage { Q_OBJECT @@ -57,17 +55,19 @@ public: virtual QString pageName() const { return tr("People") ; } //MainPage virtual QString helpText() const { return ""; } //MainPage - void loadRequest(const TokenQueue *queue, const TokenRequest &req); - void navigate(const RsGxsId& gxs_id) ; // shows the info about this particular ID protected: virtual void updateDisplay(bool complete); - void loadCircleGroupMeta(const uint32_t &token); - void loadCircleGroupData(const uint32_t &token); - void updateCircleGroup(const uint32_t& token); + void updateIdList(); + void loadIdentities(const std::map &ids_set); + + void updateIdentity(); + void loadIdentity(RsGxsIdGroup id_data); + + void updateCircles(); + void loadCircles(const std::list& circle_metas); - void requestCircleGroupMeta(); //void requestCircleGroupData(const RsGxsCircleId& circle_id); bool getItemCircleId(QTreeWidgetItem *item,RsGxsCircleId& id) ; @@ -120,10 +120,6 @@ private: void processSettings(bool load); QString createUsageString(const RsIdentityUsage& u) const; - void requestIdDetails(); - void insertIdDetails(uint32_t token); - - void requestIdList(); void requestIdData(std::list &ids); bool fillIdListItem(const RsGxsIdGroup& data, QTreeWidgetItem *&item, const RsPgpId &ownPgpId, int accept); void insertIdList(uint32_t token); @@ -139,9 +135,6 @@ private: void clearPerson(); private: - TokenQueue *mIdQueue; - TokenQueue *mCircleQueue; - UIStateHelper *mStateHelper; QTreeWidgetItem *contactsItem; @@ -152,6 +145,9 @@ private: QTreeWidgetItem *mMyCircleItem; RsGxsUpdateBroadcastBase *mCirclesBroadcastBase ; + void saveExpandedCircleItems(std::vector &expanded_root_items, std::set& expanded_circle_items) const; + void restoreExpandedCircleItems(const std::vector& expanded_root_items,const std::set& expanded_circle_items); + std::map mCircleUpdates ; RsGxsGroupId mId; diff --git a/retroshare-gui/src/gui/Identity/IdDialog.ui b/retroshare-gui/src/gui/Identity/IdDialog.ui index 4aa73e097..127f7a85a 100644 --- a/retroshare-gui/src/gui/Identity/IdDialog.ui +++ b/retroshare-gui/src/gui/Identity/IdDialog.ui @@ -6,8 +6,8 @@ 0 0 - 1573 - 1177 + 987 + 584 @@ -120,7 +120,7 @@ Qt::NoFocus - + :/icons/help_64.png:/icons/help_64.png @@ -268,7 +268,7 @@ - 0 + 1 @@ -289,8 +289,8 @@ 0 0 - 1003 - 1094 + 1372 + 719 @@ -533,24 +533,14 @@ border-image: url(:/images/closepressed.png) Identity info - - - - true - - - true - - - - - + + - Your opinion: + Owner node ID : - + <html><head/><body><p>Overall reputation score, accounting for yours and your friends'.</p><p>Negative is bad, positive is good. Zero is neutral. If the score is too low,</p><p>the identity is flagged as bad, and will be filtered out in forums, chat lobbies,</p><p>channels, etc.</p></body></html> @@ -560,130 +550,7 @@ border-image: url(:/images/closepressed.png) - - - - Last used: - - - - - - - true - - - - - - - Identity name : - - - - - - - Qt::Horizontal - - - - - - - true - - - true - - - - - - - - 0 - 0 - - - - <!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:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Your own opinion about an identity rules the visibility of that identity for yourself and your friend nodes. Your own opinion is shared among friends and used to compute a reputation score: If your opinion about an identity is neutral, the reputation score is the difference between friend's positive and negative opinions. If not, your own opinion gives the score.</p> -<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The overall score is used in chat lobbies, forums and channels to decide on the actions to take for each specific identity. When the overall score is lower than -1, the identity is banned, which prevents all messages and forums/channels authored by this identity to be forwarded, both ways. Some forums also have special anti-spam flags that require a non negative reputation level, making them more sensitive to bad opinions. Banned identities gradually lose their activity and eventually disappear (after 5 days).</p> -<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">You can change the thresholds and the time of inactivity to delete identities in preferences -&gt; people. </p> -<p style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> - - - 0 - - - - 20 - 20 - - - - - Negative - - - - :/icons/png/thumbs-down.png:/icons/png/thumbs-down.png - - - - - Neutral - - - - :/icons/png/thumbs-neutral.png:/icons/png/thumbs-neutral.png - - - - - Positive - - - - :/icons/png/thumbs-up.png:/icons/png/thumbs-up.png - - - - - - - - true - - - - - - - Type: - - - - - - - Owner node name : - - - - - - - Owner node ID : - - - - + true @@ -693,50 +560,12 @@ p, li { white-space: pre-wrap; } - - - - Ban-option: + + + + 0 - - - - - - Friend votes: - - - - - - - <html><head/><body><p>Average opinion of neighbor nodes about this identity. Negative is bad,</p><p>positive is good. Zero is neutral.</p></body></html> - - - true - - - - - - - Auto-Ban all identities signed by the same node - - - - - - - true - - - true - - - - - - + @@ -773,18 +602,21 @@ p, li { white-space: pre-wrap; } - + Send Invite - + 6 + + 2 + @@ -862,7 +694,7 @@ p, li { white-space: pre-wrap; } - + Qt::Vertical @@ -877,7 +709,21 @@ p, li { white-space: pre-wrap; } - + + + + true + + + + + + + Type: + + + + @@ -890,27 +736,170 @@ p, li { white-space: pre-wrap; } - + + + + Last used: + + + + Identity ID : - + Created on : - + + + + Owner node name : + + + + + + + Ban-option: + + + + + + + Your opinion: + + + + + + + <html><head/><body><p>Average opinion of neighbor nodes about this identity. Negative is bad,</p><p>positive is good. Zero is neutral.</p></body></html> + + + true + + + + + + + true + + + + + + + <html><head/><body><p><span style=" font-family:'Sans'; font-size:9pt;">Your own opinion about an identity rules the visibility of that identity for yourself and your friend nodes. Your own opinion is shared among friends and used to compute a reputation score: If your opinion about an identity is neutral, the reputation score is the difference between friend's positive and negative opinions. If not, your own opinion gives the score.</span></p><p><span style=" font-family:'Sans'; font-size:9pt;">The overall score is used in chat lobbies, forums and channels to decide on the actions to take for each specific identity. When the overall score is lower than -1, the identity is banned, which prevents all messages and forums/channels authored by this identity to be forwarded, both ways. Some forums also have special anti-spam flags that require a non negative reputation level, making them more sensitive to bad opinions. Banned identities gradually lose their activity and eventually disappear (after 5 days).</span></p><p><span style=" font-family:'Sans'; font-size:9pt;">You can change the thresholds and the time of inactivity to delete identities in preferences -&gt; people. </span></p></body></html> + + + + 22 + 22 + + + + + Negative + + + + :/icons/png/thumbs-down.png:/icons/png/thumbs-down.png + + + + + Neutral + + + + :/icons/png/thumbs-neutral.png:/icons/png/thumbs-neutral.png + + + + + Positive + + + + :/icons/png/thumbs-up.png:/icons/png/thumbs-up.png + + + + + + + + Identity name : + + + + + + + Auto-Ban all identities signed by the same node + + + + + + + true + + + true + + + + true + + + + Friend votes: + + + + + + + true + + + true + + + + + + + Qt::Horizontal + + + + + + + true + + + true + + +
@@ -1063,7 +1052,7 @@ p, li { white-space: pre-wrap; } - + :/images/toaster/chat.png:/images/toaster/chat.png @@ -1105,8 +1094,8 @@ p, li { white-space: pre-wrap; } idTreeWidget - + diff --git a/retroshare-gui/src/gui/Identity/IdEditDialog.cpp b/retroshare-gui/src/gui/Identity/IdEditDialog.cpp index bde6b8f0d..b4c855c1d 100644 --- a/retroshare-gui/src/gui/Identity/IdEditDialog.cpp +++ b/retroshare-gui/src/gui/Identity/IdEditDialog.cpp @@ -26,11 +26,13 @@ #include "gui/common/UIStateHelper.h" #include "gui/common/AvatarDialog.h" #include "gui/gxs/GxsIdDetails.h" -#include "util/TokenQueue.h" +#include "util/qtthreadsutils.h" #include "util/misc.h" +#include "gui/notifyqt.h" #include #include +#include "gui/common/FilesDefs.h" #include @@ -46,7 +48,7 @@ IdEditDialog::IdEditDialog(QWidget *parent) : ui->setupUi(this); - ui->headerFrame->setHeaderImage(QPixmap(":/icons/png/person.png")); + ui->headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/person.png")); ui->headerFrame->setHeaderText(tr("Create New Identity")); /* Setup UI helper */ @@ -88,17 +90,13 @@ IdEditDialog::IdEditDialog(QWidget *parent) : /* Initialize ui */ ui->lineEdit_Nickname->setMaxLength(RSID_MAXIMUM_NICKNAME_SIZE); - mIdQueue = new TokenQueue(rsIdentity->getTokenService(), this); ui->pushButton_Tag->setEnabled(false); ui->pushButton_Tag->hide(); // unfinished ui->plainTextEdit_Tag->hide(); ui->label_TagCheck->hide(); } -IdEditDialog::~IdEditDialog() -{ - delete(mIdQueue); -} +IdEditDialog::~IdEditDialog() {} void IdEditDialog::changeAvatar() { @@ -199,25 +197,52 @@ void IdEditDialog::setAvatar(const QPixmap &avatar) } } -void IdEditDialog::setupExistingId(const RsGxsGroupId &keyId) +void IdEditDialog::setupExistingId(const RsGxsGroupId& keyId) { setWindowTitle(tr("Edit identity")); - ui->headerFrame->setHeaderImage(QPixmap(":/icons/png/person.png")); + ui->headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/person.png")); ui->headerFrame->setHeaderText(tr("Edit identity")); + mStateHelper->setLoading(IDEDITDIALOG_LOADID, true); + mIsNew = false; mGroupId.clear(); - mStateHelper->setLoading(IDEDITDIALOG_LOADID, true); + RsThread::async([this,keyId]() + { + std::vector datavector; + bool res = rsIdentity->getIdentitiesInfo( + std::set({(RsGxsId)keyId}), datavector ); - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; + RsQThreadUtils::postToObject( [this,keyId,res,datavector]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete, note that + * Qt::QueuedConnection is important! + */ - std::list groupIds; - groupIds.push_back(keyId); + mStateHelper->setLoading(IDEDITDIALOG_LOADID, false); - uint32_t token; - mIdQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, groupIds, IDEDITDIALOG_LOADID); + /* get details from libretroshare */ + + if (!res || datavector.size() != 1) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to collect group info for identity " << keyId << std::endl; + + ui->lineEdit_KeyId->setText(tr("Error KeyID invalid")); + ui->lineEdit_Nickname->setText(""); + + ui->lineEdit_GpgHash->setText(tr("N/A")); + ui->lineEdit_GpgId->setText(tr("N/A")); + ui->lineEdit_GpgName->setText(tr("N/A")); + return; + } + + loadExistingId(datavector[0]); + + }, this ); + }); } void IdEditDialog::enforceNoAnonIds() @@ -227,33 +252,9 @@ void IdEditDialog::enforceNoAnonIds() ui->radioButton_Pseudo->setEnabled(false); } -void IdEditDialog::loadExistingId(uint32_t token) +void IdEditDialog::loadExistingId(const RsGxsIdGroup& id_group) { - mStateHelper->setLoading(IDEDITDIALOG_LOADID, false); - - /* get details from libretroshare */ - std::vector datavector; - if (!rsIdentity->getGroupData(token, datavector)) - { - ui->lineEdit_KeyId->setText(tr("Error getting key!")); - return; - } - - if (datavector.size() != 1) - { - std::cerr << "IdDialog::insertIdDetails() Invalid datavector size"; - std::cerr << std::endl; - - ui->lineEdit_KeyId->setText(tr("Error KeyID invalid")); - ui->lineEdit_Nickname->setText(""); - - ui->lineEdit_GpgHash->setText(tr("N/A")); - ui->lineEdit_GpgId->setText(tr("N/A")); - ui->lineEdit_GpgName->setText(tr("N/A")); - return; - } - - mEditGroup = datavector[0]; + mEditGroup = id_group; mGroupId = mEditGroup.mMeta.mGroupId; QPixmap avatar; @@ -539,25 +540,41 @@ void IdEditDialog::createId() else params.mImage.clear(); - uint32_t token = 0; - rsIdentity->createIdentity(token, params); + RsGxsId keyId; + std::string gpg_password; - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + if(params.isPgpLinked) + { + std::string gpg_name = rsPeers->getGPGName(rsPeers->getGPGOwnId()); + bool cancelled; - mIdQueue->queueRequest(token, 0, 0, IDEDITDIALOG_CREATEID); -} + if(!NotifyQt::getInstance()->askForPassword(tr("Profile password needed.").toStdString(), + gpg_name + " (" + rsPeers->getOwnId().toStdString() + ")", + false, + gpg_password,cancelled)) + { + QMessageBox::critical(NULL,tr("Identity creation failed"),tr("Cannot create an identity linked to your profile without your profile password.")); + return; + } -void IdEditDialog::idCreated(uint32_t token) -{ - if (!rsIdentity->acknowledgeGrp(token, mGroupId)) { - std::cerr << "IdDialog::idCreated() acknowledgeGrp failed"; - std::cerr << std::endl; + } - reject(); - return; - } + if(rsIdentity->createIdentity(keyId,params.nickname,params.mImage,!params.isPgpLinked,gpg_password)) + { + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); - accept(); + RsIdentityDetails det; + + if(rsIdentity->getIdDetails(keyId,det)) + { + QMessageBox::information(NULL,tr("Identity creation success"),tr("Your new identity was successfuly created.")); + close(); + } + else + QMessageBox::critical(NULL,tr("Identity creation failed"),tr("Cannot create identity. Something went wrong.")); + } + else + QMessageBox::critical(NULL,tr("Identity creation failed"),tr("Cannot create identity. Something went wrong. Check your profile password.")); } void IdEditDialog::updateId() @@ -594,24 +611,8 @@ void IdEditDialog::updateId() mEditGroup.mImage.clear(); uint32_t dummyToken = 0; - rsIdentity->updateIdentity(dummyToken, mEditGroup); + rsIdentity->updateIdentity(mEditGroup); accept(); } -void IdEditDialog::loadRequest(const TokenQueue */*queue*/, const TokenRequest &req) -{ - std::cerr << "IdDialog::loadRequest() UserType: " << req.mUserType; - std::cerr << std::endl; - - switch (req.mUserType) { - case IDEDITDIALOG_LOADID: - loadExistingId(req.mToken); - break; - - case IDEDITDIALOG_CREATEID: - idCreated(req.mToken); - break; - } - -} diff --git a/retroshare-gui/src/gui/Identity/IdEditDialog.h b/retroshare-gui/src/gui/Identity/IdEditDialog.h index 2bed1091b..36eed2891 100644 --- a/retroshare-gui/src/gui/Identity/IdEditDialog.h +++ b/retroshare-gui/src/gui/Identity/IdEditDialog.h @@ -34,7 +34,7 @@ namespace Ui { class IdEditDialog; } -class IdEditDialog : public QDialog, public TokenResponse +class IdEditDialog : public QDialog { Q_OBJECT @@ -43,13 +43,11 @@ public: ~IdEditDialog(); void setupNewId(bool pseudo, bool enable_anon = true); - void setupExistingId(const RsGxsGroupId &keyId); + void setupExistingId(const RsGxsGroupId& keyId); void enforceNoAnonIds() ; RsGxsGroupId groupId() { return mGroupId; } - void loadRequest(const TokenQueue *queue, const TokenRequest &req); - private slots: void idTypeToggled(bool checked); void submit(); @@ -68,7 +66,7 @@ private: void createId(); void updateId(); void updateIdType(bool pseudo); - void loadExistingId(uint32_t token); + void loadExistingId(const RsGxsIdGroup& id_group); void setAvatar(const QPixmap &avatar); void idCreated(uint32_t token); @@ -83,8 +81,6 @@ protected: UIStateHelper *mStateHelper; RsGxsIdGroup mEditGroup; - - TokenQueue *mIdQueue; RsGxsGroupId mGroupId; QPixmap mAvatar; // Avatar from identity (not calculated) diff --git a/retroshare-gui/src/gui/Identity/IdEditDialog.ui b/retroshare-gui/src/gui/Identity/IdEditDialog.ui index 92baf07e1..22ed6ebd9 100644 --- a/retroshare-gui/src/gui/Identity/IdEditDialog.ui +++ b/retroshare-gui/src/gui/Identity/IdEditDialog.ui @@ -17,8 +17,8 @@ - - :/images/identity/identities_32.png:/images/identity/identities_32.png + + :/icons/png/person.png:/icons/png/person.png @@ -167,13 +167,13 @@ Linked to your profile - - :/images/contact22.png:/images/contact22.png + + :/icons/png/profile.png:/icons/png/profile.png - 22 - 22 + 24 + 24 @@ -190,13 +190,13 @@ Pseudonym - - :/images/tags/anon.png:/images/tags/anon.png + + :/icons/png/anonymous.png:/icons/png/anonymous.png - 22 - 22 + 24 + 24 @@ -528,6 +528,7 @@ + diff --git a/retroshare-gui/src/gui/LogoBar.cpp b/retroshare-gui/src/gui/LogoBar.cpp index 176152f1b..081eb2c31 100644 --- a/retroshare-gui/src/gui/LogoBar.cpp +++ b/retroshare-gui/src/gui/LogoBar.cpp @@ -20,8 +20,9 @@ #include "LogoBar.h" -#include -#include +#include "util/RetroStyleLabel.h" +#include "util/MouseEventFilter.h" +#include "gui/common/FilesDefs.h" #include @@ -42,11 +43,11 @@ void LogoBar::init() { //LogoButton _logoButton = new RetroStyleLabel(this); _logoButton->setPixmaps( - QPixmap(":/images/logobar/rslogo2.png"), //Start + FilesDefs::getPixmapFromQtResourcePath(":/images/logobar/rslogo2.png"), //Start QPixmap(), //End QPixmap(), //Fill - QPixmap(":/images/logobar/rslogo2.png"), //Start + FilesDefs::getPixmapFromQtResourcePath(":/images/logobar/rslogo2.png"), //Start QPixmap(), //End QPixmap() //Fill ); @@ -59,11 +60,11 @@ void LogoBar::init() { FillLabel1->setPixmaps( QPixmap(), //Start QPixmap(), //End - QPixmap(":/images/logobar/logo_bar_fill.png"), //Fill + FilesDefs::getPixmapFromQtResourcePath(":/images/logobar/logo_bar_fill.png"), //Fill QPixmap(), //Start QPixmap(), //End - QPixmap(":/images/logobar/logo_bar_fill.png") //Fill + FilesDefs::getPixmapFromQtResourcePath(":/images/logobar/logo_bar_fill.png") //Fill ); //FillLabel2 @@ -71,11 +72,11 @@ void LogoBar::init() { FillLabel2->setPixmaps( QPixmap(), //Start QPixmap(), //End - QPixmap(":/images/logobar/logo_bar_fill.png"), //Fill + FilesDefs::getPixmapFromQtResourcePath(":/images/logobar/logo_bar_fill.png"), //Fill QPixmap(), //Start QPixmap(), //End - QPixmap(":/images/logobar/logo_bar_fill.png") //Fill + FilesDefs::getPixmapFromQtResourcePath(":/images/logobar/logo_bar_fill.png") //Fill ); QGridLayout * layout = new QGridLayout(this); diff --git a/retroshare-gui/src/gui/MainWindow.cpp b/retroshare-gui/src/gui/MainWindow.cpp index cdb0fceb4..5608590ac 100644 --- a/retroshare-gui/src/gui/MainWindow.cpp +++ b/retroshare-gui/src/gui/MainWindow.cpp @@ -103,6 +103,12 @@ #ifdef RS_USE_WIKI #include "gui/WikiPoos/WikiDialog.h" #endif +#ifdef RS_USE_WIRE +#include "gui/TheWire/WireDialog.h" +#endif +#ifdef RS_USE_PHOTO +#include "gui/PhotoShare/PhotoShare.h" +#endif #include "gui/Posted/PostedDialog.h" #include "gui/statistics/StatisticsWindow.h" @@ -135,19 +141,15 @@ #define IMAGE_BWGRAPH ":/icons/png/bandwidth.png" #define IMAGE_MESSENGER ":/images/rsmessenger48.png" -#define IMAGE_BLOCK ":/images/blockdevice.png" #define IMAGE_COLOR ":/images/highlight.png" -#define IMAGE_GAMES ":/images/kgames.png" -#define IMAGE_PHOTO ":/images/lphoto.png" #define IMAGE_NEWRSCOLLECTION ":/images/library.png" #define IMAGE_ADDSHARE ":/images/directoryadd_24x24_shadow.png" #define IMAGE_OPTIONS ":/images/settings.png" #define IMAGE_UNFINISHED ":/images/underconstruction.png" -#define IMAGE_MINIMIZE ":/images/window_nofullscreen.png" -#define IMAGE_MAXIMIZE ":/images/window_fullscreen.png" +#define IMAGE_MINIMIZE ":/icons/fullscreen.png" +#define IMAGE_MAXIMIZE ":/icons/fullscreen-exit.png" #define IMAGE_PLUGINS ":/images/extension_32.png" -#define IMAGE_BLOGS ":/images/kblogger.png" /*static*/ bool MainWindow::hiddenmode = false; @@ -417,12 +419,22 @@ void MainWindow::initStackedPage() PeopleDialog *peopleDialog = NULL; addPage(peopleDialog = new PeopleDialog(ui->stackPages), grp, ¬ify); #endif - addPage(newsFeed = new NewsFeed(ui->stackPages), grp, ¬ify); #ifdef RS_USE_WIKI WikiDialog *wikiDialog = NULL; addPage(wikiDialog = new WikiDialog(ui->stackPages), grp, ¬ify); #endif +#ifdef RS_USE_WIRE + WireDialog *wireDialog = NULL; + addPage(wireDialog = new WireDialog(ui->stackPages), grp, ¬ify); +#endif + +#ifdef RS_USE_PHOTO + PhotoShare *photoDialog = NULL; + addPage(photoDialog = new PhotoShare(ui->stackPages), grp, ¬ify); +#endif + + std::cerr << "Looking for interfaces in existing plugins:" << std::endl; for(int i = 0;inbPlugins();++i) { @@ -466,12 +478,15 @@ void MainWindow::initStackedPage() } #endif + + addPage(newsFeed = new NewsFeed(ui->stackPages), grp, ¬ify); + //List All notify before Setting was created QList > >::iterator notifyIt; for (notifyIt = notify.begin(); notifyIt != notify.end(); ++notifyIt) { UserNotify *userNotify = notifyIt->first->getUserNotify(); if (userNotify) { - userNotify->initialize(ui->toolBarPage, notifyIt->second.first, notifyIt->second.second); + userNotify->initialize(ui->toolBarPage, notifyIt->second.first, notifyIt->second.second,userNotify->textInfo()); connect(userNotify, SIGNAL(countChanged()), this, SLOT(updateTrayCombine())); userNotifyList.push_back(userNotify); } diff --git a/retroshare-gui/src/gui/NetworkDialog.cpp b/retroshare-gui/src/gui/NetworkDialog.cpp index ff41dd658..5dc999740 100644 --- a/retroshare-gui/src/gui/NetworkDialog.cpp +++ b/retroshare-gui/src/gui/NetworkDialog.cpp @@ -47,14 +47,10 @@ #include /* Images for context menu icons */ -#define IMAGE_LOADCERT ":/images/loadcert16.png" #define IMAGE_PEERDETAILS ":/images/info16.png" -#define IMAGE_AUTH ":/images/encrypted16.png" -#define IMAGE_CLEAN_UNUSED ":/images/deletemail24.png" #define IMAGE_MAKEFRIEND ":/images/user/add_user16.png" -#define IMAGE_EXPORT ":/images/exportpeers_16x16.png" #define IMAGE_COPYLINK ":/images/copyrslink.png" -#define IMAGE_MESSAGE ":/images/mail_new.png" +#define IMAGE_MESSAGE ":/icons/mail/write-mail.png" /****** * #define NET_DEBUG 1 @@ -132,11 +128,11 @@ void NetworkDialog::connectTreeWidgetCostumPopupMenu( QPoint /*point*/ ) return ; if(peer_id == rsPeers->getGPGOwnId()) - contextMnu->addAction(QIcon(IMAGE_EXPORT), tr("Export/create a new node"), this, SLOT(on_actionExportKey_activated())); + contextMnu->addAction(QIcon(), tr("Export/create a new node"), this, SLOT(on_actionExportKey_activated())); contextMnu->addAction(QIcon(IMAGE_PEERDETAILS), tr("Profile details..."), this, SLOT(peerdetails())); contextMnu->addSeparator() ; - contextMnu->addAction(QIcon(IMAGE_CLEAN_UNUSED), tr("Remove unused keys..."), this, SLOT(removeUnusedKeys())); + contextMnu->addAction(QIcon(), tr("Remove unused keys..."), this, SLOT(removeUnusedKeys())); contextMnu->exec(QCursor::pos()); } diff --git a/retroshare-gui/src/gui/NetworkDialog.h b/retroshare-gui/src/gui/NetworkDialog.h index 8391ad476..9561c1db3 100644 --- a/retroshare-gui/src/gui/NetworkDialog.h +++ b/retroshare-gui/src/gui/NetworkDialog.h @@ -21,8 +21,10 @@ #ifndef _CONNECTIONSDIALOG_H #define _CONNECTIONSDIALOG_H +#include + #include "ui_NetworkDialog.h" -#include "RsAutoUpdatePage.h" + #include "gui/NetworkDialog/pgpid_item_model.h" #include "gui/NetworkDialog/pgpid_item_proxy.h" diff --git a/retroshare-gui/src/gui/NetworkView.h b/retroshare-gui/src/gui/NetworkView.h index 274ca020b..f6fcfe0b1 100644 --- a/retroshare-gui/src/gui/NetworkView.h +++ b/retroshare-gui/src/gui/NetworkView.h @@ -25,7 +25,7 @@ #include -#include "RsAutoUpdatePage.h" +#include #include "ui_NetworkView.h" diff --git a/retroshare-gui/src/gui/NewsFeed.cpp b/retroshare-gui/src/gui/NewsFeed.cpp index 2f70340be..2940dca03 100644 --- a/retroshare-gui/src/gui/NewsFeed.cpp +++ b/retroshare-gui/src/gui/NewsFeed.cpp @@ -67,12 +67,11 @@ * #define NEWS_DEBUG 1 ****/ -static NewsFeed *instance = NULL; +static NewsFeed* instance = nullptr; /** Constructor */ -NewsFeed::NewsFeed(QWidget *parent) : MainPage(parent), ui(new Ui::NewsFeed) -{ - mEventTypes = { +NewsFeed::NewsFeed(QWidget *parent) : MainPage(parent), ui(new Ui::NewsFeed), + mEventTypes({ RsEventType::AUTHSSL_CONNECTION_AUTENTICATION, RsEventType::PEER_CONNECTION , RsEventType::GXS_CIRCLES , @@ -80,12 +79,14 @@ NewsFeed::NewsFeed(QWidget *parent) : MainPage(parent), ui(new Ui::NewsFeed) RsEventType::GXS_FORUMS , RsEventType::GXS_POSTED , RsEventType::MAIL_STATUS - }; - - for(uint32_t i=0;iregisterEventsHandler(mEventTypes[i], [this](std::shared_ptr event) { handleEvent(event); }, mEventHandlerIds.back() ); + rsEvents->registerEventsHandler( + [this](std::shared_ptr event) { handleEvent(event); }, + mEventHandlerIds.back(), mEventTypes[i] ); } /* Invoke the Qt Designer generated object setup routine */ @@ -250,14 +251,20 @@ void NewsFeed::handleForumEvent(std::shared_ptr event) switch(pe->mForumEventCode) { + case RsForumEventCode::MODERATOR_LIST_CHANGED: + addFeedItem(new GxsForumGroupItem(this, NEWSFEED_UPDATED_FORUM, pe->mForumGroupId,pe->mModeratorsAdded,pe->mModeratorsRemoved, false, true)); + break; + case RsForumEventCode::UPDATED_FORUM: case RsForumEventCode::NEW_FORUM: - addFeedItem(new GxsForumGroupItem(this, NEWSFEED_FORUMNEWLIST, pe->mForumGroupId, false, true)); + addFeedItem(new GxsForumGroupItem(this, NEWSFEED_NEW_FORUM, pe->mForumGroupId, false, true)); break; + case RsForumEventCode::UPDATED_MESSAGE: case RsForumEventCode::NEW_MESSAGE: - addFeedItem(new GxsForumMsgItem(this, NEWSFEED_FORUMNEWLIST, pe->mForumGroupId, pe->mForumMsgId, false, true)); + addFeedItem(new GxsForumMsgItem(this, NEWSFEED_NEW_FORUM, pe->mForumGroupId, pe->mForumMsgId, false, true)); break; + default: break; } } @@ -287,44 +294,120 @@ void NewsFeed::handleChannelEvent(std::shared_ptr event) void NewsFeed::handleCircleEvent(std::shared_ptr event) { - const RsGxsCircleEvent *pe = dynamic_cast(event.get()); - if(!pe) - return; + // Gives the backend a few secs to load the cache data while not blocking the UI. This is not so nice, but there's no proper + // other way to do that. - RsGxsCircleDetails details; - - if(!rsGxsCircles->getCircleDetails(pe->mCircleId,details)) + RsThread::async( [event,this]() { - std::cerr << "(EE) Cannot get information about circle " << pe->mCircleId << ". Not in cache?" << std::endl; - return; - } + const RsGxsCircleEvent *pe = dynamic_cast(event.get()); + if(!pe) + return; - // Check if the circle is one of which we belong to. If so, then notify in the GUI about other members leaving/subscribing + if(pe->mCircleId.isNull()) // probably an item for cache update + return ; - if(details.mAmIAllowed || details.mAmIAdmin) + RsGxsCircleDetails details; + bool loaded = false; + + for(int i=0;i<5 && !loaded;++i) + if(rsGxsCircles->getCircleDetails(pe->mCircleId,details)) + { + std::cerr << "Cache item loaded for circle " << pe->mCircleId << std::endl; + loaded = true; + } + else + { + std::cerr << "Cache item for circle " << pe->mCircleId << " not loaded. Waiting " << i << "s" << std::endl; + rstime::rs_usleep(1000*1000); + } + + if(!loaded) + { + std::cerr << "(EE) Cannot get information about circle " << pe->mCircleId << ". Not in cache?" << std::endl; + return; + } + + if(!details.isGxsIdBased()) // not handled yet. + return; + + // Check if the circle is one of which we belong to or we are an admin of. + // If so, then notify in the GUI about other members leaving/subscribing, according + // to the following rules. The names correspond to the RS_FEED_CIRCLE_* variables: + // + // Message-based notifications: + // + // +---------------------------+----------------------------+ + // | Membership request | Membership cancellation | + // +-------------+-------------+-------------+--------------+ + // | Admin | Not admin | Admin | Not admin | + // +--------------------+-------------+-------------+----------------------------+ + // | in invitee list | MEMB_JOIN | MEMB_JOIN | MEMB_LEAVE | MEMB_LEAVE | + // +--------------------+-------------+-------------+-------------+--------------+ + // |not in invitee list | MEMB_REQ | X | X | X | + // +--------------------+-------------+-------------+-------------+--------------+ + // + // Note: in this case, the GxsId never belongs to you, since you dont need to handle + // notifications for actions you took yourself (leave/join a circle) + // + // GroupData-based notifications, the GxsId belongs to you: + // + // +---------------------------+----------------------------+ + // | GxsId joins invitee list | GxsId leaves invitee list | + // +-------------+-------------+-------------+--------------+ + // | Id is yours| Id is not | Id is yours | Id is not | + // +--------------------+-------------+-------------+-------------+--------------+ + // | Has Member request | MEMB_ACCEPT | (MEMB_JOIN) | MEMB_REVOKED| (MEMB_LEAVE) | + // +--------------------+-------------+-------------+-------------+--------------+ + // | No Member request | INVITE_REC | X | INVITE_REM | X | + // +--------------------+-------------+-------------+-------------+--------------+ + // + // Note: In this case you're never an admin of the circle, since these notification + // would be a direct consequence of your own actions. + + RsQThreadUtils::postToObject( [event,details,this]() { + const RsGxsCircleEvent *pe = static_cast(event.get()); + switch(pe->mCircleEventType) { case RsGxsCircleEventCode::CIRCLE_MEMBERSHIP_REQUEST: // only show membership requests if we're an admin of that circle - if(details.mAmIAdmin) + if(details.isIdInInviteeList(pe->mGxsId)) + addFeedItemIfUnique(new GxsCircleItem(this, NEWSFEED_CIRCLELIST, pe->mCircleId, pe->mGxsId, RS_FEED_ITEM_CIRCLE_MEMB_JOIN),true); + else if(details.mAmIAdmin) addFeedItemIfUnique(new GxsCircleItem(this, NEWSFEED_CIRCLELIST, pe->mCircleId, pe->mGxsId, RS_FEED_ITEM_CIRCLE_MEMB_REQ),true); + break; - case RsGxsCircleEventCode::CIRCLE_MEMBERSHIP_JOIN: - addFeedItemIfUnique(new GxsCircleItem(this, NEWSFEED_CIRCLELIST, pe->mCircleId, pe->mGxsId, RS_FEED_ITEM_CIRCLE_MEMB_JOIN),true); - break; + case RsGxsCircleEventCode::CIRCLE_MEMBERSHIP_LEAVE: - addFeedItemIfUnique(new GxsCircleItem(this, NEWSFEED_CIRCLELIST, pe->mCircleId, pe->mGxsId, RS_FEED_ITEM_CIRCLE_MEMB_LEAVE),true); + + if(details.isIdInInviteeList(pe->mGxsId)) + addFeedItemIfUnique(new GxsCircleItem(this, NEWSFEED_CIRCLELIST, pe->mCircleId, pe->mGxsId, RS_FEED_ITEM_CIRCLE_MEMB_LEAVE),true); break; - case RsGxsCircleEventCode::CIRCLE_MEMBERSHIP_INVITE: - addFeedItemIfUnique(new GxsCircleItem(this, NEWSFEED_CIRCLELIST, pe->mCircleId, pe->mGxsId, RS_FEED_ITEM_CIRCLE_INVIT_REC),true); + + case RsGxsCircleEventCode::CIRCLE_MEMBERSHIP_ID_ADDED_TO_INVITEE_LIST: + if(rsIdentity->isOwnId(pe->mGxsId)) + { + if(details.isIdRequestingMembership(pe->mGxsId)) + addFeedItemIfUnique(new GxsCircleItem(this, NEWSFEED_CIRCLELIST, pe->mCircleId, pe->mGxsId, RS_FEED_ITEM_CIRCLE_MEMB_ACCEPTED),true); + else + addFeedItemIfUnique(new GxsCircleItem(this, NEWSFEED_CIRCLELIST, pe->mCircleId, pe->mGxsId, RS_FEED_ITEM_CIRCLE_INVITE_REC),true); + } break; - case RsGxsCircleEventCode::CIRCLE_MEMBERSHIP_REVOQUED: - addFeedItemIfUnique(new GxsCircleItem(this, NEWSFEED_CIRCLELIST, pe->mCircleId, pe->mGxsId, RS_FEED_ITEM_CIRCLE_MEMB_REVOQUED),true); + + case RsGxsCircleEventCode::CIRCLE_MEMBERSHIP_ID_REMOVED_FROM_INVITEE_LIST: + if(rsIdentity->isOwnId(pe->mGxsId)) + { + if(details.isIdRequestingMembership(pe->mGxsId)) + addFeedItemIfUnique(new GxsCircleItem(this, NEWSFEED_CIRCLELIST, pe->mCircleId, pe->mGxsId, RS_FEED_ITEM_CIRCLE_MEMB_REVOKED),true); + else + addFeedItemIfUnique(new GxsCircleItem(this, NEWSFEED_CIRCLELIST, pe->mCircleId, pe->mGxsId, RS_FEED_ITEM_CIRCLE_INVITE_CANCELLED),true); + } break; + default: break; } - } + }, this ); }); // damn! } void NewsFeed::handleConnectionEvent(std::shared_ptr event) @@ -490,7 +573,7 @@ void NewsFeed::addFeedItemIfUnique(FeedItem *item, bool replace) } addFeedItem(item); - sendNewsFeedChanged(); + //sendNewsFeedChanged(); //Already done by addFeedItem() } void NewsFeed::remUniqueFeedItem(FeedItem *item) diff --git a/retroshare-gui/src/gui/NewsFeed.h b/retroshare-gui/src/gui/NewsFeed.h index 9f43c0df8..869d654fd 100644 --- a/retroshare-gui/src/gui/NewsFeed.h +++ b/retroshare-gui/src/gui/NewsFeed.h @@ -21,18 +21,20 @@ #ifndef _NEWS_FEED_DIALOG_H #define _NEWS_FEED_DIALOG_H -#include "mainpage.h" +#include #include "gui/feeds/FeedHolder.h" #include "util/TokenQueue.h" #include -#define IMAGE_NEWSFEED ":/icons/plugins_128.png" +#define IMAGE_NEWSFEED ":/icons/png/newsfeed.png" const uint32_t NEWSFEED_PEERLIST = 0x0001; -const uint32_t NEWSFEED_FORUMNEWLIST = 0x0002; -const uint32_t NEWSFEED_FORUMMSGLIST = 0x0003; +const uint32_t NEWSFEED_NEW_FORUM = 0x0002; +const uint32_t NEWSFEED_NEW_FORUM_MSG = 0x0003; +const uint32_t NEWSFEED_UPDATED_FORUM = 0x000f; + const uint32_t NEWSFEED_CHANNELNEWLIST = 0x0004; //const uint32_t NEWSFEED_CHANNELMSGLIST = 0x0005; #if 0 diff --git a/retroshare-gui/src/gui/People/IdentityItem.cpp b/retroshare-gui/src/gui/People/IdentityItem.cpp index 8ae3406c0..e1096d726 100644 --- a/retroshare-gui/src/gui/People/IdentityItem.cpp +++ b/retroshare-gui/src/gui/People/IdentityItem.cpp @@ -14,7 +14,7 @@ #include "IdentityItem.h" #define IMAGE_MAKEFRIEND ":/images/user/add_user16.png" -#define IMAGE_CHAT ":/images/chat_24.png" +#define IMAGE_CHAT ":/icons/png/chats.png" IdentityItem *IdentityItem::_selected_node = NULL ; diff --git a/retroshare-gui/src/gui/People/PeopleDialog.cpp b/retroshare-gui/src/gui/People/PeopleDialog.cpp index 626df8206..e06129905 100644 --- a/retroshare-gui/src/gui/People/PeopleDialog.cpp +++ b/retroshare-gui/src/gui/People/PeopleDialog.cpp @@ -25,10 +25,11 @@ #include "gui/msgs/MessageComposer.h" #include "gui/RetroShareLink.h" #include "gui/gxs/GxsIdDetails.h" -#include "gui/gxs/RsGxsUpdateBroadcastBase.h" +//#include "gui/gxs/RsGxsUpdateBroadcastBase.h" #include "gui/Identity/IdDetailsDialog.h" #include "gui/Identity/IdDialog.h" #include "gui/MainWindow.h" +#include "gui/common/FilesDefs.h" #include "retroshare/rspeers.h" #include "retroshare/rsidentity.h" @@ -53,7 +54,7 @@ const uint32_t PeopleDialog::PD_CIRCLES = 0x0004 ; /** Constructor */ PeopleDialog::PeopleDialog(QWidget *parent) - : RsGxsUpdateBroadcastPage(rsIdentity, parent) + : MainPage(parent) { setupUi(this); @@ -61,8 +62,8 @@ PeopleDialog::PeopleDialog(QWidget *parent) mIdentityQueue = new TokenQueue(rsIdentity->getTokenService(), this); mCirclesQueue = new TokenQueue(rsGxsCircles->getTokenService(), this); // This is used to grab the broadcast of changes from p3GxsCircles, which is discarded by the current dialog, since it expects data for p3Identity only. - mCirclesBroadcastBase = new RsGxsUpdateBroadcastBase(rsGxsCircles, this); - connect(mCirclesBroadcastBase, SIGNAL(fillDisplay(bool)), this, SLOT(updateCirclesDisplay(bool))); + //mCirclesBroadcastBase = new RsGxsUpdateBroadcastBase(rsGxsCircles, this); + //connect(mCirclesBroadcastBase, SIGNAL(fillDisplay(bool)), this, SLOT(updateCirclesDisplay(bool))); tabWidget->removeTab(1); @@ -271,25 +272,29 @@ void PeopleDialog::insertCircles(uint32_t token) std::list gSummaryList; std::list::iterator gsIt; - if (!rsGxsCircles->getGroupSummary(token,gSummaryList)) { + if (!rsGxsCircles->getGroupSummary(token,gSummaryList)) + { std::cerr << "PeopleDialog::insertExtCircles() Error getting GroupSummary"; std::cerr << std::endl; return; - }//if (!rsGxsCircles->getGroupSummary(token,gSummaryList)) + } for(gsIt = gSummaryList.begin(); gsIt != gSummaryList.end(); ++gsIt) { RsGroupMetaData gsItem = (*gsIt); RsGxsCircleDetails details ; - if(!rsGxsCircles->getCircleDetails(RsGxsCircleId(gsItem.mGroupId), details)){ + if(!rsGxsCircles->getCircleDetails(RsGxsCircleId(gsItem.mGroupId), details)) + { std::cerr << "(EE) Cannot get details for circle id " << gsItem.mGroupId << ". Circle item is not created!" << std::endl; continue ; - }//if(!rsGxsCircles->getCircleDetails(RsGxsCircleId(git->mGroupId), details)) + } - if (details.mCircleType != GXS_CIRCLE_TYPE_EXTERNAL){ + if (details.mCircleType != RsGxsCircleType::EXTERNAL) + { std::map::iterator itFound; - if((itFound=_int_circles_widgets.find(gsItem.mGroupId)) == _int_circles_widgets.end()) { + if((itFound=_int_circles_widgets.find(gsItem.mGroupId)) == _int_circles_widgets.end()) + { std::cerr << "PeopleDialog::insertExtCircles() add new Internal GroupId: " << gsItem.mGroupId; std::cerr << " GroupName: " << gsItem.mGroupName; std::cerr << std::endl; @@ -307,7 +312,9 @@ void PeopleDialog::insertCircles(uint32_t token) QPixmap pixmap = gitem->getImage(); pictureFlowWidgetInternal->addSlide( pixmap ); _intListCir << gitem; - } else {//if((itFound=_int_circles_widgets.find(gsItem.mGroupId)) == _int_circles_widgets.end()) + } + else + { std::cerr << "PeopleDialog::insertExtCircles() Update GroupId: " << gsItem.mGroupId; std::cerr << " GroupName: " << gsItem.mGroupName; std::cerr << std::endl; @@ -318,8 +325,10 @@ void PeopleDialog::insertCircles(uint32_t token) //int index = _intListCir.indexOf(cirWidget); //QPixmap pixmap = cirWidget->getImage(); //pictureFlowWidgetInternal->setSlide(index, pixmap); - }//if((item=_int_circles_widgets.find(gsItem.mGroupId)) == _int_circles_widgets.end()) - } else {//if (!details.mIsExternal) + } + } + else + { std::map::iterator itFound; if((itFound=_ext_circles_widgets.find(gsItem.mGroupId)) == _ext_circles_widgets.end()) { std::cerr << "PeopleDialog::insertExtCircles() add new GroupId: " << gsItem.mGroupId; @@ -339,7 +348,9 @@ void PeopleDialog::insertCircles(uint32_t token) QPixmap pixmap = gitem->getImage(); pictureFlowWidgetExternal->addSlide( pixmap ); _extListCir << gitem; - } else {//if((itFound=_circles_widgets.find(gsItem.mGroupId)) == _circles_widgets.end()) + } + else + { std::cerr << "PeopleDialog::insertExtCircles() Update GroupId: " << gsItem.mGroupId; std::cerr << " GroupName: " << gsItem.mGroupName; std::cerr << std::endl; @@ -350,9 +361,9 @@ void PeopleDialog::insertCircles(uint32_t token) //int index = _extListCir.indexOf(cirWidget); //QPixmap pixmap = cirWidget->getImage(); //pictureFlowWidgetExternal->setSlide(index, pixmap); - }//if((item=_circles_items.find(gsItem.mGroupId)) == _circles_items.end()) - }//else (!details.mIsExternal) - }//for(gsIt = gSummaryList.begin(); gsIt != gSummaryList.end(); ++gsIt) + } + } + } } void PeopleDialog::requestIdList() @@ -430,7 +441,7 @@ void PeopleDialog::iw_AddButtonClickedExt() { QMenu contextMnu( this ); - QMenu *mnu = contextMnu.addMenu(QIcon(":/icons/png/circles.png"),tr("Invite to Circle")) ; + QMenu *mnu = contextMnu.addMenu(FilesDefs::getIconFromQtResourcePath(":/icons/png/circles.png"),tr("Invite to Circle")) ; std::map::iterator itCurs; for( itCurs =_ext_circles_widgets.begin(); itCurs != _ext_circles_widgets.end(); ++itCurs) @@ -449,7 +460,7 @@ void PeopleDialog::iw_AddButtonClickedExt() if(own_identities.size() <= 1) { - QAction *action = contextMnu.addAction(QIcon(":/images/chat_24.png"), tr("Chat with this person"), this, SLOT(chatIdentity())); + QAction *action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(":/icons/png/chats.png"), tr("Chat with this person"), this, SLOT(chatIdentity())); if(own_identities.empty()) action->setEnabled(false) ; @@ -458,7 +469,7 @@ void PeopleDialog::iw_AddButtonClickedExt() } else { - QMenu *mnu = contextMnu.addMenu(QIcon(":/images/chat_24.png"),tr("Chat with this person as...")) ; + QMenu *mnu = contextMnu.addMenu(FilesDefs::getIconFromQtResourcePath(":/icons/png/chats.png"),tr("Chat with this person as...")) ; for(std::list::const_iterator it=own_identities.begin();it!=own_identities.end();++it) { @@ -475,20 +486,20 @@ void PeopleDialog::iw_AddButtonClickedExt() } } - QAction *actionsendmsg = contextMnu.addAction(QIcon(":/images/mail_new.png"), tr("Send message"), this, SLOT(sendMessage())); + QAction *actionsendmsg = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(":/icons/mail/write-mail.png"), tr("Send message"), this, SLOT(sendMessage())); actionsendmsg->setData( QString::fromStdString(dest->groupInfo().mMeta.mGroupId.toStdString())); - QAction *actionsendinvite = contextMnu.addAction(QIcon(":/images/mail_new.png"), tr("Send invite"), this, SLOT(sendInvite())); + QAction *actionsendinvite = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(":/icons/mail/write-mail.png"), tr("Send invite"), this, SLOT(sendInvite())); actionsendinvite->setData( QString::fromStdString(dest->groupInfo().mMeta.mGroupId.toStdString())); contextMnu.addSeparator(); - QAction *actionaddcontact = contextMnu.addAction(QIcon(":/images/mail_new.png"), tr("Add to Contacts"), this, SLOT(addtoContacts())); + QAction *actionaddcontact = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(""), tr("Add to Contacts"), this, SLOT(addtoContacts())); actionaddcontact->setData( QString::fromStdString(dest->groupInfo().mMeta.mGroupId.toStdString())); contextMnu.addSeparator(); - QAction *actionDetails = contextMnu.addAction(QIcon(":/images/info16.png"), tr("Person details"), this, SLOT(personDetails())); + QAction *actionDetails = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/info16.png"), tr("Person details"), this, SLOT(personDetails())); actionDetails->setData( QString::fromStdString(dest->groupInfo().mMeta.mGroupId.toStdString())); contextMnu.exec(QCursor::pos()); diff --git a/retroshare-gui/src/gui/People/PeopleDialog.h b/retroshare-gui/src/gui/People/PeopleDialog.h index bf8e7b1ea..808c61a26 100644 --- a/retroshare-gui/src/gui/People/PeopleDialog.h +++ b/retroshare-gui/src/gui/People/PeopleDialog.h @@ -33,7 +33,7 @@ #define IMAGE_IDENTITY ":/icons/png/people.png" -class PeopleDialog : public RsGxsUpdateBroadcastPage, public Ui::PeopleDialog, public TokenResponse +class PeopleDialog : public MainPage, public Ui::PeopleDialog, public TokenResponse { Q_OBJECT @@ -97,7 +97,7 @@ private: TokenQueue *mIdentityQueue; TokenQueue *mCirclesQueue; - RsGxsUpdateBroadcastBase *mCirclesBroadcastBase ; + //RsGxsUpdateBroadcastBase *mCirclesBroadcastBase ; FlowLayout *_flowLayoutExt; std::map _gxs_identity_widgets ; diff --git a/retroshare-gui/src/gui/PhotoShare/AddCommentDialog.ui b/retroshare-gui/src/gui/PhotoShare/AddCommentDialog.ui deleted file mode 100644 index d07b9f6dd..000000000 --- a/retroshare-gui/src/gui/PhotoShare/AddCommentDialog.ui +++ /dev/null @@ -1,67 +0,0 @@ - - - AddCommentDialog - - - - 0 - 0 - 370 - 118 - - - - Add Comment - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - buttonBox - accepted() - AddCommentDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - AddCommentDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/retroshare-gui/src/gui/PhotoShare/AlbumCreateDialog.cpp b/retroshare-gui/src/gui/PhotoShare/AlbumCreateDialog.cpp deleted file mode 100644 index 5d5461d79..000000000 --- a/retroshare-gui/src/gui/PhotoShare/AlbumCreateDialog.cpp +++ /dev/null @@ -1,210 +0,0 @@ -/******************************************************************************* - * retroshare-gui/src/gui/PhotoShare/AlbumCreateDialog.cpp * - * * - * Copyright (C) 2018 by Retroshare Team * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Affero General Public License as * - * published by the Free Software Foundation, either version 3 of the * - * License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Affero General Public License for more details. * - * * - * You should have received a copy of the GNU Affero General Public License * - * along with this program. If not, see . * - * * - *******************************************************************************/ - -#include - -#include "AlbumCreateDialog.h" -#include "ui_AlbumCreateDialog.h" - -#include "util/misc.h" -#include "retroshare/rsgxsflags.h" - -AlbumCreateDialog::AlbumCreateDialog(TokenQueue *photoQueue, RsPhoto *rs_photo, QWidget *parent): - QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint), - ui(new Ui::AlbumCreateDialog), mPhotoQueue(photoQueue), mRsPhoto(rs_photo), mPhotoSelected(NULL) -{ - ui->setupUi(this); - - ui->headerFrame->setHeaderImage(QPixmap(":/images/album_create_64.png")); - ui->headerFrame->setHeaderText(tr("Create Album")); - - -#if QT_VERSION >= 0x040700 - ui->lineEdit_Title_2->setPlaceholderText(tr("Untitle Album")); - ui->lineEdit_Caption_2->setPlaceholderText(tr("Say something about this album...")); - //ui->textEdit_Description->setPlaceholderText(tr("Say something about this album...")) ; - ui->lineEdit_Where->setPlaceholderText(tr("Where were these taken?")); -#endif - - ui->backButton->hide(); - - connect(ui->publishButton, SIGNAL(clicked()), this, SLOT(publishAlbum())); - connect(ui->AlbumThumbNail, SIGNAL(clicked()), this, SLOT(addAlbumThumbnail())); - - connect(ui->addphotosButton, SIGNAL(clicked()),this, SLOT(changePage())); - connect(ui->backButton, SIGNAL(clicked()),this, SLOT(backPage())); - - - mPhotoDrop = ui->scrollAreaWidgetContents; - mPhotoDrop->setPhotoItemHolder(this); - - -} - -AlbumCreateDialog::~AlbumCreateDialog() -{ - delete ui; -} - -#define PUBLIC_INDEX 0 -#define RESTRICTED_INDEX 1 -#define PRIVATE_INDEX 2 - -void AlbumCreateDialog::publishAlbum() -{ - // get fields for album to publish, publish and then exit dialog - RsPhotoAlbum album; - - album.mCaption = ui->lineEdit_Caption_2->text().toStdString(); - album.mPhotographer = ui->lineEdit_Photographer->text().toStdString(); - album.mMeta.mGroupName = ui->lineEdit_Title_2->text().toStdString(); - album.mDescription = ui->textEdit_Description->toPlainText().toStdString(); - album.mWhere = ui->lineEdit_Where->text().toStdString(); - album.mPhotographer = ui->lineEdit_Photographer->text().toStdString(); - getAlbumThumbnail(album.mThumbnail); - - - int currIndex = ui->privacyComboBox->currentIndex(); - - switch(currIndex) - { - case PUBLIC_INDEX: - album.mMeta.mGroupFlags |= GXS_SERV::FLAG_PRIVACY_PUBLIC; - break; - case RESTRICTED_INDEX: - album.mMeta.mGroupFlags |= GXS_SERV::FLAG_PRIVACY_RESTRICTED; - break; - case PRIVATE_INDEX: - album.mMeta.mGroupFlags |= GXS_SERV::FLAG_PRIVACY_PRIVATE; - break; - } - - uint32_t token; - mRsPhoto->submitAlbumDetails(token, album); - mPhotoQueue->queueRequest(token, TOKENREQ_GROUPINFO, RS_TOKREQ_ANSTYPE_ACK, 0); - - publishPhotos(); - - close(); -} - -void AlbumCreateDialog::publishPhotos() -{ - // get fields for album to publish, publish and then exit dialog - RsPhotoAlbum album; - - QSet photos; - - mPhotoDrop->getPhotos(photos); - - QSetIterator sit(photos); - - while(sit.hasNext()) - { - PhotoItem* item = sit.next(); - uint32_t token; - RsPhotoPhoto photo = item->getPhotoDetails(); - photo.mMeta.mGroupId = album.mMeta.mGroupId; - mRsPhoto->submitPhoto(token, photo); - mPhotoQueue->queueRequest(token, TOKENREQ_MSGINFO, RS_TOKREQ_ANSTYPE_ACK, 0); - } - -} - -bool AlbumCreateDialog::getAlbumThumbnail(RsPhotoThumbnail &nail) -{ - const QPixmap *tmppix = &mThumbNail; - - QByteArray ba; - QBuffer buffer(&ba); - - if(!tmppix->isNull()) - { - // send chan image - - buffer.open(QIODevice::WriteOnly); - tmppix->save(&buffer, "PNG"); // writes image into ba in PNG format - - RsPhotoThumbnail tmpnail; - tmpnail.data = (uint8_t *) ba.data(); - tmpnail.size = ba.size(); - - nail.copyFrom(tmpnail); - - return true; - } - - nail.data = NULL; - nail.size = 0; - return false; -} - -void AlbumCreateDialog::addAlbumThumbnail() -{ - QPixmap img = misc::getOpenThumbnailedPicture(this, tr("Load Album Thumbnail"), 128, 128); - - if (img.isNull()) - return; - - mThumbNail = img; - - // to show the selected - ui->AlbumThumbNail->setIcon(mThumbNail); -} - -void AlbumCreateDialog::changePage() -{ - int nextPage = ui->stackedWidget->currentIndex() + 1; - if (nextPage >= ui->stackedWidget->count()) - nextPage = 0; - ui->stackedWidget->setCurrentIndex(nextPage); - - ui->backButton->show(); - ui->addphotosButton->hide(); -} - -void AlbumCreateDialog::backPage() -{ - int nextPage = ui->stackedWidget->currentIndex() - 1; - if (nextPage >= ui->stackedWidget->count()) - nextPage = 0; - ui->stackedWidget->setCurrentIndex(nextPage); - - ui->backButton->hide(); - ui->addphotosButton->show(); -} - -void AlbumCreateDialog::notifySelection(PhotoShareItem *selection) -{ - - PhotoItem* pItem = dynamic_cast(selection); - - if(mPhotoSelected == NULL) - { - return; - } - else - { - mPhotoSelected->setSelected(false); - mPhotoSelected = pItem; - } - - mPhotoSelected->setSelected(true); -} diff --git a/retroshare-gui/src/gui/PhotoShare/AlbumCreateDialog.ui b/retroshare-gui/src/gui/PhotoShare/AlbumCreateDialog.ui deleted file mode 100644 index af5729fc9..000000000 --- a/retroshare-gui/src/gui/PhotoShare/AlbumCreateDialog.ui +++ /dev/null @@ -1,539 +0,0 @@ - - - AlbumCreateDialog - - - - 0 - 0 - 643 - 550 - - - - Create Album - - - true - - - - 0 - - - 0 - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - 0 - - - - - - - QFrame::NoFrame - - - - 0 - - - 6 - - - - - 3 - - - - - Album Name: - - - - - - - - - - - 64 - 64 - - - - - 64 - 64 - - - - -border: 2px solid white; -border-radius: 10px; - - - - - - - - :/images/album_64.png:/images/album_64.png - - - - 64 - 64 - - - - - - - - Category: - - - - - - - - Animals - - - - - Family - - - - - Friends - - - - - Flowers - - - - - Holiday - - - - - Landscapes - - - - - Pets - - - - - Portraits - - - - - Travel - - - - - Work - - - - - Random - - - - - - - - Caption: - - - - - - - - - - Where: - - - - - - - - - - Photographer: - - - - - - - - - - Description: - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Horizontal - - - - - - - - - Qt::Horizontal - - - - - - - - 1 - 0 - - - - Share Options - - - - - - - - Policy: - - - - - - - Quality: - - - - - - - Comments: - - - - - - - Identity: - - - - - - - - - - - - 0 - 0 - - - - - Public - - - - - Restricted - - - - - - - - - 0 - 0 - - - - - Resize Images (< 1Mb) - - - - - Resize Images (< 10Mb) - - - - - Send Original Images - - - - - - - - - 0 - 0 - - - - - No Comments Allowed - - - - - Authenticated Comments - - - - - Any Comments Allowed - - - - - - - - - 0 - 0 - - - - - Publish with Identity - - - - - - - - - - Qt::Horizontal - - - - 168 - 20 - - - - - - - - - - - - - - - - - - - - <!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-size:8pt; font-weight:600;"> Drag &amp; Drop to insert pictures. Click on a picture to edit details below.</span></p></body></html> - - - - - - - - 0 - 10 - - - - true - - - Qt::ScrollBarAsNeeded - - - true - - - Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft - - - - - 0 - 0 - 621 - 458 - - - - QWidget#scrollAreaWidgetContents{border: none;} - - - - - - - - - - - - - - - - 9 - - - - - Qt::Horizontal - - - - 419 - 18 - - - - - - - - QDialogButtonBox::Cancel - - - - - - - Back - - - - - - - Add Photos - - - - - - - Publish Album - - - - - - - - - - - HeaderFrame - QFrame -
gui/common/HeaderFrame.h
- 1 -
- - PhotoDrop - QWidget -
gui/PhotoShare/PhotoDrop.h
- 1 -
-
- - - - - - - buttonBox - rejected() - AlbumCreateDialog - close() - - - 353 - 529 - - - 321 - 274 - - - - -
diff --git a/retroshare-gui/src/gui/PhotoShare/AlbumDialog.cpp b/retroshare-gui/src/gui/PhotoShare/AlbumDialog.cpp index e943d21d7..f8d00f425 100644 --- a/retroshare-gui/src/gui/PhotoShare/AlbumDialog.cpp +++ b/retroshare-gui/src/gui/PhotoShare/AlbumDialog.cpp @@ -27,11 +27,11 @@ AlbumDialog::AlbumDialog(const RsPhotoAlbum& album, TokenQueue* photoQueue, RsPhoto* rs_Photo, QWidget *parent) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint), - ui(new Ui::AlbumDialog), mRsPhoto(rs_Photo), mPhotoQueue(photoQueue), mAlbum(album), mPhotoSelected(NULL) + ui(new Ui::AlbumDialog), mRsPhoto(rs_Photo), mPhotoShareQueue(photoQueue), mAlbum(album), mPhotoSelected(NULL) { ui->setupUi(this); - ui->headerFrame->setHeaderImage(QPixmap(":/images/kview_64.png")); + ui->headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/images/kview_64.png")); ui->headerFrame->setHeaderText(tr("Album")); connect(ui->pushButton_PublishPhotos, SIGNAL(clicked()), this, SLOT(updateAlbumPhotos())); @@ -39,13 +39,18 @@ AlbumDialog::AlbumDialog(const RsPhotoAlbum& album, TokenQueue* photoQueue, RsPh mPhotoDrop = ui->scrollAreaWidgetContents; - if(!(mAlbum.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN)) + if (!(mAlbum.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN)) { ui->scrollAreaPhotos->setEnabled(false); ui->pushButton_DeletePhoto->setEnabled(false); } mPhotoDrop->setPhotoItemHolder(this); + // setup PhotoQueue for internal loading. + mPhotoQueue = new TokenQueue(rsPhoto->getTokenService(), this); + requestPhotoList(mAlbum.mMeta.mGroupId); + + // setup gui. setUp(); } @@ -54,27 +59,27 @@ void AlbumDialog::setUp() { ui->lineEdit_Title->setText(QString::fromStdString(mAlbum.mMeta.mGroupName)); ui->lineEdit_Caption->setText(QString::fromStdString(mAlbum.mCaption)); - ui->lineEdit_Category->setText(QString::fromStdString(mAlbum.mCategory)); - ui->lineEdit_Identity->setText(QString::fromStdString(mAlbum.mMeta.mAuthorId.toStdString())); - ui->lineEdit_Where->setText(QString::fromStdString(mAlbum.mWhere)); - ui->textEdit_description->setText(QString::fromStdString(mAlbum.mDescription)); - - QPixmap qtn; - GxsIdDetails::loadPixmapFromData(mAlbum.mThumbnail.data, mAlbum.mThumbnail.size, mAlbum.mThumbnail.type.c_str(),qtn); - - if(mAlbum.mThumbnail.size != 0) + if (mAlbum.mThumbnail.mSize != 0) { - ui->label_thumbNail->setPixmap(qtn); + QPixmap qtn; + GxsIdDetails::loadPixmapFromData(mAlbum.mThumbnail.mData, mAlbum.mThumbnail.mSize,qtn, GxsIdDetails::ORIGINAL); + ui->label_thumbNail->setPixmap(qtn); } else { - // display a default Album icon when album has no Thumbnail - ui->label_thumbNail->setPixmap(QPixmap(":/images/album_default_128.png")); + // display a default Album icon when album has no Thumbnail + ui->label_thumbNail->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/album_default_128.png")); } } -void AlbumDialog::updateAlbumPhotos(){ +void AlbumDialog::addPhoto(const RsPhotoPhoto &photo) +{ + mPhotoDrop->addPhotoItem(new PhotoItem(this, photo)); +} + +void AlbumDialog::updateAlbumPhotos() +{ QSet photos; @@ -82,26 +87,59 @@ void AlbumDialog::updateAlbumPhotos(){ QSetIterator sit(photos); - while(sit.hasNext()) + while (sit.hasNext()) { PhotoItem* item = sit.next(); uint32_t token; RsPhotoPhoto photo = item->getPhotoDetails(); + + // ensure GroupId, AuthorId match Album. photo.mMeta.mGroupId = mAlbum.mMeta.mGroupId; - mRsPhoto->submitPhoto(token, photo); - mPhotoQueue->queueRequest(token, TOKENREQ_MSGINFO, RS_TOKREQ_ANSTYPE_ACK, 0); + photo.mMeta.mAuthorId = mAlbum.mMeta.mAuthorId; + + bool publish = true; + switch(item->getState()) + { + case PhotoItem::New: + { + // new photo will be published. + } + break; + case PhotoItem::Existing: + { + publish = false; + } + break; + case PhotoItem::Modified: + { + // update will be published. + } + break; + case PhotoItem::Deleted: + { + // flag image as Deleted. + // clear image. + // clear file URL (todo) + photo.mLowResImage.clear(); + } + break; + } + + if (publish) { + mRsPhoto->submitPhoto(token, photo); + mPhotoShareQueue->queueRequest(token, TOKENREQ_MSGINFO, RS_TOKREQ_ANSTYPE_ACK, 0); + } } close(); } void AlbumDialog::deletePhoto(){ - if(mPhotoSelected) + if (mPhotoSelected) { mPhotoSelected->setSelected(false); - mPhotoDrop->deletePhoto(mPhotoSelected); + mPhotoSelected->markForDeletion(); } - } void AlbumDialog::editPhoto() @@ -116,12 +154,11 @@ AlbumDialog::~AlbumDialog() void AlbumDialog::notifySelection(PhotoShareItem *selection) { - PhotoItem* pItem = dynamic_cast(selection); - if(mPhotoSelected == NULL) + if (mPhotoSelected == NULL) { - return; + mPhotoSelected = pItem; } else { @@ -131,3 +168,119 @@ void AlbumDialog::notifySelection(PhotoShareItem *selection) mPhotoSelected->setSelected(true); } + + +/**************************** Request / Response Filling of Data ************************/ + +void AlbumDialog::requestPhotoList(GxsMsgReq& req) +{ + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_MSG_IDS; + uint32_t token; + mPhotoQueue->requestMsgInfo(token, RS_TOKREQ_ANSTYPE_LIST, opts, req, 0); + return; +} + +void AlbumDialog::requestPhotoList(const RsGxsGroupId &albumId) +{ + std::list grpIds; + grpIds.push_back(albumId); + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_MSG_IDS; + opts.mOptions = RS_TOKREQOPT_MSG_LATEST; + uint32_t token; + mPhotoQueue->requestMsgInfo(token, RS_TOKREQ_ANSTYPE_LIST, opts, grpIds, 0); +} + +void AlbumDialog::acknowledgeMessage(const uint32_t &token) +{ + std::pair p; + rsPhoto->acknowledgeMsg(token, p); +} + +void AlbumDialog::loadPhotoList(const uint32_t &token) +{ + GxsMsgIdResult res; + + rsPhoto->getMsgList(token, res); + requestPhotoData(res); +} + +void AlbumDialog::requestPhotoData(GxsMsgReq &photoIds) +{ + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; + uint32_t token; + mPhotoQueue->requestMsgInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, photoIds, 0); +} + +void AlbumDialog::loadPhotoData(const uint32_t &token) +{ + PhotoResult res; + rsPhoto->getPhoto(token, res); + PhotoResult::iterator mit = res.begin(); + + for (; mit != res.end(); ++mit) + { + std::vector& photoV = mit->second; + std::vector::iterator vit = photoV.begin(); + + for (; vit != photoV.end(); ++vit) + { + RsPhotoPhoto& photo = *vit; + if (!photo.mLowResImage.empty()) { + addPhoto(photo); + } + } + } +} + + +/**************************** Request / Response Filling of Data ************************/ + +void AlbumDialog::loadRequest(const TokenQueue *queue, const TokenRequest &req) +{ + if (queue == mPhotoQueue) + { + /* now switch on req */ + switch(req.mType) + { + case TOKENREQ_MSGINFO: + switch(req.mAnsType) + { + case RS_TOKREQ_ANSTYPE_LIST: + loadPhotoList(req.mToken); + break; + case RS_TOKREQ_ANSTYPE_ACK: + acknowledgeMessage(req.mToken); + break; + case RS_TOKREQ_ANSTYPE_DATA: + loadPhotoData(req.mToken); + break; + default: + std::cerr << "PhotoShare::loadRequest() ERROR: MSG: INVALID ANS TYPE"; + std::cerr << std::endl; + break; + } + break; + case TOKENREQ_MSGRELATEDINFO: + switch(req.mAnsType) + { + case RS_TOKREQ_ANSTYPE_DATA: + loadPhotoData(req.mToken); + break; + default: + std::cerr << "PhotoShare::loadRequest() ERROR: MSG: INVALID ANS TYPE"; + std::cerr << std::endl; + break; + } + break; + default: + std::cerr << "PhotoShare::loadRequest() ERROR: INVALID TYPE"; + std::cerr << std::endl; + break; + } + } +} + +/**************************** Request / Response Filling of Data ************************/ diff --git a/retroshare-gui/src/gui/PhotoShare/AlbumDialog.h b/retroshare-gui/src/gui/PhotoShare/AlbumDialog.h index 9949b7408..43bf36852 100644 --- a/retroshare-gui/src/gui/PhotoShare/AlbumDialog.h +++ b/retroshare-gui/src/gui/PhotoShare/AlbumDialog.h @@ -32,7 +32,7 @@ namespace Ui { class AlbumDialog; } -class AlbumDialog : public QDialog, public PhotoShareItemHolder +class AlbumDialog : public QDialog, public PhotoShareItemHolder, public TokenResponse { Q_OBJECT @@ -52,8 +52,24 @@ private slots: void deletePhoto(); void editPhoto(); private: + + void addPhoto(const RsPhotoPhoto &photo); + + // data request / response. + void requestPhotoList(GxsMsgReq& req); + void requestPhotoList(const RsGxsGroupId &albumId); + void requestPhotoData(GxsMsgReq &photoIds); + + void acknowledgeMessage(const uint32_t &token); + + void loadPhotoList(const uint32_t &token); + void loadPhotoData(const uint32_t &token); + + void loadRequest(const TokenQueue *queue, const TokenRequest &req); + Ui::AlbumDialog *ui; RsPhoto* mRsPhoto; + TokenQueue* mPhotoShareQueue; // external PhotoShare Queue. TokenQueue* mPhotoQueue; RsPhotoAlbum mAlbum; PhotoDrop* mPhotoDrop; diff --git a/retroshare-gui/src/gui/PhotoShare/AlbumDialog.ui b/retroshare-gui/src/gui/PhotoShare/AlbumDialog.ui index be8dbce14..2d1e7de4e 100644 --- a/retroshare-gui/src/gui/PhotoShare/AlbumDialog.ui +++ b/retroshare-gui/src/gui/PhotoShare/AlbumDialog.ui @@ -6,8 +6,8 @@ 0 0 - 725 - 427 + 795 + 597 @@ -17,416 +17,257 @@ true - - 0 - - - 0 - - - + + - QFrame::StyledPanel + QFrame::NoFrame - QFrame::Raised + QFrame::Plain + + + + + + + + + + 64 + 64 + + + + TextLabel + + + + + + + Qt::Vertical + + + + 20 + 12 + + + + + + + + + + 6 + + + + + false + + + + 0 + 0 + + + + + + + + false + + + + 0 + 0 + + + + + + + + Category: + + + + + + + Caption + + + + + + + Album Title: + + + + + + + false + + + + 0 + 0 + + + + + + + + Qt::Vertical + + + + 568 + 6 + + + + + + + + + + + 0 + 1 + + + 0 + - - - Qt::Vertical - - - - - - - - 0 - 1 - - - - Album Thumbnail - - - - - - TextLabel - - - - - - - - - - - 10 - 1 - - - - Summary - - - - - - Album Title: - - - - - - - false - - - - 0 - 0 - - - - - - - - Category: - - - - - - - false - - - - 0 - 0 - - - - - - - - Caption - - - - - - - false - - - - 0 - 0 - - - - - - - - Where: - - - - - - - false - - - - 0 - 0 - - - - - - - - When - - - - - - - false - - - - 0 - 0 - - - - - - - - Description: - - - - - - - true - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - 10 - 1 - - - - Share Options - - - - - - false - - - - 0 - 0 - - - - - - - - Comments - - - - - - - Publish Identity - - - - - - - false - - - - 0 - 0 - - - - - - - - Visibility - - - - - - - false - - - - 0 - 0 - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - - - <!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-size:8pt; font-weight:600;"> Drag &amp; Drop to insert pictures. Click on a picture to edit details below.</span></p></body></html> - - - - - - - - 0 - 10 - - - - true - - - Qt::ScrollBarAsNeeded - - - true - - - Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft - - - - - 0 - 0 - 701 - 69 - - - - QWidget#scrollAreaWidgetContents{border: none;} - - - - - - - + - - - - - Add Photo - - - - - - - Edit Photo - - - - - - - Delete Photo - - - - - - - Qt::Horizontal - - - - 68 - 17 - - - - - - - - QDialogButtonBox::Cancel - - - - - - - Publish Photos - - - - + + + + 0 + 10 + + + + true + + + Qt::ScrollBarAsNeeded + + + true + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + 0 + 0 + 757 + 395 + + + + + 0 + 10 + + + + QWidget#scrollAreaWidgetContents{border: none;} + + + + - - - - QFrame::StyledPanel + + + + 6 - - QFrame::Raised + + 6 - + + 6 + + + 6 + + + + + Add Photo + + + + + + + Edit Photo + + + + + + + Delete Photo + + + + + + + Qt::Horizontal + + + + 68 + 17 + + + + + + + + QDialogButtonBox::Cancel + + + + + + + Publish Photos + + + +
diff --git a/retroshare-gui/src/gui/PhotoShare/AlbumExtra.cpp b/retroshare-gui/src/gui/PhotoShare/AlbumExtra.cpp new file mode 100644 index 000000000..010e7ded2 --- /dev/null +++ b/retroshare-gui/src/gui/PhotoShare/AlbumExtra.cpp @@ -0,0 +1,91 @@ +/******************************************************************************* + * retroshare-gui/src/gui/PhotoShare/AlbumExtra.cpp * + * * + * Copyright (C) 2018 by Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include "AlbumExtra.h" +#include "ui_AlbumExtra.h" + +AlbumExtra::AlbumExtra(QWidget *parent) : + QWidget(NULL), + ui(new Ui::AlbumExtra) +{ + ui->setupUi(this); + setUp(); +} + +AlbumExtra::~AlbumExtra() +{ + delete ui; +} + +void AlbumExtra::setUp() +{ + +} + +void AlbumExtra::setShareMode(uint32_t mode) +{ + ui->comboBox_shareMode->setCurrentIndex(mode); +} + +void AlbumExtra::setCaption(const std::string &str) +{ + ui->lineEdit_Caption->setText(QString::fromStdString(str)); +} + +void AlbumExtra::setPhotographer(const std::string &str) +{ + ui->lineEdit_Photographer->setText(QString::fromStdString(str)); +} + +void AlbumExtra::setWhere(const std::string &str) +{ + ui->lineEdit_Where->setText(QString::fromStdString(str)); +} + +void AlbumExtra::setWhen(const std::string &str) +{ + ui->lineEdit_When->setText(QString::fromStdString(str)); +} + +uint32_t AlbumExtra::getShareMode() +{ + return ui->comboBox_shareMode->currentIndex(); +} + +std::string AlbumExtra::getCaption() +{ + return ui->lineEdit_Caption->text().toStdString(); +} + +std::string AlbumExtra::getPhotographer() +{ + return ui->lineEdit_Photographer->text().toStdString(); +} + +std::string AlbumExtra::getWhere() +{ + return ui->lineEdit_Where->text().toStdString(); +} + +std::string AlbumExtra::getWhen() +{ + return ui->lineEdit_When->text().toStdString(); +} + diff --git a/retroshare-gui/src/gui/PhotoShare/PhotoCommentItem.cpp b/retroshare-gui/src/gui/PhotoShare/AlbumExtra.h similarity index 61% rename from retroshare-gui/src/gui/PhotoShare/PhotoCommentItem.cpp rename to retroshare-gui/src/gui/PhotoShare/AlbumExtra.h index 42f50c4f0..bea24f4b3 100644 --- a/retroshare-gui/src/gui/PhotoShare/PhotoCommentItem.cpp +++ b/retroshare-gui/src/gui/PhotoShare/AlbumExtra.h @@ -1,52 +1,56 @@ -/******************************************************************************* - * retroshare-gui/src/gui/PhotoShare/PhotoCommentItem.cpp * - * * - * Copyright (C) 2018 by Retroshare Team * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Affero General Public License as * - * published by the Free Software Foundation, either version 3 of the * - * License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Affero General Public License for more details. * - * * - * You should have received a copy of the GNU Affero General Public License * - * along with this program. If not, see . * - * * - *******************************************************************************/ - -#include - -#include "PhotoCommentItem.h" -#include "ui_PhotoCommentItem.h" - - -PhotoCommentItem::PhotoCommentItem(const RsPhotoComment& comment, QWidget *parent): - QWidget(parent), - ui(new Ui::PhotoCommentItem), mComment(comment) -{ - ui->setupUi(this); - setUp(); -} - -PhotoCommentItem::~PhotoCommentItem() -{ - delete ui; -} - -const RsPhotoComment& PhotoCommentItem::getComment() -{ - return mComment; -} - -void PhotoCommentItem::setUp() -{ - ui->labelComment->setText(QString::fromUtf8(mComment.mComment.c_str())); - QDateTime qtime; - qtime.setTime_t(mComment.mMeta.mPublishTs); - QString timestamp = qtime.toString("dd.MMMM yyyy hh:mm"); - ui->datetimelabel->setText(timestamp); -} +/******************************************************************************* + * retroshare-gui/src/gui/PhotoShare/AlbumExtra.h * + * * + * Copyright (C) 2020 by Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#ifndef ALBUMEXTRA_H +#define ALBUMEXTRA_H + +#include + +namespace Ui { + class AlbumExtra; +} + +class AlbumExtra : public QWidget +{ + Q_OBJECT + +public: + explicit AlbumExtra(QWidget *parent = 0); + virtual ~AlbumExtra(); + + void setShareMode(uint32_t mode); + void setCaption(const std::string &str); + void setPhotographer(const std::string &str); + void setWhere(const std::string &str); + void setWhen(const std::string &str); + + uint32_t getShareMode(); + std::string getCaption(); + std::string getPhotographer(); + std::string getWhere(); + std::string getWhen(); + +private: + void setUp(); +private: + Ui::AlbumExtra *ui; +}; + +#endif // ALBUMEXTRA_H diff --git a/retroshare-gui/src/gui/PhotoShare/AlbumExtra.ui b/retroshare-gui/src/gui/PhotoShare/AlbumExtra.ui new file mode 100644 index 000000000..23b0a462f --- /dev/null +++ b/retroshare-gui/src/gui/PhotoShare/AlbumExtra.ui @@ -0,0 +1,116 @@ + + + AlbumExtra + + + + 0 + 0 + 516 + 199 + + + + + 0 + 0 + + + + Form + + + + + + Quality: + + + + + + + + 0 + 0 + + + + + Embedded Only (<50Kb) + + + + + Share Original Images + + + + + Copy Original Images (extra disk space) + + + + + Resize Images (< 200Kb) + + + + + Resize Images (< 1Mb) + + + + + + + + Qt::Horizontal + + + + + + + Caption: + + + + + + + + + + Photographer: + + + + + + + + + + Where: + + + + + + + + + + When: + + + + + + + + + + + diff --git a/retroshare-gui/src/gui/PhotoShare/AlbumGroupDialog.cpp b/retroshare-gui/src/gui/PhotoShare/AlbumGroupDialog.cpp new file mode 100644 index 000000000..f58d658c6 --- /dev/null +++ b/retroshare-gui/src/gui/PhotoShare/AlbumGroupDialog.cpp @@ -0,0 +1,215 @@ +/******************************************************************************* + * retroshare-gui/src/gui/PhotoShare/AlbumGroupDialog.cpp * + * * + * Copyright (C) 2020 by Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ +#include + +#include "AlbumGroupDialog.h" +#include "AlbumExtra.h" +#include "gui/gxs/GxsIdDetails.h" + +#include + +const uint32_t AlbumCreateEnabledFlags = ( + GXS_GROUP_FLAGS_NAME | + GXS_GROUP_FLAGS_ICON | + GXS_GROUP_FLAGS_DESCRIPTION | + GXS_GROUP_FLAGS_DISTRIBUTION | + // GXS_GROUP_FLAGS_PUBLISHSIGN | + // GXS_GROUP_FLAGS_SHAREKEYS | // disabled because the UI doesn't handle it yet. + // GXS_GROUP_FLAGS_PERSONALSIGN | + // GXS_GROUP_FLAGS_COMMENTS | + GXS_GROUP_FLAGS_EXTRA | + 0); + +// Album Requirements: +// - All Photos require Publish signature (PUBLISH THREADS). +// - Comments are in the threads - so these need Author signatures. +// - Author signature required for all groups. +uint32_t AlbumCreateDefaultsFlags = ( GXS_GROUP_DEFAULTS_DISTRIB_PUBLIC | + //GXS_GROUP_DEFAULTS_DISTRIB_GROUP | + //GXS_GROUP_DEFAULTS_DISTRIB_LOCAL | + + //GXS_GROUP_DEFAULTS_PUBLISH_OPEN | + GXS_GROUP_DEFAULTS_PUBLISH_THREADS | + //GXS_GROUP_DEFAULTS_PUBLISH_REQUIRED | + //GXS_GROUP_DEFAULTS_PUBLISH_ENCRYPTED | + + //GXS_GROUP_DEFAULTS_PERSONAL_GPG | + //GXS_GROUP_DEFAULTS_PERSONAL_REQUIRED | + GXS_GROUP_DEFAULTS_PERSONAL_IFNOPUB | + GXS_GROUP_DEFAULTS_PERSONAL_GROUP | + + GXS_GROUP_DEFAULTS_COMMENTS_YES | + //GXS_GROUP_DEFAULTS_COMMENTS_NO | + 0); + +uint32_t AlbumEditEnabledFlags = AlbumCreateEnabledFlags; +uint32_t AlbumEditDefaultsFlags = AlbumCreateDefaultsFlags; + +AlbumGroupDialog::AlbumGroupDialog(QWidget *parent) + : GxsGroupDialog(AlbumCreateEnabledFlags, AlbumCreateDefaultsFlags, parent) +{ +} + +AlbumGroupDialog::AlbumGroupDialog(Mode mode, RsGxsGroupId groupId, QWidget *parent) + : GxsGroupDialog(mode, groupId, AlbumEditEnabledFlags, AlbumEditDefaultsFlags, parent) +{ +} + +void AlbumGroupDialog::initUi() +{ + switch (mode()) + { + case MODE_CREATE: + setUiText(UITYPE_SERVICE_HEADER, tr("Create New Album")); + setUiText(UITYPE_BUTTONBOX_OK, tr("Create")); + break; + case MODE_SHOW: + setUiText(UITYPE_SERVICE_HEADER, tr("Album")); + break; + case MODE_EDIT: + setUiText(UITYPE_SERVICE_HEADER, tr("Edit ALbum")); + setUiText(UITYPE_BUTTONBOX_OK, tr("Update Album")); + break; + } + + setUiText(UITYPE_ADD_ADMINS_CHECKBOX, tr("Add Album Admins")); + setUiText(UITYPE_CONTACTS_DOCK, tr("Select Album Admins")); + + // Inject Extra Widgets. + mAlbumExtra = new AlbumExtra(this); + injectExtraWidget(mAlbumExtra); +} + +QPixmap AlbumGroupDialog::serviceImage() +{ + return FilesDefs::getPixmapFromQtResourcePath(":/images/album_create_64.png"); +} + +void AlbumGroupDialog::prepareAlbumGroup(RsPhotoAlbum &group, const RsGroupMetaData &meta) +{ + group.mMeta = meta; + group.mDescription = getDescription().toUtf8().constData(); + + QPixmap pixmap = getLogo(); + + if (!pixmap.isNull()) { + QByteArray ba; + QBuffer buffer(&ba); + + buffer.open(QIODevice::WriteOnly); + pixmap.save(&buffer, "PNG"); // writes image into ba in PNG format + + group.mThumbnail.copy((uint8_t *) ba.data(), ba.size()); + } else { + group.mThumbnail.clear(); + } + + // Additional Fields. + group.mShareMode = mAlbumExtra->getShareMode(); + group.mCaption = mAlbumExtra->getCaption(); + group.mPhotographer = mAlbumExtra->getPhotographer(); + group.mWhere = mAlbumExtra->getWhere(); + group.mWhen = mAlbumExtra->getWhen(); +} + +bool AlbumGroupDialog::service_createGroup(RsGroupMetaData &meta) +{ + // Specific Function. + RsPhotoAlbum grp; + prepareAlbumGroup(grp, meta); + + bool success = rsPhoto->createAlbum(grp); + // TODO createAlbum should refresh groupId or GroupObj. + return success; +} + +bool AlbumGroupDialog::service_updateGroup(const RsGroupMetaData &editedMeta) +{ + RsPhotoAlbum grp; + prepareAlbumGroup(grp, editedMeta); + + std::cerr << "AlbumGroupDialog::service_updateGroup() submitting changes"; + std::cerr << std::endl; + + bool success = rsPhoto->updateAlbum(grp); + // TODO updateAlbum should refresh groupId or GroupObj. + return success; +} + +bool AlbumGroupDialog::service_loadGroup(const RsGxsGenericGroupData *data, Mode /*mode*/, QString &description) +{ + std::cerr << "AlbumGroupDialog::service_loadGroup()"; + std::cerr << std::endl; + + const RsPhotoAlbum *pgroup = dynamic_cast(data); + + if(pgroup == nullptr) + { + std::cerr << "AlbumGroupDialog::service_loadGroup() Error not a RsPhotoAlbum" << std::endl; + return false; + } + + const RsPhotoAlbum& group = *pgroup; + description = QString::fromUtf8(group.mDescription.c_str()); + + if (group.mThumbnail.mData) { + QPixmap pixmap; + if (GxsIdDetails::loadPixmapFromData(group.mThumbnail.mData, group.mThumbnail.mSize, pixmap,GxsIdDetails::ORIGINAL)) { + setLogo(pixmap); + } + } else { + setLogo(FilesDefs::getPixmapFromQtResourcePath(":/images/album_create_64.png")); + } + + // Load additional data.... + mAlbumExtra->setShareMode(group.mShareMode); + mAlbumExtra->setCaption(group.mCaption); + mAlbumExtra->setPhotographer(group.mPhotographer); + mAlbumExtra->setWhere(group.mWhere); + mAlbumExtra->setWhen(group.mWhen); + + return true; +} + +bool AlbumGroupDialog::service_getGroupData(const RsGxsGroupId& grpId,RsGxsGenericGroupData *& data) +{ + std::cerr << "AlbumGroupDialog::service_getGroupData(" << grpId << ")"; + std::cerr << std::endl; + + std::list groupIds({grpId}); + std::vector groups; + if (!rsPhoto->getAlbums(groupIds, groups)) + { + std::cerr << "AlbumGroupDialog::service_getGroupData() Error getting GroupData"; + std::cerr << std::endl; + return false; + } + + if (groups.size() != 1) + { + std::cerr << "AlbumGroupDialog::service_getGroupData() Error Group.size() != 1"; + std::cerr << std::endl; + return false; + } + + data = new RsPhotoAlbum(groups[0]); + return true; +} + diff --git a/retroshare-gui/src/gui/PhotoShare/AlbumGroupDialog.h b/retroshare-gui/src/gui/PhotoShare/AlbumGroupDialog.h new file mode 100644 index 000000000..32794650f --- /dev/null +++ b/retroshare-gui/src/gui/PhotoShare/AlbumGroupDialog.h @@ -0,0 +1,51 @@ +/******************************************************************************* + * retroshare-gui/src/gui/Posted/PostedGroupDialog.h * + * * + * Copyright (C) 2020 by Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + + +#ifndef _ALBUM_GROUP_DIALOG_H +#define _ALBUM_GROUP_DIALOG_H + +#include "gui/gxs/GxsGroupDialog.h" +#include + +#include "AlbumExtra.h" + +class AlbumGroupDialog : public GxsGroupDialog +{ + Q_OBJECT + +public: + AlbumGroupDialog(QWidget *parent); + AlbumGroupDialog(Mode mode, RsGxsGroupId groupId, QWidget *parent); + +protected: + virtual void initUi() override; + virtual QPixmap serviceImage() override; + virtual bool service_createGroup(RsGroupMetaData &meta) override; + virtual bool service_updateGroup(const RsGroupMetaData &editedMeta) override; + virtual bool service_loadGroup(const RsGxsGenericGroupData *data, Mode mode, QString &description) override; + virtual bool service_getGroupData(const RsGxsGroupId &grpId, RsGxsGenericGroupData *&data) override; + +private: + void prepareAlbumGroup(RsPhotoAlbum &group, const RsGroupMetaData &meta); + AlbumExtra *mAlbumExtra; +}; + +#endif diff --git a/retroshare-gui/src/gui/PhotoShare/AlbumItem.cpp b/retroshare-gui/src/gui/PhotoShare/AlbumItem.cpp index 4637a673a..e5dfac387 100644 --- a/retroshare-gui/src/gui/PhotoShare/AlbumItem.cpp +++ b/retroshare-gui/src/gui/PhotoShare/AlbumItem.cpp @@ -41,17 +41,17 @@ void AlbumItem::setUp() { ui->label_AlbumTitle->setText(QString::fromStdString(mAlbum.mMeta.mGroupName)); ui->label_Photographer->setText(QString::fromStdString(mAlbum.mPhotographer)); - QPixmap qtn; - qtn.loadFromData(mAlbum.mThumbnail.data, mAlbum.mThumbnail.size, mAlbum.mThumbnail.type.c_str()); - if(mAlbum.mThumbnail.size != 0) + if(mAlbum.mThumbnail.mSize != 0) { - ui->label_Thumbnail->setPixmap(qtn); + QPixmap qtn; + qtn.loadFromData(mAlbum.mThumbnail.mData, mAlbum.mThumbnail.mSize, "PNG"); + ui->label_Thumbnail->setPixmap(qtn); } else { - // display a default Album icon when album has no Thumbnail - ui->label_Thumbnail->setPixmap(QPixmap(":/images/album_default_128.png")); + // display a default Album icon when album has no Thumbnail + ui->label_Thumbnail->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/album_default_128.png")); } } diff --git a/retroshare-gui/src/gui/PhotoShare/AlbumItem.ui b/retroshare-gui/src/gui/PhotoShare/AlbumItem.ui index 88ef3770d..9e5d36378 100644 --- a/retroshare-gui/src/gui/PhotoShare/AlbumItem.ui +++ b/retroshare-gui/src/gui/PhotoShare/AlbumItem.ui @@ -6,8 +6,8 @@ 0 0 - 229 - 234 + 230 + 217 @@ -28,15 +28,51 @@ border-radius: 10px}
QFrame::Raised - - - - - TextLabel - - + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 18 + 20 + + + + + + + + TextLabel + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 18 + 20 + + + + + - + @@ -91,7 +127,7 @@ p, li { white-space: pre-wrap; } - + Qt::Vertical diff --git a/retroshare-gui/src/gui/PhotoShare/PhotoCommentItem.ui b/retroshare-gui/src/gui/PhotoShare/PhotoCommentItem.ui deleted file mode 100644 index ce00ee85d..000000000 --- a/retroshare-gui/src/gui/PhotoShare/PhotoCommentItem.ui +++ /dev/null @@ -1,200 +0,0 @@ - - - PhotoCommentItem - - - - 0 - 0 - 338 - 60 - - - - Form - - - - 0 - - - - - - 0 - 0 - - - - - 400 - 71 - - - - - - - - - 255 - 255 - 255 - - - - - - - 237 - 239 - 244 - - - - - - - - - 255 - 255 - 255 - - - - - - - 237 - 239 - 244 - - - - - - - - - 237 - 239 - 244 - - - - - - - 237 - 239 - 244 - - - - - - - - true - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 2 - - - 2 - - - - - - 32 - 32 - - - - - 32 - 32 - - - - - - - :/images/no_avatar_70.png - - - true - - - - - - - - 0 - 0 - - - - true - - - true - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - Qt::Vertical - - - - 29 - 13 - - - - - - - - - 75 - true - - - - <!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:600; 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-size:8pt; color:#666666;">DateTime</span></p></body></html> - - - - - - - - - - - - - diff --git a/retroshare-gui/src/gui/PhotoShare/PhotoDialog.cpp b/retroshare-gui/src/gui/PhotoShare/PhotoDialog.cpp index a3e0e8c69..16ac8c4e7 100644 --- a/retroshare-gui/src/gui/PhotoShare/PhotoDialog.cpp +++ b/retroshare-gui/src/gui/PhotoShare/PhotoDialog.cpp @@ -23,235 +23,177 @@ #include "PhotoDialog.h" #include "ui_PhotoDialog.h" #include "retroshare/rsidentity.h" -#include "AddCommentDialog.h" +#include "gui/gxs/GxsCommentDialog.h" + +#define IMAGE_FULLSCREEN ":/icons/fullscreen.png" +#define IMAGE_FULLSCREENEXIT ":/icons/fullscreen-exit.png" +#define IMAGE_SHOW ":/icons/png/down-arrow.png" +#define IMAGE_HIDE ":/icons/png/up-arrow.png" PhotoDialog::PhotoDialog(RsPhoto *rs_photo, const RsPhotoPhoto &photo, QWidget *parent) : - QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint), - ui(new Ui::PhotoDialog), mRsPhoto(rs_photo), mPhotoQueue(new TokenQueue(mRsPhoto->getTokenService(), this)), - mPhotoDetails(photo) + QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint), + ui(new Ui::PhotoDialog), mRsPhoto(rs_photo), mPhotoQueue(new TokenQueue(mRsPhoto->getTokenService(), this)), + mPhotoDetails(photo), + mCommentsCreated(false) { - ui->setupUi(this); - setAttribute ( Qt::WA_DeleteOnClose, true ); + ui->setupUi(this); + setAttribute ( Qt::WA_DeleteOnClose, true ); - connect(ui->pushButton_AddComment, SIGNAL(clicked()), this, SLOT(createComment())); - connect(ui->pushButton_AddCommentDlg, SIGNAL(clicked()), this, SLOT(addComment())); - connect(ui->fullscreenButton, SIGNAL(clicked()),this, SLOT(setFullScreen())); + connect(ui->fullscreenButton, SIGNAL(clicked()),this, SLOT(setFullScreen())); + connect(ui->commentsButton, SIGNAL(clicked()),this, SLOT(toggleComments())); + connect(ui->detailsButton, SIGNAL(clicked()),this, SLOT(toggleDetails())); -#if QT_VERSION >= 0x040700 - ui->lineEdit->setPlaceholderText(tr("Write a comment...")) ; -#endif - - setUp(); + setUp(); } PhotoDialog::~PhotoDialog() { - delete ui; - delete mPhotoQueue; + delete ui; + delete mPhotoQueue; } void PhotoDialog::setUp() { - QPixmap qtn; - qtn.loadFromData(mPhotoDetails.mThumbnail.data, mPhotoDetails.mThumbnail.size, mPhotoDetails.mThumbnail.type.c_str()); - ui->label_Photo->setPixmap(qtn); - ui->lineEdit_Title->setText(QString::fromStdString(mPhotoDetails.mMeta.mMsgName)); + QPixmap qtn; + qtn.loadFromData(mPhotoDetails.mLowResImage.mData, mPhotoDetails.mLowResImage.mSize); + ui->label_Photo->setPixmap(qtn); + ui->label_Photo->setVisible(true); - requestComments(); + // set size of label to match image. + ui->label_Photo->setMinimumSize(ui->label_Photo->sizeHint()); + // alternative is to scale contents. + // ui->label_Photo->setScaledContents(true); + // Neither are ideal. sizeHint is potentially too large. + // scaled contents - doesn't respect Aspect Ratio... + // + // Ideal soln: + // Allow both, depending on Zoom Factor. + // Auto: use Scale, with correct aspect ratio. + // answer here: https://stackoverflow.com/questions/8211982/qt-resizing-a-qlabel-containing-a-qpixmap-while-keeping-its-aspect-ratio + // Fixed %, then manually scale to that, with scroll area. + + ui->lineEdit_Title->setText(QString::fromStdString(mPhotoDetails.mMeta.mMsgName)); + ui->frame_comments->setVisible(false); + ui->frame_details->setVisible(false); } -void PhotoDialog::addComment() +void PhotoDialog::toggleDetails() { - AddCommentDialog dlg(this); - if (dlg.exec() == QDialog::Accepted) { - RsPhotoComment comment; - comment.mComment = dlg.getComment().toUtf8().constData(); - - uint32_t token; - comment.mMeta.mGroupId = mPhotoDetails.mMeta.mGroupId; - comment.mMeta.mParentId = mPhotoDetails.mMeta.mOrigMsgId; - mRsPhoto->submitComment(token, comment); - mPhotoQueue->queueRequest(token, TOKENREQ_MSGINFO, RS_TOKREQ_ANSTYPE_ACK, 0); - } + if (ui->frame_details->isVisible()) { + ui->frame_details->setVisible(false); + ui->detailsButton->setIcon(QIcon(IMAGE_SHOW)); + } else { + ui->frame_details->setVisible(true); + ui->detailsButton->setIcon(QIcon(IMAGE_HIDE)); + } } -void PhotoDialog::clearComments() +void PhotoDialog::toggleComments() { - //QLayout* l = ui->scrollAreaWidgetContents->layout(); - QSetIterator sit(mComments); - while(sit.hasNext()) - { - PhotoCommentItem* item = sit.next(); - ui->verticalLayout->removeWidget(item); - item->setParent(NULL); - delete item; - } + if (ui->frame_comments->isVisible()) { + ui->frame_comments->setVisible(false); + } else { + if (mCommentsCreated) { + ui->frame_comments->setVisible(true); + } else { + // create CommentDialog. + RsGxsCommentService *commentService = dynamic_cast(mRsPhoto); + GxsCommentDialog *commentDialog = new GxsCommentDialog(this, mRsPhoto->getTokenService(), commentService); - mComments.clear(); + // TODO: Need to fetch all msg versions, otherwise - won't get all the comments. + // For the moment - use current msgid. + // Needs to be passed to PhotoDialog, or fetched here. + + RsGxsGroupId grpId = mPhotoDetails.mMeta.mGroupId; + RsGxsMessageId msgId = mPhotoDetails.mMeta.mMsgId; + + std::set msgv; + msgv.insert(msgId); + msgv.insert(mPhotoDetails.mMeta.mOrigMsgId); // if duplicate will be ignored. + + commentDialog->commentLoad(grpId, msgv,msgId); + + // insert into frame. + QVBoxLayout *vbox = new QVBoxLayout(); + + vbox->addWidget(commentDialog); + ui->frame_comments->setLayout(vbox); + + ui->frame_comments->setVisible(true); + mCommentsCreated = true; + } + } } -void PhotoDialog::resetComments() -{ - QSetIterator sit(mComments); - //QLayout* l = ui->scrollAreaWidgetContents->layout(); - while(sit.hasNext()) - { - PhotoCommentItem* item = sit.next(); - ui->verticalLayout->insertWidget(0,item); - } -} - -void PhotoDialog::requestComments() -{ - RsTokReqOptions opts; - opts.mMsgFlagMask = RsPhoto::FLAG_MSG_TYPE_MASK; - opts.mMsgFlagFilter = RsPhoto::FLAG_MSG_TYPE_PHOTO_COMMENT; - - opts.mReqType = GXS_REQUEST_TYPE_MSG_IDS; - opts.mReqType = GXS_REQUEST_TYPE_MSG_RELATED_DATA; - opts.mOptions = RS_TOKREQOPT_MSG_PARENT | RS_TOKREQOPT_MSG_LATEST; - RsGxsGrpMsgIdPair msgId; - uint32_t token; - msgId.first = mPhotoDetails.mMeta.mGroupId; - msgId.second = mPhotoDetails.mMeta.mMsgId; - std::vector msgIdV; - msgIdV.push_back(msgId); - mPhotoQueue->requestMsgRelatedInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, msgIdV, 0); -} - -void PhotoDialog::createComment() -{ - RsPhotoComment comment; - QString commentString = ui->lineEdit->text(); - - comment.mComment = commentString.toUtf8().constData(); - - uint32_t token; - comment.mMeta.mGroupId = mPhotoDetails.mMeta.mGroupId; - comment.mMeta.mParentId = mPhotoDetails.mMeta.mOrigMsgId; - mRsPhoto->submitComment(token, comment); - mPhotoQueue->queueRequest(token, TOKENREQ_MSGINFO, RS_TOKREQ_ANSTYPE_ACK, 0); - - ui->lineEdit->clear(); -} - - /*************** message loading **********************/ void PhotoDialog::loadRequest(const TokenQueue *queue, const TokenRequest &req) { - std::cerr << "PhotoShare::loadRequest()"; - std::cerr << std::endl; + std::cerr << "PhotoShare::loadRequest()"; + std::cerr << std::endl; - if (queue == mPhotoQueue) - { - /* now switch on req */ - switch(req.mType) - { - case TOKENREQ_MSGINFO: - { - switch(req.mAnsType) - { - case RS_TOKREQ_ANSTYPE_DATA: - loadComment(req.mToken); - break; - case RS_TOKREQ_ANSTYPE_LIST: - loadList(req.mToken); - break; - case RS_TOKREQ_ANSTYPE_ACK: - acknowledgeComment(req.mToken); - break; - default: - std::cerr << "PhotoShare::loadRequest() ERROR: MSG INVALID TYPE"; - std::cerr << std::endl; - break; - } - break; - } + if (queue == mPhotoQueue) + { + /* now switch on req */ + switch(req.mType) + { + case TOKENREQ_MSGINFO: + { + switch(req.mAnsType) + { + case RS_TOKREQ_ANSTYPE_LIST: + loadList(req.mToken); + break; + default: + std::cerr << "PhotoShare::loadRequest() ERROR: MSG INVALID TYPE"; + std::cerr << std::endl; + break; + } + break; + } - default: - { - std::cerr << "PhotoShare::loadRequest() ERROR: INVALID TYPE"; - std::cerr << std::endl; - break; - } - } - } + default: + { + std::cerr << "PhotoShare::loadRequest() ERROR: INVALID TYPE"; + std::cerr << std::endl; + break; + } + } + } } -void PhotoDialog::loadComment(uint32_t token) -{ - - clearComments(); - - PhotoRelatedCommentResult results; - mRsPhoto->getPhotoRelatedComment(token, results); - - PhotoRelatedCommentResult::iterator mit = results.begin(); - - for(; mit != results.end(); ++mit) - { - const std::vector& commentV = mit->second; - std::vector::const_iterator vit = commentV.begin(); - - for(; vit != commentV.end(); ++vit) - { - addComment(*vit); - } - } - - resetComments(); -} - void PhotoDialog::loadList(uint32_t token) { - GxsMsgReq msgIds; - mRsPhoto->getMsgList(token, msgIds); - RsTokReqOptions opts; + GxsMsgReq msgIds; + mRsPhoto->getMsgList(token, msgIds); + RsTokReqOptions opts; - // just use data as no need to worry about getting comments - opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; - uint32_t reqToken; - mPhotoQueue->requestMsgInfo(reqToken, RS_TOKREQ_ANSTYPE_DATA, opts, msgIds, 0); -} - -void PhotoDialog::addComment(const RsPhotoComment &comment) -{ - PhotoCommentItem* item = new PhotoCommentItem(comment); - mComments.insert(item); -} - -void PhotoDialog::acknowledgeComment(uint32_t token) -{ - RsGxsGrpMsgIdPair msgId; - mRsPhoto->acknowledgeMsg(token, msgId); - - if(msgId.first.isNull() || msgId.second.isNull()){ - - }else - { - requestComments(); - } + // just use data as no need to worry about getting comments + opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; + uint32_t reqToken; + mPhotoQueue->requestMsgInfo(reqToken, RS_TOKREQ_ANSTYPE_DATA, opts, msgIds, 0); } void PhotoDialog::setFullScreen() { if (!isFullScreen()) { - // hide menu & toolbars + // hide menu & toolbars #ifdef Q_OS_LINUX - show(); - raise(); - setWindowState( windowState() | Qt::WindowFullScreen ); + show(); + raise(); + setWindowState( windowState() | Qt::WindowFullScreen ); #else - setWindowState( windowState() | Qt::WindowFullScreen ); - show(); - raise(); + setWindowState( windowState() | Qt::WindowFullScreen ); + show(); + raise(); #endif + ui->fullscreenButton->setIcon(QIcon(IMAGE_FULLSCREENEXIT)); } else { - setWindowState( windowState() ^ Qt::WindowFullScreen ); - show(); + setWindowState( windowState() ^ Qt::WindowFullScreen ); + show(); + ui->fullscreenButton->setIcon(QIcon(IMAGE_FULLSCREEN)); } } diff --git a/retroshare-gui/src/gui/PhotoShare/PhotoDialog.h b/retroshare-gui/src/gui/PhotoShare/PhotoDialog.h index abbd0bd7d..4f2c325eb 100644 --- a/retroshare-gui/src/gui/PhotoShare/PhotoDialog.h +++ b/retroshare-gui/src/gui/PhotoShare/PhotoDialog.h @@ -25,58 +25,40 @@ #include #include "retroshare/rsphoto.h" #include "util/TokenQueue.h" -#include "PhotoCommentItem.h" namespace Ui { - class PhotoDialog; + class PhotoDialog; } class PhotoDialog : public QDialog, public TokenResponse { - Q_OBJECT + Q_OBJECT public: - explicit PhotoDialog(RsPhoto* rs_photo, const RsPhotoPhoto& photo, QWidget *parent = 0); - ~PhotoDialog(); + explicit PhotoDialog(RsPhoto* rs_photo, const RsPhotoPhoto& photo, QWidget *parent = 0); + ~PhotoDialog(); private slots: - void addComment(); - void createComment(); void setFullScreen(); + void toggleDetails(); + void toggleComments(); public: - void loadRequest(const TokenQueue *queue, const TokenRequest &req); + void loadRequest(const TokenQueue *queue, const TokenRequest &req); private: - void setUp(); + void setUp(); - /*! - * clears comments - * and places them back in dialog - */ - void resetComments(); + void loadList(uint32_t token); - /*! - * Request comments - */ - void requestComments(); - - /*! - * Simply removes comments but doesn't place them back in dialog - */ - void clearComments(); - - void acknowledgeComment(uint32_t token); - void loadComment(uint32_t token); - void loadList(uint32_t token); - void addComment(const RsPhotoComment& comment); private: - Ui::PhotoDialog *ui; + Ui::PhotoDialog *ui; - RsPhoto* mRsPhoto; - TokenQueue* mPhotoQueue; - RsPhotoPhoto mPhotoDetails; - QSet mComments; + RsPhoto* mRsPhoto; + TokenQueue* mPhotoQueue; + RsPhotoPhoto mPhotoDetails; + + bool mCommentsCreated; }; #endif // PHOTODIALOG_H diff --git a/retroshare-gui/src/gui/PhotoShare/PhotoDialog.ui b/retroshare-gui/src/gui/PhotoShare/PhotoDialog.ui index a6689f772..8a0adf7c9 100644 --- a/retroshare-gui/src/gui/PhotoShare/PhotoDialog.ui +++ b/retroshare-gui/src/gui/PhotoShare/PhotoDialog.ui @@ -6,8 +6,8 @@ 0 0 - 594 - 572 + 790 + 509 @@ -16,319 +16,283 @@ true - - - - - - 0 - 1 - + + + + + Qt::Horizontal - - Photo - - - - - - - 0 - 0 - - - - - 200 - 300 - - - - TextLabel - - - - - - - - - - - 0 - 0 - - - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - - true - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - 0 - 0 - - - - - 300 - 0 - - - - true - - - - - 0 - 0 - 328 - 20 - - + + + + - + + 0 + 1 + + + + Album / Photo Name + + + + + + + 100 + 100 + + + + true + + + + + 0 + 0 + 540 + 318 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 200 + 300 + + + + TextLabel + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + + ... + + + + :/icons/fullscreen.png:/icons/fullscreen.png + + + true + + + + + + + Details + + + + :/icons/png/down-arrow.png:/icons/png/down-arrow.png + + + + + + + + 50 % + + + + + 75 % + + + + + 100 % + + + + + 200 % + + + + + + + + <N> Comments >> + + + + :/icons/png/comment.png:/icons/png/comment.png + + + + + + + + + + 0 0 - - - - - 1 + + + 50 + 0 + + + + + + + false - + + + 0 + 0 + + + + + + + + Caption + + + + + + + false + + + + 0 + 0 + + + + + + + + Where: + + + + + + + false + + + + 0 + 0 + + + + + + + + false + + + + 0 + 0 + + + + + + + + Photo Title: + + + + + + + When + + - - - - - - - - - Comment - - - - - - - - - - - 0 - 0 - - - - - 50 - 0 - - - - Summary - - - - - - false - - - - 0 - 0 - - - - - - - - Caption - - - - - - - false - - - - 0 - 0 - - - - - - - - Where: - - - - - - - false - - - - 0 - 0 - - - - - - - - false - - - - 0 - 0 - - - - - - - - Photo Title: - - - - - - - When - - - - - - - - - - ... - - - - :/images/window_fullscreen.png:/images/window_fullscreen.png - - - true - - - - - - - Qt::Horizontal - - - - 238 - 20 - - - - - - - - Add Comment - + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + diff --git a/retroshare-gui/src/gui/PhotoShare/PhotoDrop.cpp b/retroshare-gui/src/gui/PhotoShare/PhotoDrop.cpp index f092d2e02..d8fc76e0f 100644 --- a/retroshare-gui/src/gui/PhotoShare/PhotoDrop.cpp +++ b/retroshare-gui/src/gui/PhotoShare/PhotoDrop.cpp @@ -48,16 +48,15 @@ class gridIndex }; - - +#define DEFAULT_ORDER_INCREMENT (100) PhotoDrop::PhotoDrop(QWidget *parent) - : QWidget(parent) + :QWidget(parent), mLastOrder(0) { setAcceptDrops(true); mSelected = NULL; - checkMoveButtons(); + checkMoveButtons(); reorderPhotos(); } @@ -80,7 +79,7 @@ void PhotoDrop::resizeEvent ( QResizeEvent * event ) reorderPhotos(); } -int PhotoDrop::getPhotoCount() +int PhotoDrop::getPhotoCount() { std::cerr << "PhotoDrop::getPhotoCount()"; std::cerr << std::endl; @@ -147,7 +146,7 @@ PhotoItem *PhotoDrop::getPhotoIdx(int idx) void PhotoDrop::getPhotos(QSet &photos) { - photos = mPhotos; + photos = mPhotos; } @@ -453,7 +452,7 @@ void PhotoDrop::checkMoveButtons() int count = alayout->count(); if ((!mSelected) || (count < 2)) { - buttonStatus(PHOTO_SHIFT_NO_BUTTONS); + buttonStatus(PHOTO_SHIFT_NO_BUTTONS); return; } @@ -462,7 +461,7 @@ void PhotoDrop::checkMoveButtons() { std::cerr << "PhotoDrop::checkMoveButtons() not GridLayout... not much we can do!"; std::cerr << std::endl; - buttonStatus(PHOTO_SHIFT_NO_BUTTONS); + buttonStatus(PHOTO_SHIFT_NO_BUTTONS); return; } @@ -477,15 +476,15 @@ void PhotoDrop::checkMoveButtons() int maxCol = (count - 1) % mColumns; if ((selectedRow == 0) && (selectedColumn == 0)) { - buttonStatus(PHOTO_SHIFT_RIGHT_ONLY); + buttonStatus(PHOTO_SHIFT_RIGHT_ONLY); } else if ((selectedRow == maxRow) && (selectedColumn == maxCol)) { - buttonStatus(PHOTO_SHIFT_LEFT_ONLY); + buttonStatus(PHOTO_SHIFT_LEFT_ONLY); } else { - buttonStatus(PHOTO_SHIFT_BOTH); + buttonStatus(PHOTO_SHIFT_BOTH); } } @@ -547,17 +546,6 @@ void PhotoDrop::dragEnterEvent(QDragEnterEvent *event) std::cerr << "PhotoDrop::dragEnterEvent()"; std::cerr << std::endl; - -#if 0 - const QStringList& formats = event->mimeData()->formats(); - std::cerr << "dragEnterEvent() Formats" << std::endl; - QStringList::const_iterator it; - for (it = formats.begin(); it != formats.end(); ++it) { - std::cerr << "Format: " << (*it).toStdString(); - std::cerr << std::endl; - } -#endif - if (event->mimeData()->hasUrls()) { std::cerr << "PhotoDrop::dragEnterEvent() Accepting"; @@ -589,8 +577,8 @@ void PhotoDrop::dragMoveEvent(QDragMoveEvent *event) std::cerr << "PhotoDrop::dragMoveEvent()"; std::cerr << std::endl; - event->accept(); - //event->ignore(); + event->accept(); + //event->ignore(); } void PhotoDrop::dropEvent(QDropEvent *event) @@ -611,11 +599,11 @@ void PhotoDrop::dropEvent(QDropEvent *event) std::cerr << "Whole URL: " << uit->toString().toStdString() << std::endl; std::cerr << "or As Local File: " << localpath.toStdString() << std::endl; - PhotoItem* item = new PhotoItem(mHolder, localpath); - addPhotoItem(item); + PhotoItem* item = new PhotoItem(mHolder, localpath, mLastOrder+DEFAULT_ORDER_INCREMENT); + addPhotoItem(item); } - event->setDropAction(Qt::CopyAction); - event->accept(); + event->setDropAction(Qt::CopyAction); + event->accept(); // Notify Listeners. (only happens for drop - not programmatically added). photosChanged(); @@ -624,17 +612,17 @@ void PhotoDrop::dropEvent(QDropEvent *event) { std::cerr << "PhotoDrop::dropEvent Ignoring"; std::cerr << std::endl; - event->ignore(); + event->ignore(); } - checkMoveButtons(); + checkMoveButtons(); } void PhotoDrop::mousePressEvent(QMouseEvent *event) { /* see if this is in the space of one of our children */ - QPoint pos = event->pos(); + QPoint pos = event->pos(); std::cerr << "PhotoDrop::mousePressEvent(" << pos.x() << ", " << pos.y() << ")"; std::cerr << std::endl; @@ -644,7 +632,7 @@ void PhotoDrop::mousePressEvent(QMouseEvent *event) void PhotoDrop::setPhotoItemHolder(PhotoShareItemHolder *holder) { - mHolder = holder; + mHolder = holder; } void PhotoDrop::addPhotoItem(PhotoItem *item) @@ -652,20 +640,25 @@ void PhotoDrop::addPhotoItem(PhotoItem *item) std::cerr << "PhotoDrop::addPhotoItem()"; std::cerr << std::endl; - mPhotos.insert(item); + // record lastOrder number + // so any new photos can be added after this. + if (item->getPhotoDetails().mOrder > mLastOrder) { + mLastOrder = item->getPhotoDetails().mOrder; + } + + mPhotos.insert(item); layout()->addWidget(item); - //checkMoveButtons(); - + //checkMoveButtons(); } bool PhotoDrop::deletePhoto(PhotoItem *item) { - if(mPhotos.contains(item)){ - mPhotos.remove(item); - layout()->removeWidget(item); - delete item; - } - else - return false; + if (mPhotos.contains(item)) { + mPhotos.remove(item); + layout()->removeWidget(item); + delete item; + } + else + return false; } diff --git a/retroshare-gui/src/gui/PhotoShare/PhotoDrop.h b/retroshare-gui/src/gui/PhotoShare/PhotoDrop.h index 4b862eaf6..0b2ed55bc 100644 --- a/retroshare-gui/src/gui/PhotoShare/PhotoDrop.h +++ b/retroshare-gui/src/gui/PhotoShare/PhotoDrop.h @@ -55,7 +55,7 @@ public: PhotoItem *getPhotoIdx(int idx); void getPhotos(QSet& photos); - void addPhotoItem(PhotoItem *item); + void addPhotoItem(PhotoItem *item); void setPhotoItemHolder(PhotoShareItemHolder* holder); /*! @@ -91,8 +91,9 @@ private: PhotoItem *mSelected; int mColumns; - PhotoShareItemHolder* mHolder; - QSet mPhotos; + PhotoShareItemHolder* mHolder; + QSet mPhotos; + uint32_t mLastOrder; }; #endif diff --git a/retroshare-gui/src/gui/PhotoShare/PhotoItem.cpp b/retroshare-gui/src/gui/PhotoShare/PhotoItem.cpp index f0cfaa895..75f553290 100644 --- a/retroshare-gui/src/gui/PhotoShare/PhotoItem.cpp +++ b/retroshare-gui/src/gui/PhotoShare/PhotoItem.cpp @@ -30,6 +30,7 @@ PhotoItem::PhotoItem(PhotoShareItemHolder *holder, const RsPhotoPhoto &photo, QW QWidget(parent), ui(new Ui::PhotoItem), mHolder(holder), mPhotoDetails(photo) { + mState = State::Existing; ui->setupUi(this); setSelected(false); @@ -48,71 +49,66 @@ PhotoItem::PhotoItem(PhotoShareItemHolder *holder, const RsPhotoPhoto &photo, QW ui->idChooser->setVisible(false); } -PhotoItem::PhotoItem(PhotoShareItemHolder *holder, const QString& path, QWidget *parent) : +PhotoItem::PhotoItem(PhotoShareItemHolder *holder, const QString& path, uint32_t order, QWidget *parent) : QWidget(parent), ui(new Ui::PhotoItem), mHolder(holder) { + mState = State::New; + ui->setupUi(this); + mPhotoDetails.mOrder = order; - QPixmap qtn = QPixmap(path); - mThumbNail = qtn; - ui->label_Thumbnail->setPixmap(mThumbNail.scaled(120, 120, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + QPixmap qtn = FilesDefs::getPixmapFromQtResourcePath(path); + mLowResImage = qtn.scaled(512,512, Qt::KeepAspectRatio, Qt::SmoothTransformation); + + ui->label_Thumbnail->setPixmap(qtn.scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation)); setSelected(false); - getPhotoThumbnail(mPhotoDetails.mThumbnail); + getLowResImage(mPhotoDetails.mLowResImage); connect(ui->lineEdit_Title, SIGNAL(editingFinished()), this, SLOT(setTitle())); connect(ui->lineEdit_PhotoGrapher, SIGNAL(editingFinished()), this, SLOT(setPhotoGrapher())); ui->idChooser->loadIds(0, RsGxsId()); +} +void PhotoItem::markForDeletion() +{ + mState = State::Deleted; + setSelected(true); // to repaint with deleted scheme. } void PhotoItem::setSelected(bool selected) { mSelected = selected; + + QString bottomColor; + switch(mState) + { + case State::New: + bottomColor = "#FFFFFF"; + break; + case State::Existing: + bottomColor = "#888888"; + break; + default: + case State::Deleted: + bottomColor = "#EE5555"; + break; + } + if (mSelected) { - ui->photoFrame->setStyleSheet("QFrame#photoFrame{border: 2px solid #9562B8;\nbackground: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #55EE55, stop: 1 #CCCCCC);\nborder-radius: 10px}"); + ui->photoFrame->setStyleSheet("QFrame#photoFrame{border: 2px solid #9562B8;\nbackground: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #55EE55, stop: 1 " + bottomColor + ");\nborder-radius: 10px}"); } else { - ui->photoFrame->setStyleSheet("QFrame#photoFrame{border: 2px solid #CCCCCC;\nbackground: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #EEEEEE, stop: 1 #CCCCCC);\nborder-radius: 10px}"); + ui->photoFrame->setStyleSheet("QFrame#photoFrame{border: 2px solid #CCCCCC;\nbackground: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #EEEEEE, stop: 1 " + bottomColor + ");\nborder-radius: 10px}"); } update(); } -bool PhotoItem::getPhotoThumbnail(RsPhotoThumbnail &nail) -{ - const QPixmap *tmppix = &mThumbNail; - - QByteArray ba; - QBuffer buffer(&ba); - - if(!tmppix->isNull()) - { - // send chan image - - buffer.open(QIODevice::WriteOnly); - tmppix->save(&buffer, "PNG"); // writes image into ba in PNG format - - RsPhotoThumbnail tmpnail; - tmpnail.data = (uint8_t *) ba.data(); - tmpnail.size = ba.size(); - - nail.copyFrom(tmpnail); - - return true; - } - - nail.data = NULL; - nail.size = 0; - return false; -} - - - void PhotoItem::setTitle(){ mPhotoDetails.mMeta.mMsgName = ui->lineEdit_Title->text().toStdString(); @@ -120,13 +116,11 @@ void PhotoItem::setTitle(){ void PhotoItem::setPhotoGrapher() { - mPhotoDetails.mPhotographer = ui->lineEdit_PhotoGrapher->text().toStdString(); + // mPhotoDetails.mPhotographer = ui->lineEdit_PhotoGrapher->text().toStdString(); } const RsPhotoPhoto& PhotoItem::getPhotoDetails() { - - if (ui->idChooser->isVisible()) { RsGxsId id; switch (ui->idChooser->getChosenId(id)) { @@ -152,28 +146,44 @@ PhotoItem::~PhotoItem() void PhotoItem::setUp() { - mTitleLabel = new QLabel(); mPhotoGrapherLabel = new QLabel(); mTitleLabel->setText(QString::fromStdString(mPhotoDetails.mMeta.mMsgName)); - mPhotoGrapherLabel->setText(QString::fromStdString(mPhotoDetails.mPhotographer)); - ui->editLayOut->addWidget(mPhotoGrapherLabel); ui->editLayOut->addWidget(mTitleLabel); - updateImage(mPhotoDetails.mThumbnail); + updateImage(mPhotoDetails.mLowResImage); } -void PhotoItem::updateImage(const RsPhotoThumbnail &thumbnail) +bool PhotoItem::getLowResImage(RsGxsImage &image) { - if (thumbnail.data != NULL) + const QPixmap *tmppix = &mLowResImage; + + QByteArray ba; + QBuffer buffer(&ba); + + if(!tmppix->isNull()) + { + buffer.open(QIODevice::WriteOnly); + tmppix->save(&buffer, "JPG"); + image.copy((uint8_t *) ba.data(), ba.size()); + return true; + } + + image.clear(); + return false; +} + +void PhotoItem::updateImage(const RsGxsImage &image) +{ + if (image.mData != NULL) { QPixmap qtn; - qtn.loadFromData(thumbnail.data, thumbnail.size, thumbnail.type.c_str()); - ui->label_Thumbnail->setPixmap(qtn.scaled(120, 120, Qt::KeepAspectRatio, Qt::SmoothTransformation)); - mThumbNail = qtn; + qtn.loadFromData(image.mData, image.mSize); + ui->label_Thumbnail->setPixmap(qtn.scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + mLowResImage = qtn; } } diff --git a/retroshare-gui/src/gui/PhotoShare/PhotoItem.h b/retroshare-gui/src/gui/PhotoShare/PhotoItem.h index ee7b4e578..376973a11 100644 --- a/retroshare-gui/src/gui/PhotoShare/PhotoItem.h +++ b/retroshare-gui/src/gui/PhotoShare/PhotoItem.h @@ -36,19 +36,23 @@ class PhotoItem : public QWidget, public PhotoShareItem public: + enum State { New, Existing, Modified, Deleted }; + PhotoItem(PhotoShareItemHolder *holder, const RsPhotoPhoto& photo, QWidget* parent = 0); - PhotoItem(PhotoShareItemHolder *holder, const QString& path, QWidget* parent = 0); // for new photos. + PhotoItem(PhotoShareItemHolder *holder, const QString& path, uint32_t order, QWidget* parent = 0); // for new photos. ~PhotoItem(); void setSelected(bool selected); bool isSelected(){ return mSelected; } const RsPhotoPhoto& getPhotoDetails(); - bool getPhotoThumbnail(RsPhotoThumbnail &nail); + bool getLowResImage(RsGxsImage &image); + void markForDeletion(); + State getState() { return mState; } protected: void mousePressEvent(QMouseEvent *event); private: - void updateImage(const RsPhotoThumbnail &thumbnail); + void updateImage(const RsGxsImage &image); void setUp(); private slots: @@ -58,11 +62,12 @@ private: private: Ui::PhotoItem *ui; - QPixmap mThumbNail; + QPixmap mLowResImage; - QPixmap getPixmap() { return mThumbNail; } + QPixmap getPixmap() { return mLowResImage; } bool mSelected; + State mState; PhotoShareItemHolder* mHolder; RsPhotoPhoto mPhotoDetails; diff --git a/retroshare-gui/src/gui/PhotoShare/PhotoShare.cpp b/retroshare-gui/src/gui/PhotoShare/PhotoShare.cpp index 5c00bcb7a..584433d42 100644 --- a/retroshare-gui/src/gui/PhotoShare/PhotoShare.cpp +++ b/retroshare-gui/src/gui/PhotoShare/PhotoShare.cpp @@ -31,7 +31,7 @@ #include #include -#include "AlbumCreateDialog.h" +#include "AlbumGroupDialog.h" #include "AlbumItem.h" #include "PhotoItem.h" @@ -60,7 +60,7 @@ #define IS_ALBUM_ADMIN(subscribeFlags) (subscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN) #define IS_ALBUM_SUBSCRIBED(subscribeFlags) (subscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_SUBSCRIBED) -#define IS_ALBUM_N_SUBSCR_OR_ADMIN(subscribeFlags) ((subscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_MASK) == 0) +#define IS_ALBUM_N_SUBSCR_OR_ADMIN(subscribeFlags) (!IS_ALBUM_ADMIN(subscribeFlags) && !IS_ALBUM_SUBSCRIBED(subscribeFlags)) /** Constructor */ @@ -73,10 +73,11 @@ PhotoShare::PhotoShare(QWidget *parent) mPhotoSelected = NULL; connect( ui.toolButton_NewAlbum, SIGNAL(clicked()), this, SLOT(createAlbum())); - connect( ui.toolButton_ViewAlbum, SIGNAL(clicked()), this, SLOT(OpenAlbumDialog())); + connect( ui.toolButton_ViewEditAlbum, SIGNAL(clicked()), this, SLOT(OpenViewEditAlbumDialog())); + connect( ui.toolButton_EditAlbumPhotos, SIGNAL(clicked()), this, SLOT(OpenEditAlbumPhotosDialog())); connect( ui.toolButton_SlideShow, SIGNAL(clicked()), this, SLOT(OpenSlideShow())); connect( ui.toolButton_subscribe, SIGNAL(clicked()), this, SLOT(subscribeToAlbum())); - connect(ui.toolButton_ViewPhoto, SIGNAL(clicked()), this, SLOT(OpenPhotoDialog())); + connect( ui.toolButton_ViewPhoto, SIGNAL(clicked()), this, SLOT(OpenPhotoDialog())); connect( ui.pushButton_YourAlbums, SIGNAL(clicked()), this, SLOT(updateAlbums())); connect( ui.pushButton_SharedAlbums, SIGNAL(clicked()), this, SLOT(updateAlbums())); @@ -133,16 +134,33 @@ void PhotoShare::notifySelection(PhotoShareItem *selection) grpIds.push_back(mAlbumSelected->getAlbum().mMeta.mGroupId); requestPhotoData(grpIds); } + + /* update button status */ + /* if own album - Enable Edit Photos */ + if (IS_ALBUM_ADMIN(mAlbumSelected->getAlbum().mMeta.mSubscribeFlags)) + { + ui.toolButton_EditAlbumPhotos->setEnabled(true); + } + + /* is subscribed enable slideshow (includes own) */ + if (IS_ALBUM_SUBSCRIBED(mAlbumSelected->getAlbum().mMeta.mSubscribeFlags)) + { + ui.toolButton_SlideShow->setEnabled(true); + } + + /* enable view / subscribe - as all can use this (sub/unsub/delete) */ + ui.toolButton_ViewEditAlbum->setEnabled(true); + ui.toolButton_subscribe->setEnabled(true); } - else if((pItem = dynamic_cast(selection)) != NULL) + else if ((pItem = dynamic_cast(selection)) != NULL) { - if(mPhotoSelected == pItem) + if (mPhotoSelected == pItem) { mPhotoSelected->setSelected(true); } else { - if(mPhotoSelected == NULL) + if (mPhotoSelected == NULL) { mPhotoSelected = pItem; } @@ -174,7 +192,7 @@ void PhotoShare::checkUpdate() //insertAlbums(); std::list grpIds; rsPhoto->groupsChanged(grpIds); - if(!grpIds.empty()) + if (!grpIds.empty()) { RsTokReqOptions opts; uint32_t token; @@ -184,7 +202,7 @@ void PhotoShare::checkUpdate() GxsMsgIdResult res; rsPhoto->msgsChanged(res); - if(!res.empty()) + if (!res.empty()) requestPhotoList(res); } @@ -215,11 +233,25 @@ void PhotoShare::OpenSlideShow() void PhotoShare::createAlbum() { - AlbumCreateDialog albumCreate(mPhotoQueue, rsPhoto, this); + AlbumGroupDialog albumCreate(this); albumCreate.exec(); } -void PhotoShare::OpenAlbumDialog() +void PhotoShare::OpenViewEditAlbumDialog() +{ + if (mAlbumSelected) { + const RsPhotoAlbum &album = mAlbumSelected->getAlbum(); + GxsGroupDialog::Mode mode = GxsGroupDialog::MODE_SHOW; + bool canEdit = IS_ALBUM_ADMIN(album.mMeta.mSubscribeFlags); + if (canEdit) { + mode = GxsGroupDialog::MODE_EDIT; + } + AlbumGroupDialog agDialog(mode, album.mMeta.mGroupId, this); + agDialog.exec(); + } +} + +void PhotoShare::OpenEditAlbumPhotosDialog() { if (mAlbumSelected) { AlbumDialog dlg(mAlbumSelected->getAlbum(), mPhotoQueue, rsPhoto); @@ -305,62 +337,64 @@ void PhotoShare::clearPhotos() void PhotoShare::updateAlbums() { - clearAlbums(); + // disable all buttons - as nothing is selected. + ui.toolButton_ViewEditAlbum->setEnabled(false); + ui.toolButton_EditAlbumPhotos->setEnabled(false); + ui.toolButton_subscribe->setEnabled(false); + ui.toolButton_SlideShow->setEnabled(false); + QLayout *alayout = ui.scrollAreaWidgetContents->layout(); QSetIterator sit(mAlbumItems); - if(ui.pushButton_YourAlbums->isChecked()) + if (ui.pushButton_YourAlbums->isChecked()) { + ui.toolButton_ViewEditAlbum->setText("Edit Album Details"); + ui.toolButton_subscribe->setText("Delete Album"); + ui.toolButton_subscribe->setIcon(QIcon(":/images/album_unsubscribe.png")); - ui.toolButton_subscribe->setEnabled(false); - ui.toolButton_NewAlbum->setEnabled(true); - ui.toolButton_SlideShow->setEnabled(true); - - while(sit.hasNext()){ + while (sit.hasNext()) { AlbumItem* item = sit.next(); uint32_t flags = item->getAlbum().mMeta.mSubscribeFlags; if(IS_ALBUM_ADMIN(flags)) + { alayout->addWidget(item); + } } - }else if(ui.pushButton_SubscribedAlbums->isChecked()) + } else if (ui.pushButton_SubscribedAlbums->isChecked()) { - ui.toolButton_subscribe->setEnabled(true); + ui.toolButton_ViewEditAlbum->setText("View Album Details"); ui.toolButton_subscribe->setText("Unsubscribe From Album"); ui.toolButton_subscribe->setIcon(QIcon(":/images/album_unsubscribe.png")); - //ui.toolButton_NewAlbum->setEnabled(false); - ui.toolButton_SlideShow->setEnabled(true); - while(sit.hasNext()){ + while (sit.hasNext()) { AlbumItem* item = sit.next(); uint32_t flags = item->getAlbum().mMeta.mSubscribeFlags; - if(IS_ALBUM_SUBSCRIBED(flags)) + if(!IS_ALBUM_ADMIN(flags) && IS_ALBUM_SUBSCRIBED(flags)) { alayout->addWidget(item); + } } - }else if(ui.pushButton_SharedAlbums->isChecked()) + } else if (ui.pushButton_SharedAlbums->isChecked()) { - - ui.toolButton_subscribe->setEnabled(true); + ui.toolButton_ViewEditAlbum->setText("View Album Details"); ui.toolButton_subscribe->setText("Subscribe To Album"); ui.toolButton_subscribe->setIcon(QIcon(":/images/album_subscribe.png")); - //ui.toolButton_NewAlbum->setEnabled(false); - ui.toolButton_SlideShow->setEnabled(false); - while(sit.hasNext()){ + while (sit.hasNext()){ AlbumItem* item = sit.next(); uint32_t flags = item->getAlbum().mMeta.mSubscribeFlags; - if(IS_ALBUM_N_SUBSCR_OR_ADMIN(flags)) + if (IS_ALBUM_N_SUBSCR_OR_ADMIN(flags)) { alayout->addWidget(item); - + } } } } @@ -370,13 +404,13 @@ void PhotoShare::deleteAlbum(const RsGxsGroupId &grpId) QSetIterator sit(mAlbumItems); - while(sit.hasNext()) + while (sit.hasNext()) { AlbumItem* item = sit.next(); - if(item->getAlbum().mMeta.mGroupId == grpId){ + if (item->getAlbum().mMeta.mGroupId == grpId){ - if(mAlbumSelected == item) + if (mAlbumSelected == item) { item->setSelected(false); mAlbumSelected = NULL; @@ -394,8 +428,6 @@ void PhotoShare::deleteAlbum(const RsGxsGroupId &grpId) void PhotoShare::addAlbum(const RsPhotoAlbum &album) { - std::cerr << " PhotoShare::addAlbum() AlbumId: " << album.mMeta.mGroupId << std::endl; - deleteAlbum(album.mMeta.mGroupId); // remove from ui AlbumItem *item = new AlbumItem(album, this, this); mAlbumItems.insert(item); @@ -404,29 +436,36 @@ void PhotoShare::addAlbum(const RsPhotoAlbum &album) void PhotoShare::addPhoto(const RsPhotoPhoto &photo) { - std::cerr << "PhotoShare::addPhoto() AlbumId: " << photo.mMeta.mGroupId; - std::cerr << " PhotoId: " << photo.mMeta.mMsgId; - std::cerr << std::endl; - - PhotoItem* item = new PhotoItem(this, photo, this); - mPhotoItems.insert(item); + if (!photo.mLowResImage.empty()) + { + PhotoItem* item = new PhotoItem(this, photo, this); + mPhotoItems.insert(item); + } } void PhotoShare::subscribeToAlbum() { - if(mAlbumSelected){ + if (mAlbumSelected){ RsGxsGroupId id = mAlbumSelected->getAlbum().mMeta.mGroupId; uint32_t token; - if(IS_ALBUM_SUBSCRIBED(mAlbumSelected->getAlbum().mMeta.mSubscribeFlags)) + if (IS_ALBUM_ADMIN(mAlbumSelected->getAlbum().mMeta.mSubscribeFlags)) + { + // TODO support delete. + return; + } + else if (IS_ALBUM_SUBSCRIBED(mAlbumSelected->getAlbum().mMeta.mSubscribeFlags)) + { rsPhoto->subscribeToAlbum(token, id, false); - else if(IS_ALBUM_ADMIN(mAlbumSelected->getAlbum().mMeta.mSubscribeFlags)) - return; - else if(IS_ALBUM_N_SUBSCR_OR_ADMIN( - mAlbumSelected->getAlbum().mMeta.mSubscribeFlags)) + } + else if (IS_ALBUM_N_SUBSCR_OR_ADMIN(mAlbumSelected->getAlbum().mMeta.mSubscribeFlags)) + { rsPhoto->subscribeToAlbum(token, id, true); + } else + { return; + } mPhotoQueue->queueRequest(token, TOKENREQ_GROUPINFO, RS_TOKREQ_ANSTYPE_ACK, 0); } @@ -435,11 +474,11 @@ void PhotoShare::subscribeToAlbum() void PhotoShare::updatePhotos() { - if(mAlbumSelected) + if (mAlbumSelected) { QSetIterator sit(mPhotoItems); - while(sit.hasNext()) + while (sit.hasNext()) { QLayout *layout = ui.scrollAreaWidgetContents_2->layout(); layout->addWidget(sit.next()); @@ -449,43 +488,16 @@ void PhotoShare::updatePhotos() /**************************** Request / Response Filling of Data ************************/ - -void PhotoShare::requestAlbumList(std::list& ids) -{ - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_GROUP_IDS; - uint32_t token; - mPhotoQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_LIST, opts, ids, 0); -} - void PhotoShare::requestPhotoList(GxsMsgReq& req) { RsTokReqOptions opts; opts.mReqType = GXS_REQUEST_TYPE_MSG_IDS; + opts.mOptions = RS_TOKREQOPT_MSG_LATEST; uint32_t token; mPhotoQueue->requestMsgInfo(token, RS_TOKREQ_ANSTYPE_LIST, opts, req, 0); return; } - -void PhotoShare::loadAlbumList(const uint32_t &token) -{ - std::cerr << "PhotoShare::loadAlbumList()"; - std::cerr << std::endl; - - std::list albumIds; - rsPhoto->getGroupList(token, albumIds); - - requestAlbumData(albumIds); - - std::list::iterator it; - for(it = albumIds.begin(); it != albumIds.end(); ++it) - { - requestPhotoList(*it); - } -} - - void PhotoShare::requestAlbumData(std::list &ids) { RsTokReqOptions opts; @@ -504,20 +516,14 @@ void PhotoShare::requestAlbumData() bool PhotoShare::loadAlbumData(const uint32_t &token) { - std::cerr << "PhotoShare::loadAlbumData()"; - std::cerr << std::endl; - std::vector albums; rsPhoto->getAlbum(token, albums); std::vector::iterator vit = albums.begin(); - for(; vit != albums.end(); ++vit) + for (; vit != albums.end(); ++vit) { RsPhotoAlbum& album = *vit; - - std::cerr << " PhotoShare::addAlbum() AlbumId: " << album.mMeta.mGroupId << std::endl; - addAlbum(album); } @@ -528,7 +534,6 @@ bool PhotoShare::loadAlbumData(const uint32_t &token) void PhotoShare::requestPhotoList(const RsGxsGroupId &albumId) { - std::list grpIds; grpIds.push_back(albumId); RsTokReqOptions opts; @@ -544,7 +549,7 @@ void PhotoShare::acknowledgeGroup(const uint32_t &token) RsGxsGroupId grpId; rsPhoto->acknowledgeGrp(token, grpId); - if(!grpId.isNull()) + if (!grpId.isNull()) { std::list grpIds; grpIds.push_back(grpId); @@ -560,29 +565,10 @@ void PhotoShare::acknowledgeMessage(const uint32_t &token) { std::pair p; rsPhoto->acknowledgeMsg(token, p); - - // just acknowledge don't load it - // loading is only instigated by clicking an album (i.e. requesting photo data) - // but load it if the album is selected -// if(!p.first.empty()) -// { -// if(mAlbumSelected) -// { -// if(mAlbumSelected->getAlbum().mMeta.mGroupId == p.first) -// { -// std::list grpIds; -// grpIds.push_back(p.first); -// requestPhotoData(grpIds); -// } -// } -// } } void PhotoShare::loadPhotoList(const uint32_t &token) { - std::cerr << "PhotoShare::loadPhotoList()"; - std::cerr << std::endl; - GxsMsgIdResult res; rsPhoto->getMsgList(token, res); @@ -602,6 +588,7 @@ void PhotoShare::requestPhotoData(const std::list& grpIds) { RsTokReqOptions opts; opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; + opts.mOptions = RS_TOKREQOPT_MSG_LATEST; uint32_t token; mPhotoQueue->requestMsgInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, grpIds, 0); } @@ -609,9 +596,6 @@ void PhotoShare::requestPhotoData(const std::list& grpIds) void PhotoShare::loadPhotoData(const uint32_t &token) { - std::cerr << "PhotoShare::loadPhotoData()"; - std::cerr << std::endl; - clearPhotos(); PhotoResult res; @@ -619,18 +603,15 @@ void PhotoShare::loadPhotoData(const uint32_t &token) PhotoResult::iterator mit = res.begin(); - for(; mit != res.end(); ++mit) + for (; mit != res.end(); ++mit) { std::vector& photoV = mit->second; std::vector::iterator vit = photoV.begin(); - for(; vit != photoV.end(); ++vit) + for (; vit != photoV.end(); ++vit) { RsPhotoPhoto& photo = *vit; addPhoto(photo); - std::cerr << "PhotoShare::loadPhotoData() AlbumId: " << photo.mMeta.mGroupId; - std::cerr << " PhotoId: " << photo.mMeta.mMsgId; - std::cerr << std::endl; } } updatePhotos(); @@ -641,9 +622,6 @@ void PhotoShare::loadPhotoData(const uint32_t &token) void PhotoShare::loadRequest(const TokenQueue *queue, const TokenRequest &req) { - std::cerr << "PhotoShare::loadRequest()"; - std::cerr << std::endl; - if (queue == mPhotoQueue) { /* now switch on req */ @@ -652,9 +630,6 @@ void PhotoShare::loadRequest(const TokenQueue *queue, const TokenRequest &req) case TOKENREQ_GROUPINFO: switch(req.mAnsType) { - case RS_TOKREQ_ANSTYPE_LIST: - loadAlbumList(req.mToken); - break; case RS_TOKREQ_ANSTYPE_DATA: loadAlbumData(req.mToken); break; diff --git a/retroshare-gui/src/gui/PhotoShare/PhotoShare.h b/retroshare-gui/src/gui/PhotoShare/PhotoShare.h index 9bebcd1c5..ea13e7669 100644 --- a/retroshare-gui/src/gui/PhotoShare/PhotoShare.h +++ b/retroshare-gui/src/gui/PhotoShare/PhotoShare.h @@ -28,7 +28,6 @@ #include "retroshare/rsphoto.h" #include "retroshare-gui/mainpage.h" -#include "AlbumCreateDialog.h" #include "AlbumDialog.h" #include "PhotoDialog.h" @@ -39,6 +38,8 @@ #include "util/TokenQueue.h" #include "PhotoShareItemHolder.h" +#define IMAGE_PHOTO ":/icons/png/photo.png" + namespace Ui { class PhotoShare; } @@ -51,12 +52,17 @@ public: PhotoShare(QWidget *parent = 0); ~PhotoShare(); + virtual QIcon iconPixmap() const { return QIcon(IMAGE_PHOTO) ; } + virtual QString pageName() const { return tr("Photo Albums") ; } + virtual QString helpText() const { return ""; } + void notifySelection(PhotoShareItem* selection); private slots: void checkUpdate(); void createAlbum(); - void OpenAlbumDialog(); + void OpenViewEditAlbumDialog(); + void OpenEditAlbumPhotosDialog(); void OpenPhotoDialog(); void OpenSlideShow(); void updateAlbums(); @@ -65,7 +71,6 @@ private slots: private: /* Request Response Functions for loading data */ - void requestAlbumList(std::list &ids); void requestAlbumData(std::list &ids); /*! @@ -77,7 +82,6 @@ private: void requestPhotoData(GxsMsgReq &photoIds); void requestPhotoData(const std::list &grpIds); - void loadAlbumList(const uint32_t &token); bool loadAlbumData(const uint32_t &token); void loadPhotoList(const uint32_t &token); void loadPhotoData(const uint32_t &token); diff --git a/retroshare-gui/src/gui/PhotoShare/PhotoShare.ui b/retroshare-gui/src/gui/PhotoShare/PhotoShare.ui index 78e3ed538..19230a67f 100644 --- a/retroshare-gui/src/gui/PhotoShare/PhotoShare.ui +++ b/retroshare-gui/src/gui/PhotoShare/PhotoShare.ui @@ -20,7 +20,16 @@ - + + 2 + + + 2 + + + 2 + + 2 @@ -29,8 +38,8 @@ Create Album - - :/images/add_image24.png:/images/add_image24.png + + :/images/album_create_64.png:/images/album_create_64.png @@ -47,9 +56,32 @@ - + - View Album + Edit Album Details + + + + :/images/edit_16.png:/images/edit_16.png + + + + 24 + 24 + + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + Edit Album Photos @@ -140,7 +172,16 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -287,7 +328,7 @@ QWidget#scrollAreaWidgetContents{border: none;} - + diff --git a/retroshare-gui/src/gui/PhotoShare/PhotoSlideShow.cpp b/retroshare-gui/src/gui/PhotoShare/PhotoSlideShow.cpp index 8b1df126f..5213adc52 100644 --- a/retroshare-gui/src/gui/PhotoShare/PhotoSlideShow.cpp +++ b/retroshare-gui/src/gui/PhotoShare/PhotoSlideShow.cpp @@ -20,6 +20,10 @@ #include "gui/PhotoShare/PhotoSlideShow.h" #include "gui/PhotoShare/PhotoDrop.h" +#include "gui/gxs/GxsIdDetails.h" + +#define IMAGE_FULLSCREEN ":/icons/fullscreen.png" +#define IMAGE_FULLSCREENEXIT ":/icons/fullscreen-exit.png" #include @@ -175,16 +179,10 @@ void PhotoSlideShow::loadImage() if (ptr) { /* load into the slot */ - if (ptr->mThumbnail.data != NULL) + if (ptr->mLowResImage.mData != NULL) { QPixmap qtn; - - // copy the data for Qpixmap to use. - RsPhotoThumbnail tn; - tn.copyFrom(ptr->mThumbnail); - qtn.loadFromData(tn.data, tn.size, tn.type.c_str()); - tn.data = 0; - + GxsIdDetails::loadPixmapFromData(ptr->mLowResImage.mData, ptr->mLowResImage.mSize,qtn, GxsIdDetails::ORIGINAL); QPixmap sqtn = qtn.scaled(800, 600, Qt::KeepAspectRatio, Qt::SmoothTransformation); ui.imgLabel->setPixmap(sqtn); @@ -223,6 +221,7 @@ void PhotoSlideShow::requestPhotos() { RsTokReqOptions opts; opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; + opts.mOptions = RS_TOKREQOPT_MSG_LATEST; uint32_t token; std::list grpIds; grpIds.push_back(mAlbum.mMeta.mGroupId); @@ -249,9 +248,8 @@ bool PhotoSlideShow::loadPhotoData(const uint32_t &token) RsPhotoPhoto& photo = *vit; RsPhotoPhoto *ptr = new RsPhotoPhoto; *ptr = photo; - ptr->mThumbnail.data = 0; - ptr->mThumbnail.copyFrom(photo.mThumbnail); - ptr->mOrder = i++; + + ptr->mLowResImage = photo.mLowResImage; // copies data. mPhotos[photo.mMeta.mMsgId] = ptr; mPhotoOrder[ptr->mOrder] = photo.mMeta.mMsgId; @@ -305,10 +303,12 @@ void PhotoSlideShow::setFullScreen() show(); raise(); #endif + ui.fullscreenButton->setIcon(QIcon(IMAGE_FULLSCREENEXIT)); } else { setWindowState( windowState() ^ Qt::WindowFullScreen ); show(); + ui.fullscreenButton->setIcon(QIcon(IMAGE_FULLSCREEN)); } } diff --git a/retroshare-gui/src/gui/PhotoShare/PhotoSlideShow.ui b/retroshare-gui/src/gui/PhotoShare/PhotoSlideShow.ui index bc771eec0..50d031e4e 100644 --- a/retroshare-gui/src/gui/PhotoShare/PhotoSlideShow.ui +++ b/retroshare-gui/src/gui/PhotoShare/PhotoSlideShow.ui @@ -98,8 +98,8 @@ - - :/images/window_fullscreen.png:/images/window_fullscreen.png + + :/icons/fullscreen.png:/icons/fullscreen.png false @@ -157,6 +157,7 @@ + diff --git a/retroshare-gui/src/gui/PluginsPage.h b/retroshare-gui/src/gui/PluginsPage.h index 4832a1ac6..36d4f9c8a 100644 --- a/retroshare-gui/src/gui/PluginsPage.h +++ b/retroshare-gui/src/gui/PluginsPage.h @@ -21,7 +21,7 @@ #ifndef _PLUGINS_PAGE_H_ #define _PLUGINS_PAGE_H_ -#include "mainpage.h" +#include #include #include diff --git a/retroshare-gui/src/gui/Posted/PostedCardView.cpp b/retroshare-gui/src/gui/Posted/PostedCardView.cpp index 1fef4f160..0aafaeeb5 100644 --- a/retroshare-gui/src/gui/Posted/PostedCardView.cpp +++ b/retroshare-gui/src/gui/Posted/PostedCardView.cpp @@ -28,6 +28,8 @@ #include "gui/feeds/FeedHolder.h" #include "gui/gxs/GxsIdDetails.h" #include "util/misc.h" +#include "gui/common/FilesDefs.h" +#include "util/qtthreadsutils.h" #include "util/HandleRichText.h" #include "ui_PostedCardView.h" @@ -39,39 +41,76 @@ /** Constructor */ -PostedCardView::PostedCardView(FeedHolder *feedHolder, uint32_t feedId, const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, bool isHome, bool autoUpdate) : - GxsFeedItem(feedHolder, feedId, groupId, messageId, isHome, rsPosted, autoUpdate) +PostedCardView::PostedCardView(FeedHolder *feedHolder, uint32_t feedId, const RsGroupMetaData &group_meta, const RsGxsMessageId &post_id, bool isHome, bool autoUpdate) + : BasePostedItem(feedHolder, feedId, group_meta, post_id, isHome, autoUpdate) { - setup(); - - requestGroup(); - requestMessage(); - requestComment(); + setup(); } -PostedCardView::PostedCardView(FeedHolder *feedHolder, uint32_t feedId, const RsPostedGroup &group, const RsPostedPost &post, bool isHome, bool autoUpdate) : - GxsFeedItem(feedHolder, feedId, post.mMeta.mGroupId, post.mMeta.mMsgId, isHome, rsPosted, autoUpdate) +PostedCardView::PostedCardView(FeedHolder *feedHolder, uint32_t feedId, const RsGxsGroupId &groupId, const RsGxsMessageId& post_id, bool isHome, bool autoUpdate) + : BasePostedItem(feedHolder, feedId, groupId, post_id, isHome, autoUpdate) { - setup(); - - mMessageId = post.mMeta.mMsgId; - - - setGroup(group, false); - setPost(post); - requestComment(); + setup(); + loadGroup(); } -PostedCardView::PostedCardView(FeedHolder *feedHolder, uint32_t feedId, const RsPostedPost &post, bool isHome, bool autoUpdate) : - GxsFeedItem(feedHolder, feedId, post.mMeta.mGroupId, post.mMeta.mMsgId, isHome, rsPosted, autoUpdate) +void PostedCardView::setCommentsSize(int comNb) { - setup(); + QString sComButText = tr("Comment"); + if (comNb == 1) + sComButText = sComButText.append("(1)"); + else if(comNb > 1) + sComButText = tr("Comments ").append("(%1)").arg(comNb); - requestGroup(); - setPost(post); - requestComment(); + ui->commentButton->setText(sComButText); } +void PostedCardView::makeDownVote() +{ + RsGxsGrpMsgIdPair msgId; + msgId.first = mPost.mMeta.mGroupId; + msgId.second = mPost.mMeta.mMsgId; + + ui->voteUpButton->setEnabled(false); + ui->voteDownButton->setEnabled(false); + + emit vote(msgId, false); +} + +void PostedCardView::makeUpVote() +{ + RsGxsGrpMsgIdPair msgId; + msgId.first = mPost.mMeta.mGroupId; + msgId.second = mPost.mMeta.mMsgId; + + ui->voteUpButton->setEnabled(false); + ui->voteDownButton->setEnabled(false); + + emit vote(msgId, true); +} + +void PostedCardView::setReadStatus(bool isNew, bool isUnread) +{ + if (isUnread) + { + ui->readButton->setChecked(true); + ui->readButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/message-state-unread.png")); + } + else + { + ui->readButton->setChecked(false); + ui->readButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/message-state-read.png")); + } + + ui->newLabel->setVisible(isNew); + + ui->mainFrame->setProperty("new", isNew); + ui->mainFrame->style()->unpolish(ui->mainFrame); + ui->mainFrame->style()->polish( ui->mainFrame); +} + +void PostedCardView::setComment(const RsGxsComment& cmt) {} + PostedCardView::~PostedCardView() { delete(ui); @@ -107,6 +146,8 @@ void PostedCardView::setup() QAction *CopyLinkAction = new QAction(QIcon(""),tr("Copy RetroShare Link"), this); connect(CopyLinkAction, SIGNAL(triggered()), this, SLOT(copyMessageLink())); + QAction *showInPeopleAct = new QAction(QIcon(), tr("Show author in people tab"), this); + connect(showInPeopleAct, SIGNAL(triggered()), this, SLOT(showAuthorInPeople())); int S = QFontMetricsF(font()).height() ; @@ -118,213 +159,123 @@ void PostedCardView::setup() QMenu *menu = new QMenu(); menu->addAction(CopyLinkAction); + menu->addSeparator(); + menu->addAction(showInPeopleAct); ui->shareButton->setMenu(menu); ui->clearButton->hide(); ui->readAndClearButton->hide(); } -bool PostedCardView::setGroup(const RsPostedGroup &group, bool doFill) -{ - if (groupId() != group.mMeta.mGroupId) { - std::cerr << "PostedCardView::setGroup() - Wrong id, cannot set post"; - std::cerr << std::endl; - return false; - } - mGroup = group; - - if (doFill) { - fill(); - } - - return true; -} - -bool PostedCardView::setPost(const RsPostedPost &post, bool doFill) -{ - if (groupId() != post.mMeta.mGroupId || messageId() != post.mMeta.mMsgId) { - std::cerr << "PostedCardView::setPost() - Wrong id, cannot set post"; - std::cerr << std::endl; - return false; - } - - mPost = post; - - if (doFill) { - fill(); - } - - return true; -} - -void PostedCardView::loadGroup(const uint32_t &token) -{ - std::vector groups; - if (!rsPosted->getGroupData(token, groups)) - { - std::cerr << "PostedCardView::loadGroup() ERROR getting data"; - std::cerr << std::endl; - return; - } - - if (groups.size() != 1) - { - std::cerr << "PostedCardView::loadGroup() Wrong number of Items"; - std::cerr << std::endl; - return; - } - - setGroup(groups[0]); -} - -void PostedCardView::loadMessage(const uint32_t &token) -{ - std::vector posts; - std::vector cmts; - if (!rsPosted->getPostData(token, posts, cmts)) - { - std::cerr << "GxsChannelPostItem::loadMessage() ERROR getting data"; - std::cerr << std::endl; - return; - } - - if (posts.size() == 1) - { - setPost(posts[0]); - } - else if (cmts.size() == 1) - { - RsGxsComment cmt = cmts[0]; - - //ui->newCommentLabel->show(); - //ui->commLabel->show(); - //ui->commLabel->setText(QString::fromUtf8(cmt.mComment.c_str())); - - //Change this item to be uploaded with thread element. - setMessageId(cmt.mMeta.mThreadId); - requestMessage(); - } - else - { - std::cerr << "GxsChannelPostItem::loadMessage() Wrong number of Items. Remove It."; - std::cerr << std::endl; - removeItem(); - return; - } -} - -void PostedCardView::loadComment(const uint32_t &token) -{ - std::vector cmts; - if (!rsPosted->getRelatedComments(token, cmts)) - { - std::cerr << "GxsChannelPostItem::loadComment() ERROR getting data"; - std::cerr << std::endl; - return; - } - - size_t comNb = cmts.size(); - QString sComButText = tr("Comment"); - if (comNb == 1) { - sComButText = sComButText.append("(1)"); - } else if (comNb > 1) { - sComButText = " " + tr("Comments").append(" (%1)").arg(comNb); - } - ui->commentButton->setText(sComButText); -} void PostedCardView::fill() { - if (isLoading()) { - /* Wait for all requests */ - return; - } +// if (isLoading()) { +// /* Wait for all requests */ +// return; +// } - QPixmap sqpixmap2 = QPixmap(":/images/thumb-default.png"); + RsReputationLevel overall_reputation = rsReputations->overallReputationLevel(mPost.mMeta.mAuthorId); + bool redacted = (overall_reputation == RsReputationLevel::LOCALLY_NEGATIVE); - mInFill = true; - int desired_height = 1.5*(ui->voteDownButton->height() + ui->voteUpButton->height() + ui->scoreLabel->height()); - int desired_width = sqpixmap2.width()*desired_height/(float)sqpixmap2.height(); - - QDateTime qtime; - qtime.setTime_t(mPost.mMeta.mPublishTs); - QString timestamp = qtime.toString("hh:mm dd-MMM-yyyy"); - QString timestamp2 = misc::timeRelativeToNow(mPost.mMeta.mPublishTs); - ui->dateLabel->setText(timestamp2); - ui->dateLabel->setToolTip(timestamp); - - ui->fromLabel->setId(mPost.mMeta.mAuthorId); - - // Use QUrl to check/parse our URL - // The only combination that seems to work: load as EncodedUrl, extract toEncoded(). - QByteArray urlarray(mPost.mLink.c_str()); - QUrl url = QUrl::fromEncoded(urlarray.trimmed()); - QString urlstr = "Invalid Link"; - QString sitestr = "Invalid Link"; - - bool urlOkay = url.isValid(); - if (urlOkay) - { - QString scheme = url.scheme(); - if ((scheme != "https") - && (scheme != "http") - && (scheme != "ftp") - && (scheme != "retroshare")) - { - urlOkay = false; - sitestr = "Invalid Link Scheme"; - } - } - - if (urlOkay) - { - urlstr = QString(" "); - urlstr += messageName(); - urlstr += QString(" "); - - QString siteurl = url.toEncoded(); - sitestr = QString(" %2 ").arg(siteurl).arg(siteurl); - - ui->titleLabel->setText(urlstr); - }else - { - ui->titleLabel->setText(messageName()); - - } - - if (urlarray.isEmpty()) - { - ui->siteLabel->hide(); - } - - ui->siteLabel->setText(sitestr); - - if(mPost.mImage.mData != NULL) - { - QPixmap pixmap; - GxsIdDetails::loadPixmapFromData(mPost.mImage.mData, mPost.mImage.mSize, pixmap,GxsIdDetails::ORIGINAL); - // Wiping data - as its been passed to thumbnail. - - QPixmap scaledpixmap; - if(pixmap.width() > 800){ - QPixmap scaledpixmap = pixmap.scaledToWidth(800, Qt::SmoothTransformation); - ui->pictureLabel->setPixmap(scaledpixmap); - }else{ - ui->pictureLabel->setPixmap(pixmap); - } - } - else if (mPost.mImage.mData == NULL) - { + if(redacted) { + ui->commentButton->setDisabled(true); + ui->voteUpButton->setDisabled(true); + ui->voteDownButton->setDisabled(true); ui->picture_frame->hide(); - } - else - { - ui->picture_frame->show(); - } + ui->fromLabel->setId(mPost.mMeta.mAuthorId); + ui->titleLabel->setText(tr( "

The author of this message (with ID %1) is banned.").arg(QString::fromStdString(mPost.mMeta.mAuthorId.toStdString()))) ; + QDateTime qtime; + qtime.setTime_t(mPost.mMeta.mPublishTs); + QString timestamp = qtime.toString("hh:mm dd-MMM-yyyy"); + ui->dateLabel->setText(timestamp); + } else { + QPixmap sqpixmap2 = FilesDefs::getPixmapFromQtResourcePath(":/images/thumb-default.png"); + + mInFill = true; + int desired_height = 1.5*(ui->voteDownButton->height() + ui->voteUpButton->height() + ui->scoreLabel->height()); + int desired_width = sqpixmap2.width()*desired_height/(float)sqpixmap2.height(); + + QDateTime qtime; + qtime.setTime_t(mPost.mMeta.mPublishTs); + QString timestamp = qtime.toString("hh:mm dd-MMM-yyyy"); + QString timestamp2 = misc::timeRelativeToNow(mPost.mMeta.mPublishTs); + ui->dateLabel->setText(timestamp2); + ui->dateLabel->setToolTip(timestamp); + + ui->fromLabel->setId(mPost.mMeta.mAuthorId); + + // Use QUrl to check/parse our URL + // The only combination that seems to work: load as EncodedUrl, extract toEncoded(). + QByteArray urlarray(mPost.mLink.c_str()); + QUrl url = QUrl::fromEncoded(urlarray.trimmed()); + QString urlstr = "Invalid Link"; + QString sitestr = "Invalid Link"; + + bool urlOkay = url.isValid(); + if (urlOkay) + { + QString scheme = url.scheme(); + if ((scheme != "https") + && (scheme != "http") + && (scheme != "ftp") + && (scheme != "retroshare")) + { + urlOkay = false; + sitestr = "Invalid Link Scheme"; + } + } + + if (urlOkay) + { + urlstr = QString(" "); + urlstr += messageName(); + urlstr += QString(" "); + + QString siteurl = url.toEncoded(); + sitestr = QString(" %2 ").arg(siteurl).arg(siteurl); + + ui->titleLabel->setText(urlstr); + }else + { + ui->titleLabel->setText(messageName()); + + } + + if (urlarray.isEmpty()) + { + ui->siteLabel->hide(); + } + + ui->siteLabel->setText(sitestr); + + if(mPost.mImage.mData != NULL) + { + QPixmap pixmap; + GxsIdDetails::loadPixmapFromData(mPost.mImage.mData, mPost.mImage.mSize, pixmap,GxsIdDetails::ORIGINAL); + // Wiping data - as its been passed to thumbnail. + + QPixmap scaledpixmap; + if(pixmap.width() > 800){ + QPixmap scaledpixmap = pixmap.scaledToWidth(800, Qt::SmoothTransformation); + ui->pictureLabel->setPixmap(scaledpixmap); + }else{ + ui->pictureLabel->setPixmap(pixmap); + } + } + else if (mPost.mImage.mData == NULL) + { + ui->picture_frame->hide(); + } + else + { + ui->picture_frame->show(); + } + } //QString score = "Hot" + QString::number(post.mHotScore); //score += " Top" + QString::number(post.mTopScore); @@ -413,141 +364,4 @@ void PostedCardView::fill() emit sizeChanged(this); } - -const RsPostedPost &PostedCardView::getPost() const -{ - return mPost; -} - -RsPostedPost &PostedCardView::post() -{ - return mPost; -} - -QString PostedCardView::groupName() -{ - return QString::fromUtf8(mGroup.mMeta.mGroupName.c_str()); -} - -QString PostedCardView::messageName() -{ - return QString::fromUtf8(mPost.mMeta.mMsgName.c_str()); -} - -void PostedCardView::makeDownVote() -{ - RsGxsGrpMsgIdPair msgId; - msgId.first = mPost.mMeta.mGroupId; - msgId.second = mPost.mMeta.mMsgId; - - ui->voteUpButton->setEnabled(false); - ui->voteDownButton->setEnabled(false); - - emit vote(msgId, false); -} - -void PostedCardView::makeUpVote() -{ - RsGxsGrpMsgIdPair msgId; - msgId.first = mPost.mMeta.mGroupId; - msgId.second = mPost.mMeta.mMsgId; - - ui->voteUpButton->setEnabled(false); - ui->voteDownButton->setEnabled(false); - - emit vote(msgId, true); -} - -void PostedCardView::loadComments() -{ - std::cerr << "PostedCardView::loadComments()"; - std::cerr << std::endl; - - if (mFeedHolder) - { - QString title = QString::fromUtf8(mPost.mMeta.mMsgName.c_str()); - -#warning (csoler) Posted item versions not handled yet. When it is the case, start here. - - QVector post_versions ; - post_versions.push_back(mPost.mMeta.mMsgId) ; - - mFeedHolder->openComments(0, mPost.mMeta.mGroupId, post_versions,mPost.mMeta.mMsgId, title); - } -} - -void PostedCardView::setReadStatus(bool isNew, bool isUnread) -{ - if (isUnread) - { - ui->readButton->setChecked(true); - ui->readButton->setIcon(QIcon(":/images/message-state-unread.png")); - } - else - { - ui->readButton->setChecked(false); - ui->readButton->setIcon(QIcon(":/images/message-state-read.png")); - } - - ui->newLabel->setVisible(isNew); - - ui->mainFrame->setProperty("new", isNew); - ui->mainFrame->style()->unpolish(ui->mainFrame); - ui->mainFrame->style()->polish( ui->mainFrame); -} - -void PostedCardView::readToggled(bool checked) -{ - if (mInFill) { - return; - } - - RsGxsGrpMsgIdPair msgPair = std::make_pair(groupId(), messageId()); - - uint32_t token; - rsPosted->setMessageReadStatus(token, msgPair, !checked); - - setReadStatus(false, checked); -} - -void PostedCardView::readAndClearItem() -{ -#ifdef DEBUG_ITEM - std::cerr << "PostedCardView::readAndClearItem()"; - std::cerr << std::endl; -#endif - - readToggled(false); - removeItem(); -} - - -void PostedCardView::doExpand(bool open) -{ - /*if (open) - { - - } - else - { - - } - - emit sizeChanged(this);*/ - -} - -void PostedCardView::copyMessageLink() -{ - if (groupId().isNull() || mMessageId.isNull()) { - return; - } - - RetroShareLink link = RetroShareLink::createGxsMessageLink(RetroShareLink::TYPE_POSTED, groupId(), mMessageId, messageName()); - - if (link.valid()) { - QList urls; - urls.push_back(link); - RSLinkClipboard::copyLinks(urls); - } -} +void PostedCardView::toggleNotes() {} diff --git a/retroshare-gui/src/gui/Posted/PostedCardView.h b/retroshare-gui/src/gui/Posted/PostedCardView.h index 7fc303680..79c9c3fdb 100644 --- a/retroshare-gui/src/gui/Posted/PostedCardView.h +++ b/retroshare-gui/src/gui/Posted/PostedCardView.h @@ -24,7 +24,7 @@ #include #include -#include "gui/gxs/GxsFeedItem.h" +#include "PostedItem.h" namespace Ui { class PostedCardView; @@ -33,62 +33,30 @@ class PostedCardView; class FeedHolder; class RsPostedPost; -class PostedCardView : public GxsFeedItem +class PostedCardView : public BasePostedItem { Q_OBJECT public: - PostedCardView(FeedHolder *parent, uint32_t feedId, const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, bool isHome, bool autoUpdate); - PostedCardView(FeedHolder *parent, uint32_t feedId, const RsPostedGroup &group, const RsPostedPost &post, bool isHome, bool autoUpdate); - PostedCardView(FeedHolder *parent, uint32_t feedId, const RsPostedPost &post, bool isHome, bool autoUpdate); + PostedCardView(FeedHolder *parent, uint32_t feedId, const RsGxsGroupId& groupId, const RsGxsMessageId& messageId, bool isHome, bool autoUpdate); + PostedCardView(FeedHolder *parent, uint32_t feedId, const RsGroupMetaData& group_meta, const RsGxsMessageId& post_id, bool isHome, bool autoUpdate); virtual ~PostedCardView(); - bool setGroup(const RsPostedGroup& group, bool doFill = true); - bool setPost(const RsPostedPost& post, bool doFill = true); - - const RsPostedPost &getPost() const; - RsPostedPost &post(); - - uint64_t uniqueIdentifier() const override { return hash_64bits("PostedItem " + mMessageId.toStdString()); } - -protected: - /* FeedItem */ - virtual void doExpand(bool open); - -private slots: - void loadComments(); - void makeUpVote(); - void makeDownVote(); - void readToggled(bool checked); - void readAndClearItem(); - void copyMessageLink(); - -signals: - void vote(const RsGxsGrpMsgIdPair& msgId, bool up); - protected: /* GxsGroupFeedItem */ - virtual QString groupName(); - virtual void loadGroup(const uint32_t &token); - virtual RetroShareLink::enumType getLinkType() { return RetroShareLink::TYPE_UNKNOWN; } - /* GxsFeedItem */ - virtual QString messageName(); - virtual void loadMessage(const uint32_t &token); - virtual void loadComment(const uint32_t &token); + void setup() override; + void fill() override; + void doExpand(bool open) override {} + void setComment(const RsGxsComment&) override; + void setReadStatus(bool isNew, bool isUnread) override; + void toggle() override {} + void setCommentsSize(int comNb) override; + void makeUpVote() override; + void makeDownVote() override; + void toggleNotes() override; private: - void setup(); - void fill(); - void setReadStatus(bool isNew, bool isUnread); - -private: - bool mInFill; - - RsPostedGroup mGroup; - RsPostedPost mPost; - RsGxsMessageId mMessageId; - /** Qt Designer generated object */ Ui::PostedCardView *ui; }; diff --git a/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.cpp b/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.cpp index ddacd4895..a9edea275 100644 --- a/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.cpp +++ b/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.cpp @@ -22,11 +22,12 @@ #include #include #include +#include + #include "PostedCreatePostDialog.h" #include "ui_PostedCreatePostDialog.h" #include "util/misc.h" -#include "util/TokenQueue.h" #include "util/RichTextEdit.h" #include "gui/feeds/SubFileItem.h" #include "util/rsdir.h" @@ -35,14 +36,18 @@ #include #include - -#include - #include +#include +#include "gui/common/FilesDefs.h" -PostedCreatePostDialog::PostedCreatePostDialog(TokenQueue* tokenQ, RsPosted *posted, const RsGxsGroupId& grpId, QWidget *parent): +/* View Page */ +#define VIEW_POST 1 +#define VIEW_IMAGE 2 +#define VIEW_LINK 3 + +PostedCreatePostDialog::PostedCreatePostDialog(RsPosted *posted, const RsGxsGroupId& grpId, QWidget *parent): QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint), - mTokenQueue(tokenQ), mPosted(posted), mGrpId(grpId), + mPosted(posted), mGrpId(grpId), ui(new Ui::PostedCreatePostDialog) { ui->setupUi(this); @@ -52,7 +57,7 @@ PostedCreatePostDialog::PostedCreatePostDialog(TokenQueue* tokenQ, RsPosted *pos connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(close())); connect(ui->addPicButton, SIGNAL(clicked() ), this , SLOT(addPicture())); - ui->headerFrame->setHeaderImage(QPixmap(":/icons/png/postedlinks.png")); + ui->headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/postedlinks.png")); ui->headerFrame->setHeaderText(tr("Create a new Post")); setAttribute ( Qt::WA_DeleteOnClose, true ); @@ -67,6 +72,16 @@ PostedCreatePostDialog::PostedCreatePostDialog(TokenQueue* tokenQ, RsPosted *pos /* fill in the available OwnIds for signing */ ui->idChooser->loadIds(IDCHOOSER_ID_REQUIRED, RsGxsId()); + QSignalMapper *signalMapper = new QSignalMapper(this); + connect(ui->postButton, SIGNAL(clicked()), signalMapper, SLOT(map())); + connect(ui->imageButton, SIGNAL(clicked()), signalMapper, SLOT(map())); + connect(ui->linkButton, SIGNAL(clicked()), signalMapper, SLOT(map())); + + signalMapper->setMapping(ui->postButton, VIEW_POST); + signalMapper->setMapping(ui->imageButton, VIEW_IMAGE); + signalMapper->setMapping(ui->linkButton, VIEW_LINK); + connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(setPage(int))); + ui->removeButton->hide(); /* load settings */ @@ -93,11 +108,17 @@ void PostedCreatePostDialog::processSettings(bool load) // state of ID Chooser combobox int index = Settings->value("IDChooser", 0).toInt(); ui->idChooser->setCurrentIndex(index); + + // load last used Stacked Page + setPage(Settings->value("viewPage", VIEW_POST).toInt()); } else { // save settings // state of ID Chooser combobox Settings->setValue("IDChooser", ui->idChooser->currentIndex()); + + // store last used Page + Settings->setValue("viewPage", viewMode()); } Settings->endGroup(); @@ -155,7 +176,6 @@ void PostedCreatePostDialog::createPost() uint32_t token; mPosted->createPost(token, post); -// mTokenQueue->queueRequest(token, TOKENREQ_MSGINFO, RS_TOKREQ_ANSTYPE_ACK, TOKEN_USER_TYPE_POST); accept(); } @@ -222,19 +242,51 @@ void PostedCreatePostDialog::addPicture() } -void PostedCreatePostDialog::on_postButton_clicked() +int PostedCreatePostDialog::viewMode() { - ui->stackedWidget->setCurrentIndex(0); + if (ui->postButton->isChecked()) { + return VIEW_POST; + } else if (ui->imageButton->isChecked()) { + return VIEW_IMAGE; + } else if (ui->linkButton->isChecked()) { + return VIEW_LINK; + } + + /* Default */ + return VIEW_POST; } -void PostedCreatePostDialog::on_imageButton_clicked() +void PostedCreatePostDialog::setPage(int viewMode) { - ui->stackedWidget->setCurrentIndex(1); -} + switch (viewMode) { + case VIEW_POST: + ui->stackedWidget->setCurrentIndex(0); -void PostedCreatePostDialog::on_linkButton_clicked() -{ - ui->stackedWidget->setCurrentIndex(2); + ui->postButton->setChecked(true); + ui->imageButton->setChecked(false); + ui->linkButton->setChecked(false); + + break; + case VIEW_IMAGE: + ui->stackedWidget->setCurrentIndex(1); + + ui->imageButton->setChecked(true); + ui->postButton->setChecked(false); + ui->linkButton->setChecked(false); + + break; + case VIEW_LINK: + ui->stackedWidget->setCurrentIndex(2); + + ui->linkButton->setChecked(true); + ui->postButton->setChecked(false); + ui->imageButton->setChecked(false); + + break; + default: + setPage(VIEW_POST); + return; + } } void PostedCreatePostDialog::on_removeButton_clicked() diff --git a/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.h b/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.h index 5aed59950..64ab35566 100644 --- a/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.h +++ b/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.h @@ -26,8 +26,6 @@ #include "retroshare/rsposted.h" #include "util/RichTextEdit.h" -class TokenQueue; - namespace Ui { class PostedCreatePostDialog; } @@ -41,7 +39,7 @@ public: * @param tokenQ parent callee token * @param posted */ - explicit PostedCreatePostDialog(TokenQueue* tokenQ, RsPosted* posted, const RsGxsGroupId& grpId, QWidget *parent = 0); + explicit PostedCreatePostDialog(RsPosted* posted, const RsGxsGroupId& grpId, QWidget *parent = 0); ~PostedCreatePostDialog(); private: @@ -52,18 +50,16 @@ private: private slots: void createPost(); void addPicture(); - void on_postButton_clicked(); - void on_imageButton_clicked(); - void on_linkButton_clicked(); void on_removeButton_clicked(); void fileHashingFinished(QList hashedFiles); + void setPage(int viewMode); private: void processSettings(bool load); + int viewMode(); QString mLink; QString mNotes; - TokenQueue* mTokenQueue; RsPosted* mPosted; RsGxsGroupId mGrpId; diff --git a/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.ui b/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.ui index 50abd48a2..bd44c4d0a 100644 --- a/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.ui +++ b/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.ui @@ -176,8 +176,8 @@ - 24 - 24 + 32 + 32 @@ -246,36 +246,7 @@ 2 - - - - - 800 - 200 - - - - - - - true - - - - - - - Qt::Horizontal - - - - 188 - 17 - - - - - + Remove image @@ -295,6 +266,35 @@ + + + + Qt::Horizontal + + + + 188 + 17 + + + + + + + + + 800 + 200 + + + + + + + true + + + @@ -418,6 +418,9 @@ 24 + + true + @@ -435,6 +438,15 @@ 24 + + true + + + false + + + false + @@ -452,6 +464,12 @@ 24 + + true + + + true + diff --git a/retroshare-gui/src/gui/Posted/PostedDialog.cpp b/retroshare-gui/src/gui/Posted/PostedDialog.cpp index e696db36c..fa5826151 100644 --- a/retroshare-gui/src/gui/Posted/PostedDialog.cpp +++ b/retroshare-gui/src/gui/Posted/PostedDialog.cpp @@ -41,13 +41,14 @@ public: }; /** Constructor */ -PostedDialog::PostedDialog(QWidget *parent) - : GxsGroupFrameDialog(rsPosted, parent) +PostedDialog::PostedDialog(QWidget *parent): + GxsGroupFrameDialog(rsPosted, parent), mEventHandlerId(0) { - mEventHandlerId = 0; - // Needs to be asynced because this function is likely to be called by another thread! - - rsEvents->registerEventsHandler(RsEventType::GXS_POSTED, [this](std::shared_ptr event) { RsQThreadUtils::postToObject( [=]() { handleEvent_main_thread(event); }, this ); }, mEventHandlerId ); + // Needs to be asynced because this function is likely to be called by another thread! + rsEvents->registerEventsHandler( + [this](std::shared_ptr event) + { RsQThreadUtils::postToObject( [=]() { handleEvent_main_thread(event); }, this ); }, + mEventHandlerId, RsEventType::GXS_POSTED ); } void PostedDialog::handleEvent_main_thread(std::shared_ptr event) @@ -60,21 +61,20 @@ void PostedDialog::handleEvent_main_thread(std::shared_ptr event) switch(e->mPostedEventCode) { case RsPostedEventCode::NEW_MESSAGE: - updateMessageSummaryList(e->mPostedGroupId); - break; - case RsPostedEventCode::UPDATED_MESSAGE: // [[fallthrough]]; - updateDisplay(false); - break; - case RsPostedEventCode::READ_STATUS_CHANGED: // [[fallthrough]]; - updateMessageSummaryList(e->mPostedGroupId); + updateGroupStatisticsReal(e->mPostedGroupId); // update the list immediately break; case RsPostedEventCode::NEW_POSTED_GROUP: // [[fallthrough]]; case RsPostedEventCode::SUBSCRIBE_STATUS_CHANGED: // [[fallthrough]]; updateDisplay(true); break; + + case RsPostedEventCode::STATISTICS_CHANGED: + updateGroupStatistics(e->mPostedGroupId); + break; + default: break; } } @@ -88,18 +88,18 @@ PostedDialog::~PostedDialog() UserNotify *PostedDialog::createUserNotify(QObject *parent) { - return new PostedUserNotify(rsPosted, parent); + return new PostedUserNotify(rsPosted, this, parent); } QString PostedDialog::getHelpString() const { - QString hlp_str = tr("

  Posted

\ -

The posted service allows you to share internet links, that spread among Retroshare nodes like forums and \ + QString hlp_str = tr("

  Boards

\ +

The Boards service allows you to share images, blog posts & internet links, that spread among Retroshare nodes like forums and \ channels

\ -

Links can be commented by subscribed users. A promotion system also gives the opportunity to \ +

Posts can be commented by subscribed users. A promotion system also gives the opportunity to \ enlight important links.

\

There is no restriction on which links are shared. Be careful when clicking on them.

\ -

Posted links are kept for %1 days, and sync-ed over the last %2 days, unless you change this.

\ +

Boards are kept for %1 days, and sync-ed over the last %2 days, unless you change this.

\ ").arg(QString::number(rsPosted->getDefaultStoragePeriod()/86400)).arg(QString::number(rsPosted->getDefaultSyncPeriod()/86400)); return hlp_str ; @@ -109,9 +109,9 @@ QString PostedDialog::text(TextType type) { switch (type) { case TEXT_NAME: - return tr("Posted Links"); + return tr("Boards"); case TEXT_NEW: - return tr("Create Topic"); + return tr("Create Board"); case TEXT_TODO: return "Open points:
    " "
  • Subreddits/tag to posts support" @@ -120,13 +120,13 @@ QString PostedDialog::text(TextType type) "
"; case TEXT_YOUR_GROUP: - return tr("My Topics"); + return tr("My Boards"); case TEXT_SUBSCRIBED_GROUP: - return tr("Subscribed Topics"); + return tr("Subscribed Boards"); case TEXT_POPULAR_GROUP: - return tr("Popular Topics"); + return tr("Popular Boards"); case TEXT_OTHER_GROUP: - return tr("Other Topics"); + return tr("Other Boards"); } return ""; @@ -156,14 +156,36 @@ QString PostedDialog::icon(IconType type) return ""; } -GxsGroupDialog *PostedDialog::createNewGroupDialog(TokenQueue *tokenQueue) +bool PostedDialog::getGroupData(std::list& groupInfo) { - return new PostedGroupDialog(tokenQueue, this); + std::vector groups; + + // request all group infos at once + + if(! rsPosted->getBoardsInfo(std::list(),groups)) + return false; + + /* Save groups to fill icons and description */ + + for (auto& group: groups) + groupInfo.push_back(new RsPostedGroup(group)); + + return true; } -GxsGroupDialog *PostedDialog::createGroupDialog(TokenQueue *tokenQueue, RsTokenService *tokenService, GxsGroupDialog::Mode mode, RsGxsGroupId groupId) +bool PostedDialog::getGroupStatistics(const RsGxsGroupId& groupId,GxsGroupStatistic& stat) { - return new PostedGroupDialog(tokenQueue, tokenService, mode, groupId, this); + return rsPosted->getBoardStatistics(groupId,stat); +} + +GxsGroupDialog *PostedDialog::createNewGroupDialog() +{ + return new PostedGroupDialog(this); +} + +GxsGroupDialog *PostedDialog::createGroupDialog(GxsGroupDialog::Mode mode, RsGxsGroupId groupId) +{ + return new PostedGroupDialog(mode, groupId, this); } int PostedDialog::shareKeyType() @@ -186,6 +208,7 @@ QWidget *PostedDialog::createCommentHeaderWidget(const RsGxsGroupId &grpId, cons return new PostedItem(NULL, 0, grpId, msgId, true, false); } +#ifdef TO_REMOVE void PostedDialog::loadGroupSummaryToken(const uint32_t &token, std::list &groupInfo, RsUserdata *&userdata) { std::vector groups; @@ -211,25 +234,28 @@ void PostedDialog::loadGroupSummaryToken(const uint32_t &token, std::list(userdata); - if (!postedData) { - std::cerr << "PostedDialog::groupInfoToGroupItemInfo() Failed to cast data to PostedGroupInfoData"; - std::cerr << std::endl; + const RsPostedGroup *postedGroupData = dynamic_cast(groupData); + + if (!postedGroupData) + { + std::cerr << "PostedDialog::groupInfoToGroupItemInfo() Failed to cast data to RsPostedGroup"<< std::endl; return; } - QMap::const_iterator descriptionIt = postedData->mDescription.find(groupInfo.mGroupId); - if (descriptionIt != postedData->mDescription.end()) { - groupItemInfo.description = descriptionIt.value(); - } - - QMap::const_iterator iconIt = postedData->mIcon.find(groupInfo.mGroupId); - if (iconIt != postedData->mIcon.end()) { - groupItemInfo.icon = iconIt.value(); - } + if(postedGroupData->mGroupImage.mSize > 0) + { + QPixmap image; + GxsIdDetails::loadPixmapFromData(postedGroupData->mGroupImage.mData, postedGroupData->mGroupImage.mSize, image,GxsIdDetails::ORIGINAL); + groupItemInfo.icon = image; + } + else + groupItemInfo.icon = FilesDefs::getIconFromQtResourcePath(":icons/png/postedlinks.png"); + + groupItemInfo.description = QString::fromUtf8(postedGroupData->mDescription.c_str()); } diff --git a/retroshare-gui/src/gui/Posted/PostedDialog.h b/retroshare-gui/src/gui/Posted/PostedDialog.h index 7f23ee24f..5931ec25b 100644 --- a/retroshare-gui/src/gui/Posted/PostedDialog.h +++ b/retroshare-gui/src/gui/Posted/PostedDialog.h @@ -37,7 +37,7 @@ public: ~PostedDialog(); virtual QIcon iconPixmap() const { return QIcon(IMAGE_POSTED) ; } //MainPage - virtual QString pageName() const { return tr("Links") ; } //MainPage + virtual QString pageName() const { return tr("Boards") ; } //MainPage virtual QString helpText() const { return ""; } //MainPage protected: @@ -45,21 +45,23 @@ protected: virtual QString getHelpString() const ; virtual RetroShareLink::enumType getLinkType() { return RetroShareLink::TYPE_POSTED; } virtual GroupFrameSettings::Type groupFrameSettingsType() { return GroupFrameSettings::Posted; } - virtual void groupInfoToGroupItemInfo(const RsGroupMetaData &groupInfo, GroupItemInfo &groupItemInfo, const RsUserdata *userdata); + + void groupInfoToGroupItemInfo(const RsGxsGenericGroupData *groupData, GroupItemInfo &groupItemInfo) override; + bool getGroupData(std::list& groupInfo) override; + bool getGroupStatistics(const RsGxsGroupId& groupId,GxsGroupStatistic& stat) override; private: /* GxsGroupFrameDialog */ virtual QString text(TextType type); virtual QString icon(IconType type); virtual QString settingsGroupName() { return "PostedDialog"; } - virtual GxsGroupDialog *createNewGroupDialog(TokenQueue *tokenQueue); - virtual GxsGroupDialog *createGroupDialog(TokenQueue *tokenQueue, RsTokenService *tokenService, GxsGroupDialog::Mode mode, RsGxsGroupId groupId); + virtual GxsGroupDialog *createNewGroupDialog(); + virtual GxsGroupDialog *createGroupDialog(GxsGroupDialog::Mode mode, RsGxsGroupId groupId); virtual int shareKeyType(); virtual GxsMessageFrameWidget *createMessageFrameWidget(const RsGxsGroupId &groupId); virtual RsGxsCommentService *getCommentService(); virtual QWidget *createCommentHeaderWidget(const RsGxsGroupId &grpId, const RsGxsMessageId &msgId); virtual uint32_t requestGroupSummaryType() { return GXS_REQUEST_TYPE_GROUP_DATA; } // request complete group data - virtual void loadGroupSummaryToken(const uint32_t &token, std::list &groupInfo, RsUserdata* &userdata); void handleEvent_main_thread(std::shared_ptr event); RsEventsHandlerId_t mEventHandlerId; diff --git a/retroshare-gui/src/gui/Posted/PostedGroupDialog.cpp b/retroshare-gui/src/gui/Posted/PostedGroupDialog.cpp index 678c41a98..c516f7520 100644 --- a/retroshare-gui/src/gui/Posted/PostedGroupDialog.cpp +++ b/retroshare-gui/src/gui/Posted/PostedGroupDialog.cpp @@ -21,6 +21,7 @@ #include "PostedGroupDialog.h" #include "gui/gxs/GxsIdDetails.h" +#include "gui/common/FilesDefs.h" #include #include @@ -56,13 +57,13 @@ uint32_t PostedCreateDefaultsFlags = ( GXS_GROUP_DEFAULTS_DISTRIB_PUBLIC | uint32_t PostedEditEnabledFlags = PostedCreateEnabledFlags; uint32_t PostedEditDefaultsFlags = PostedCreateDefaultsFlags; -PostedGroupDialog::PostedGroupDialog(TokenQueue *tokenQueue, QWidget *parent) - : GxsGroupDialog(tokenQueue, PostedCreateEnabledFlags, PostedCreateDefaultsFlags, parent) +PostedGroupDialog::PostedGroupDialog(QWidget *parent) + : GxsGroupDialog(PostedCreateEnabledFlags, PostedCreateDefaultsFlags, parent) { } -PostedGroupDialog::PostedGroupDialog(TokenQueue *tokenExternalQueue, RsTokenService *tokenService, Mode mode, RsGxsGroupId groupId, QWidget *parent) - : GxsGroupDialog(tokenExternalQueue, tokenService, mode, groupId, PostedEditEnabledFlags, PostedEditDefaultsFlags, parent) +PostedGroupDialog::PostedGroupDialog(Mode mode, RsGxsGroupId groupId, QWidget *parent) + : GxsGroupDialog(mode, groupId, PostedEditEnabledFlags, PostedEditDefaultsFlags, parent) { } @@ -71,25 +72,25 @@ void PostedGroupDialog::initUi() switch (mode()) { case MODE_CREATE: - setUiText(UITYPE_SERVICE_HEADER, tr("Create New Topic")); + setUiText(UITYPE_SERVICE_HEADER, tr("Create New Board")); setUiText(UITYPE_BUTTONBOX_OK, tr("Create")); break; case MODE_SHOW: - setUiText(UITYPE_SERVICE_HEADER, tr("Posted Topic")); + setUiText(UITYPE_SERVICE_HEADER, tr("Board")); break; case MODE_EDIT: - setUiText(UITYPE_SERVICE_HEADER, tr("Edit Topic")); - setUiText(UITYPE_BUTTONBOX_OK, tr("Update Topic")); + setUiText(UITYPE_SERVICE_HEADER, tr("Edit Board")); + setUiText(UITYPE_BUTTONBOX_OK, tr("Update Board")); break; } - setUiText(UITYPE_ADD_ADMINS_CHECKBOX, tr("Add Topic Admins")); - setUiText(UITYPE_CONTACTS_DOCK, tr("Select Topic Admins")); + setUiText(UITYPE_ADD_ADMINS_CHECKBOX, tr("Add Board Admins")); + setUiText(UITYPE_CONTACTS_DOCK, tr("Select Board Admins")); } QPixmap PostedGroupDialog::serviceImage() { - return QPixmap(":/icons/png/posted.png"); + return FilesDefs::getPixmapFromQtResourcePath(":/icons/png/posted.png"); } void PostedGroupDialog::preparePostedGroup(RsPostedGroup &group, const RsGroupMetaData &meta) @@ -112,18 +113,19 @@ void PostedGroupDialog::preparePostedGroup(RsPostedGroup &group, const RsGroupMe } } -bool PostedGroupDialog::service_CreateGroup(uint32_t &token, const RsGroupMetaData &meta) +bool PostedGroupDialog::service_createGroup(RsGroupMetaData& meta) { // Specific Function. RsPostedGroup grp; preparePostedGroup(grp, meta); - rsPosted->createGroup(token, grp); + if(rsPosted->createBoard(grp)) + meta = grp.mMeta; return true; } -bool PostedGroupDialog::service_EditGroup(uint32_t &token, RsGroupMetaData &editedMeta) +bool PostedGroupDialog::service_updateGroup(const RsGroupMetaData& editedMeta) { RsPostedGroup grp; preparePostedGroup(grp, editedMeta); @@ -131,45 +133,48 @@ bool PostedGroupDialog::service_EditGroup(uint32_t &token, RsGroupMetaData &edit std::cerr << "PostedGroupDialog::service_EditGroup() submitting changes"; std::cerr << std::endl; - rsPosted->updateGroup(token, grp); - return true; + return rsPosted->editBoard(grp); } -bool PostedGroupDialog::service_loadGroup(uint32_t token, Mode /*mode*/, RsGroupMetaData& groupMetaData, QString &description) +bool PostedGroupDialog::service_loadGroup(const RsGxsGenericGroupData *data, Mode mode, QString& description) { - std::cerr << "PostedGroupDialog::service_loadGroup(" << token << ")"; - std::cerr << std::endl; + const RsPostedGroup *pgroup = dynamic_cast(data); - std::vector groups; - if (!rsPosted->getGroupData(token, groups)) + if(pgroup == nullptr) { - std::cerr << "PostedGroupDialog::service_loadGroup() Error getting GroupData"; - std::cerr << std::endl; - return false; - } - - if (groups.size() != 1) - { - std::cerr << "PostedGroupDialog::service_loadGroup() Error Group.size() != 1"; - std::cerr << std::endl; + std::cerr << "PostedGroupDialog::service_loadGroup() Error not a RsPostedGroup" << std::endl; return false; } std::cerr << "PostedGroupDialog::service_loadGroup() Unfinished Loading"; std::cerr << std::endl; - const RsPostedGroup &group = groups[0]; - groupMetaData = group.mMeta; + const RsPostedGroup& group = *pgroup; description = QString::fromUtf8(group.mDescription.c_str()); - if (group.mGroupImage.mData) { + if (group.mGroupImage.mData) + { QPixmap pixmap; if (GxsIdDetails::loadPixmapFromData(group.mGroupImage.mData, group.mGroupImage.mSize, pixmap,GxsIdDetails::ORIGINAL)) setLogo(pixmap); - } else { - setLogo(QPixmap(":/icons/png/posted.png")); } + else + setLogo(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/posted.png")); return true; } + +bool PostedGroupDialog::service_getGroupData(const RsGxsGroupId& grpId,RsGxsGenericGroupData *& data) +{ + std::vector boardsInfo ; + + if( rsPosted->getBoardsInfo(std::list({grpId}),boardsInfo) && boardsInfo.size() == 1) + { + data = new RsPostedGroup(boardsInfo[0]); + return true; + } + else + return false; + +} diff --git a/retroshare-gui/src/gui/Posted/PostedGroupDialog.h b/retroshare-gui/src/gui/Posted/PostedGroupDialog.h index c0d860b96..ce3d25a5d 100644 --- a/retroshare-gui/src/gui/Posted/PostedGroupDialog.h +++ b/retroshare-gui/src/gui/Posted/PostedGroupDialog.h @@ -30,15 +30,16 @@ class PostedGroupDialog : public GxsGroupDialog Q_OBJECT public: - PostedGroupDialog(TokenQueue *tokenQueue, QWidget *parent); - PostedGroupDialog(TokenQueue *tokenExternalQueue, RsTokenService *tokenService, Mode mode, RsGxsGroupId groupId, QWidget *parent); + PostedGroupDialog(QWidget *parent); + PostedGroupDialog(Mode mode, RsGxsGroupId groupId, QWidget *parent); protected: - virtual void initUi(); - virtual QPixmap serviceImage(); - virtual bool service_CreateGroup(uint32_t &token, const RsGroupMetaData &meta); - virtual bool service_loadGroup(uint32_t token, Mode mode, RsGroupMetaData& groupMetaData, QString &description); - virtual bool service_EditGroup(uint32_t &token, RsGroupMetaData &editedMeta); + void initUi() override; + QPixmap serviceImage() override; + bool service_createGroup(RsGroupMetaData& meta) override; + bool service_loadGroup(const RsGxsGenericGroupData *data,Mode mode, QString &description) override; + bool service_updateGroup(const RsGroupMetaData& editedMeta) override; + bool service_getGroupData(const RsGxsGroupId& grpId,RsGxsGenericGroupData *& data) override; private: void preparePostedGroup(RsPostedGroup &group, const RsGroupMetaData &meta); diff --git a/retroshare-gui/src/gui/Posted/PostedItem.cpp b/retroshare-gui/src/gui/Posted/PostedItem.cpp index b09452a8e..6f9bc1cc3 100644 --- a/retroshare-gui/src/gui/Posted/PostedItem.cpp +++ b/retroshare-gui/src/gui/Posted/PostedItem.cpp @@ -26,57 +26,356 @@ #include "rshare.h" #include "PostedItem.h" #include "gui/feeds/FeedHolder.h" +#include "gui/RetroShareLink.h" #include "gui/gxs/GxsIdDetails.h" #include "util/misc.h" +#include "gui/common/FilesDefs.h" +#include "util/qtthreadsutils.h" #include "util/HandleRichText.h" +#include "gui/MainWindow.h" +#include "gui/Identity/IdDialog.h" #include "PhotoView.h" #include "ui_PostedItem.h" #include + +#include #include #define LINK_IMAGE ":/images/thumb-link.png" /** Constructor */ -PostedItem::PostedItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, bool isHome, bool autoUpdate) : - GxsFeedItem(feedHolder, feedId, groupId, messageId, isHome, rsPosted, autoUpdate) -{ - setup(); +//======================================================================================== +// BasePostedItem // +//======================================================================================== - requestGroup(); - requestMessage(); - requestComment(); +BasePostedItem::BasePostedItem( FeedHolder *feedHolder, uint32_t feedId + , const RsGroupMetaData &group_meta, const RsGxsMessageId& post_id + , bool isHome, bool autoUpdate) + : GxsFeedItem(feedHolder, feedId, group_meta.mGroupId, post_id, isHome, rsPosted, autoUpdate) + , mInFill(false), mGroupMeta(group_meta) + , mLoaded(false), mIsLoadingGroup(false), mIsLoadingMessage(false), mIsLoadingComment(false) +{ + mPost.mMeta.mMsgId = post_id; + mPost.mMeta.mGroupId = mGroupMeta.mGroupId; } -PostedItem::PostedItem(FeedHolder *feedHolder, uint32_t feedId, const RsPostedGroup &group, const RsPostedPost &post, bool isHome, bool autoUpdate) : - GxsFeedItem(feedHolder, feedId, post.mMeta.mGroupId, post.mMeta.mMsgId, isHome, rsPosted, autoUpdate) +BasePostedItem::BasePostedItem( FeedHolder *feedHolder, uint32_t feedId + , const RsGxsGroupId &groupId, const RsGxsMessageId& post_id + , bool isHome, bool autoUpdate) + : GxsFeedItem(feedHolder, feedId, groupId, post_id, isHome, rsPosted, autoUpdate) + , mInFill(false) + , mLoaded(false), mIsLoadingGroup(false), mIsLoadingMessage(false), mIsLoadingComment(false) { - setup(); + mPost.mMeta.mMsgId = post_id; +} + +BasePostedItem::~BasePostedItem() +{ + auto timeout = std::chrono::steady_clock::now() + std::chrono::milliseconds(200); + while( (mIsLoadingGroup || mIsLoadingMessage || mIsLoadingComment) + && std::chrono::steady_clock::now() < timeout) + { + RsDbg() << __PRETTY_FUNCTION__ << " is Waiting " + << (mIsLoadingGroup ? "Group " : "") + << (mIsLoadingMessage ? "Message " : "") + << (mIsLoadingComment ? "Comment " : "") + << "loading finished." << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } +} + +void BasePostedItem::paintEvent(QPaintEvent *e) +{ + /* This method employs a trick to trigger a deferred loading. The post and group is requested only + * when actually displayed on the screen. */ + + if(!mLoaded) + { + mLoaded = true ; + + requestMessage(); + requestComment(); + } + + GxsFeedItem::paintEvent(e) ; +} + +bool BasePostedItem::setPost(const RsPostedPost &post, bool doFill) +{ + if (groupId() != post.mMeta.mGroupId || messageId() != post.mMeta.mMsgId) { + std::cerr << "BasePostedItem::setPost() - Wrong id, cannot set post"; + std::cerr << std::endl; + return false; + } + + mPost = post; + + if (doFill) + fill(); + + return true; +} + +void BasePostedItem::loadGroup() +{ + mIsLoadingGroup = true; + RsThread::async([this]() + { + // 1 - get group data + +#ifdef DEBUG_FORUMS + std::cerr << "Retrieving post data for post " << mThreadId << std::endl; +#endif + + std::vector groups; + const std::list groupIds = { groupId() }; + + if(!rsPosted->getBoardsInfo(groupIds,groups)) + { + RsErr() << "GxsPostedGroupItem::loadGroup() ERROR getting data" << std::endl; + mIsLoadingGroup = false; + return; + } + + if (groups.size() != 1) + { + std::cerr << "GxsPostedGroupItem::loadGroup() Wrong number of Items" << std::endl; + mIsLoadingGroup = false; + return; + } + RsPostedGroup group(groups[0]); + + RsQThreadUtils::postToObject( [group,this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ + + mGroupMeta = group.mMeta; + mIsLoadingGroup = false; + + }, this ); + }); +} + +void BasePostedItem::loadMessage() +{ + mIsLoadingMessage = true; + RsThread::async([this]() + { + // 1 - get group data + + std::vector posts; + std::vector comments; + std::vector votes; + + if(! rsPosted->getBoardContent( groupId(), std::set( { messageId() } ),posts,comments,votes)) + { + RsErr() << "BasePostedItem::loadMessage() ERROR getting data" << std::endl; + mIsLoadingMessage = false; + return; + } + + if (posts.size() == 1) + { + std::cerr << (void*)this << ": Obtained post, with msgId = " << posts[0].mMeta.mMsgId << std::endl; + const RsPostedPost& post(posts[0]); + + RsQThreadUtils::postToObject( [post,this]() { setPost(post,true); mIsLoadingMessage = false;}, this ); + } + else if(comments.size() == 1) + { + const RsGxsComment& cmt = comments[0]; + std::cerr << (void*)this << ": Obtained comment, setting messageId to threadID = " << cmt.mMeta.mThreadId << std::endl; + + RsQThreadUtils::postToObject( [cmt,this]() + { + setComment(cmt); + + //Change this item to be uploaded with thread element. + setMessageId(cmt.mMeta.mThreadId); + requestMessage(); + + mIsLoadingMessage = false; + }, this ); + + } + else + { + std::cerr << "GxsChannelPostItem::loadMessage() Wrong number of Items. Remove It." << std::endl; + + RsQThreadUtils::postToObject( [this]() { removeItem(); mIsLoadingMessage = false;}, this ); + } + }); +} + + +void BasePostedItem::loadComment() +{ +#ifdef DEBUG_ITEM + std::cerr << "GxsChannelPostItem::loadComment()"; + std::cerr << std::endl; +#endif + mIsLoadingComment = true; + RsThread::async([this]() + { + // 1 - get group data + + std::set msgIds; + + for(auto MsgId: messageVersions()) + msgIds.insert(MsgId); + + std::vector posts; + std::vector comments; + std::vector votes; + + if(! rsPosted->getBoardContent( groupId(),msgIds,posts,comments,votes)) + { + RsErr() << "BasePostedItem::loadGroup() ERROR getting data" << std::endl; + mIsLoadingComment = false; + return; + } + + int comNb = comments.size(); + + RsQThreadUtils::postToObject( [comNb,this]() + { + setCommentsSize(comNb); + mIsLoadingComment = false; + }, this ); + }); +} + +QString BasePostedItem::groupName() +{ + return QString::fromUtf8(mGroupMeta.mGroupName.c_str()); +} + +QString BasePostedItem::messageName() +{ + return QString::fromUtf8(mPost.mMeta.mMsgName.c_str()); +} + +void BasePostedItem::loadComments() +{ + std::cerr << "BasePostedItem::loadComments()"; + std::cerr << std::endl; + + if (mFeedHolder) + { + QString title = QString::fromUtf8(mPost.mMeta.mMsgName.c_str()); + +#warning (csoler) Posted item versions not handled yet. When it is the case, start here. + + QVector post_versions ; + post_versions.push_back(mPost.mMeta.mMsgId) ; + + mFeedHolder->openComments(0, mPost.mMeta.mGroupId, post_versions,mPost.mMeta.mMsgId, title); + } +} +void BasePostedItem::readToggled(bool checked) +{ + if (mInFill) { + return; + } + + RsGxsGrpMsgIdPair msgPair = std::make_pair(groupId(), messageId()); + + uint32_t token; + rsPosted->setMessageReadStatus(token, msgPair, !checked); + + setReadStatus(false, checked); +} + +void BasePostedItem::readAndClearItem() +{ +#ifdef DEBUG_ITEM + std::cerr << "BasePostedItem::readAndClearItem()"; + std::cerr << std::endl; +#endif + + readToggled(false); + removeItem(); +} +void BasePostedItem::copyMessageLink() +{ + if (groupId().isNull() || messageId().isNull()) { + return; + } + + RetroShareLink link = RetroShareLink::createGxsMessageLink(RetroShareLink::TYPE_POSTED, groupId(), messageId(), messageName()); + + if (link.valid()) { + QList urls; + urls.push_back(link); + RSLinkClipboard::copyLinks(urls); + } +} + +void BasePostedItem::viewPicture() +{ + if(mPost.mImage.mData == NULL) { + return; + } + + QString timestamp = misc::timeRelativeToNow(mPost.mMeta.mPublishTs); + QPixmap pixmap; + GxsIdDetails::loadPixmapFromData(mPost.mImage.mData, mPost.mImage.mSize, pixmap,GxsIdDetails::ORIGINAL); + RsGxsId authorID = mPost.mMeta.mAuthorId; - mMessageId = post.mMeta.mMsgId; + PhotoView *PView = new PhotoView(this); + + PView->setPixmap(pixmap); + PView->setTitle(messageName()); + PView->setName(authorID); + PView->setTime(timestamp); + PView->setGroupId(groupId()); + PView->setMessageId(messageId()); + PView->show(); - setGroup(group, false); - setPost(post); - requestComment(); + /* window will destroy itself! */ } -PostedItem::PostedItem(FeedHolder *feedHolder, uint32_t feedId, const RsPostedPost &post, bool isHome, bool autoUpdate) : - GxsFeedItem(feedHolder, feedId, post.mMeta.mGroupId, post.mMeta.mMsgId, isHome, rsPosted, autoUpdate) +void BasePostedItem::showAuthorInPeople() +{ + if(mPost.mMeta.mAuthorId.isNull()) + { + std::cerr << "(EE) GxsForumThreadWidget::loadMsgData_showAuthorInPeople() ERROR Missing Message Data..."; + std::cerr << std::endl; + } + + /* window will destroy itself! */ + IdDialog *idDialog = dynamic_cast(MainWindow::getPage(MainWindow::People)); + + if (!idDialog) + return ; + + MainWindow::showWindow(MainWindow::People); + idDialog->navigate(RsGxsId(mPost.mMeta.mAuthorId)); +} + +//======================================================================================== +// PostedItem // +//======================================================================================== + +PostedItem::PostedItem(FeedHolder *feedHolder, uint32_t feedId, const RsGroupMetaData &group_meta, const RsGxsMessageId& post_id, bool isHome, bool autoUpdate) : + BasePostedItem(feedHolder, feedId, group_meta, post_id, isHome, autoUpdate) { setup(); - - requestGroup(); - setPost(post); - requestComment(); } -PostedItem::~PostedItem() +PostedItem::PostedItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsGroupId &groupId, const RsGxsMessageId& post_id, bool isHome, bool autoUpdate) : + BasePostedItem(feedHolder, feedId, groupId, post_id, isHome, autoUpdate) { - delete(ui); + setup(); + loadGroup(); } + void PostedItem::setup() { /* Invoke the Qt Designer generated object setup routine */ @@ -115,9 +414,11 @@ void PostedItem::setup() QAction *CopyLinkAction = new QAction(QIcon(""),tr("Copy RetroShare Link"), this); connect(CopyLinkAction, SIGNAL(triggered()), this, SLOT(copyMessageLink())); + QAction *showInPeopleAct = new QAction(QIcon(), tr("Show author in people tab"), this); + connect(showInPeopleAct, SIGNAL(triggered()), this, SLOT(showAuthorInPeople())); int S = QFontMetricsF(font()).height() ; - + ui->voteUpButton->setIconSize(QSize(S*1.5,S*1.5)); ui->voteDownButton->setIconSize(QSize(S*1.5,S*1.5)); ui->commentButton->setIconSize(QSize(S*1.5,S*1.5)); @@ -125,224 +426,175 @@ void PostedItem::setup() ui->notesButton->setIconSize(QSize(S*1.5,S*1.5)); ui->readButton->setIconSize(QSize(S*1.5,S*1.5)); ui->shareButton->setIconSize(QSize(S*1.5,S*1.5)); - + QMenu *menu = new QMenu(); menu->addAction(CopyLinkAction); + menu->addSeparator(); + menu->addAction(showInPeopleAct); ui->shareButton->setMenu(menu); ui->clearButton->hide(); ui->readAndClearButton->hide(); + ui->nameLabel->hide(); } -bool PostedItem::setGroup(const RsPostedGroup &group, bool doFill) +void PostedItem::makeDownVote() { - if (groupId() != group.mMeta.mGroupId) { - std::cerr << "PostedItem::setGroup() - Wrong id, cannot set post"; - std::cerr << std::endl; - return false; - } + RsGxsGrpMsgIdPair msgId; + msgId.first = mPost.mMeta.mGroupId; + msgId.second = mPost.mMeta.mMsgId; - mGroup = group; + ui->voteUpButton->setEnabled(false); + ui->voteDownButton->setEnabled(false); - if (doFill) { - fill(); - } - - return true; + emit vote(msgId, false); } -bool PostedItem::setPost(const RsPostedPost &post, bool doFill) +void PostedItem::makeUpVote() { - if (groupId() != post.mMeta.mGroupId || messageId() != post.mMeta.mMsgId) { - std::cerr << "PostedItem::setPost() - Wrong id, cannot set post"; - std::cerr << std::endl; - return false; - } + RsGxsGrpMsgIdPair msgId; + msgId.first = mPost.mMeta.mGroupId; + msgId.second = mPost.mMeta.mMsgId; - mPost = post; + ui->voteUpButton->setEnabled(false); + ui->voteDownButton->setEnabled(false); - if (doFill) { - fill(); - } - - return true; + emit vote(msgId, true); } -void PostedItem::loadGroup(const uint32_t &token) +void PostedItem::setComment(const RsGxsComment& cmt) { - std::vector groups; - if (!rsPosted->getGroupData(token, groups)) - { - std::cerr << "PostedItem::loadGroup() ERROR getting data"; - std::cerr << std::endl; - return; - } - - if (groups.size() != 1) - { - std::cerr << "PostedItem::loadGroup() Wrong number of Items"; - std::cerr << std::endl; - return; - } - - setGroup(groups[0]); + ui->newCommentLabel->show(); + ui->commLabel->show(); + ui->commLabel->setText(QString::fromUtf8(cmt.mComment.c_str())); } - -void PostedItem::loadMessage(const uint32_t &token) +void PostedItem::setCommentsSize(int comNb) { - std::vector posts; - std::vector cmts; - if (!rsPosted->getPostData(token, posts, cmts)) - { - std::cerr << "GxsChannelPostItem::loadMessage() ERROR getting data"; - std::cerr << std::endl; - return; - } - - if (posts.size() == 1) - { - setPost(posts[0]); - } - else if (cmts.size() == 1) - { - RsGxsComment cmt = cmts[0]; - - ui->newCommentLabel->show(); - ui->commLabel->show(); - ui->commLabel->setText(QString::fromUtf8(cmt.mComment.c_str())); - - //Change this item to be uploaded with thread element. - setMessageId(cmt.mMeta.mThreadId); - requestMessage(); - } - else - { - std::cerr << "GxsChannelPostItem::loadMessage() Wrong number of Items. Remove It."; - std::cerr << std::endl; - removeItem(); - return; - } -} - -void PostedItem::loadComment(const uint32_t &token) -{ - std::vector cmts; - if (!rsPosted->getRelatedComments(token, cmts)) - { - std::cerr << "GxsChannelPostItem::loadComment() ERROR getting data"; - std::cerr << std::endl; - return; - } - - size_t comNb = cmts.size(); QString sComButText = tr("Comment"); - if (comNb == 1) { + if (comNb == 1) sComButText = sComButText.append("(1)"); - } else if (comNb > 1) { - sComButText = " " + tr("Comments").append(" (%1)").arg(comNb); - } + else if(comNb > 1) + sComButText = tr("Comments ").append("(%1)").arg(comNb); + ui->commentButton->setText(sComButText); } void PostedItem::fill() { - if (isLoading()) { - /* Wait for all requests */ - return; - } + RsReputationLevel overall_reputation = rsReputations->overallReputationLevel(mPost.mMeta.mAuthorId); + bool redacted = (overall_reputation == RsReputationLevel::LOCALLY_NEGATIVE); - QPixmap sqpixmap2 = QPixmap(":/images/thumb-default.png"); + if(redacted) { + ui->expandButton->setDisabled(true); + ui->commentButton->setDisabled(true); + ui->voteUpButton->setDisabled(true); + ui->voteDownButton->setDisabled(true); - mInFill = true; - int desired_height = 1.5*(ui->voteDownButton->height() + ui->voteUpButton->height() + ui->scoreLabel->height()); - int desired_width = sqpixmap2.width()*desired_height/(float)sqpixmap2.height(); + ui->thumbnailLabel->setPixmap( FilesDefs::getPixmapFromQtResourcePath(":/images/thumb-default.png")); + ui->fromLabel->setId(mPost.mMeta.mAuthorId); + ui->titleLabel->setText(tr( "

The author of this message (with ID %1) is banned.").arg(QString::fromStdString(mPost.mMeta.mAuthorId.toStdString()))) ; + QDateTime qtime; + qtime.setTime_t(mPost.mMeta.mPublishTs); + QString timestamp = qtime.toString("hh:mm dd-MMM-yyyy"); + ui->dateLabel->setText(timestamp); + } else { + RetroShareLink link = RetroShareLink::createGxsGroupLink(RetroShareLink::TYPE_POSTED, mGroupMeta.mGroupId, groupName()); + ui->nameLabel->setText(link.toHtml()); - QDateTime qtime; - qtime.setTime_t(mPost.mMeta.mPublishTs); - QString timestamp = qtime.toString("hh:mm dd-MMM-yyyy"); - QString timestamp2 = misc::timeRelativeToNow(mPost.mMeta.mPublishTs); - ui->dateLabel->setText(timestamp2); - ui->dateLabel->setToolTip(timestamp); + QPixmap sqpixmap2 = FilesDefs::getPixmapFromQtResourcePath(":/images/thumb-default.png"); - ui->fromLabel->setId(mPost.mMeta.mAuthorId); + mInFill = true; + int desired_height = 1.5*(ui->voteDownButton->height() + ui->voteUpButton->height() + ui->scoreLabel->height()); + int desired_width = sqpixmap2.width()*desired_height/(float)sqpixmap2.height(); - // Use QUrl to check/parse our URL - // The only combination that seems to work: load as EncodedUrl, extract toEncoded(). - QByteArray urlarray(mPost.mLink.c_str()); - QUrl url = QUrl::fromEncoded(urlarray.trimmed()); - QString urlstr = "Invalid Link"; - QString sitestr = "Invalid Link"; - bool urlOkay = url.isValid(); - if (urlOkay) - { - QString scheme = url.scheme(); - if ((scheme != "https") - && (scheme != "http") - && (scheme != "ftp") - && (scheme != "retroshare")) + QDateTime qtime; + qtime.setTime_t(mPost.mMeta.mPublishTs); + QString timestamp = qtime.toString("hh:mm dd-MMM-yyyy"); + QString timestamp2 = misc::timeRelativeToNow(mPost.mMeta.mPublishTs); + ui->dateLabel->setText(timestamp2); + ui->dateLabel->setToolTip(timestamp); + + ui->fromLabel->setId(mPost.mMeta.mAuthorId); + + // Use QUrl to check/parse our URL + // The only combination that seems to work: load as EncodedUrl, extract toEncoded(). + QByteArray urlarray(mPost.mLink.c_str()); + QUrl url = QUrl::fromEncoded(urlarray.trimmed()); + QString urlstr = "Invalid Link"; + QString sitestr = "Invalid Link"; + + bool urlOkay = url.isValid(); + if (urlOkay) { - urlOkay = false; - sitestr = "Invalid Link Scheme"; + QString scheme = url.scheme(); + if ((scheme != "https") + && (scheme != "http") + && (scheme != "ftp") + && (scheme != "retroshare")) + { + urlOkay = false; + sitestr = "Invalid Link Scheme"; + } } - } - - if (urlOkay) - { - urlstr = QString(" "); - urlstr += messageName(); - urlstr += QString(" "); - QString siteurl = url.toEncoded(); - sitestr = QString(" %2 ").arg(siteurl).arg(siteurl); - - ui->titleLabel->setText(urlstr); - }else - { - ui->titleLabel->setText(messageName()); + if (urlOkay) + { + urlstr = QString(" "); + urlstr += messageName(); + urlstr += QString(" "); - } + QString siteurl = url.toEncoded(); + sitestr = QString(" %2 ").arg(siteurl).arg(siteurl); - if (urlarray.isEmpty()) - { - ui->siteLabel->hide(); - } + ui->titleLabel->setText(urlstr); + }else + { + ui->titleLabel->setText(messageName()); - ui->siteLabel->setText(sitestr); - - if(mPost.mImage.mData != NULL) - { - QPixmap pixmap; - GxsIdDetails::loadPixmapFromData(mPost.mImage.mData, mPost.mImage.mSize, pixmap,GxsIdDetails::ORIGINAL); - // Wiping data - as its been passed to thumbnail. - - QPixmap sqpixmap = pixmap.scaled(desired_width,desired_height, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); - ui->thumbnailLabel->setPixmap(sqpixmap); - ui->thumbnailLabel->setToolTip(tr("Click to view Picture")); - - QPixmap scaledpixmap; - if(pixmap.width() > 800){ - QPixmap scaledpixmap = pixmap.scaledToWidth(800, Qt::SmoothTransformation); - ui->pictureLabel->setPixmap(scaledpixmap); - }else{ - ui->pictureLabel->setPixmap(pixmap); } - } - else if (urlOkay && (mPost.mImage.mData == NULL)) - { - ui->expandButton->setDisabled(true); - ui->thumbnailLabel->setPixmap(QPixmap(LINK_IMAGE)); - } - else - { - ui->expandButton->setDisabled(true); - ui->thumbnailLabel->setPixmap(sqpixmap2); + + if (urlarray.isEmpty()) + { + ui->siteLabel->hide(); + } + + ui->siteLabel->setText(sitestr); + + if(mPost.mImage.mData != NULL) + { + QPixmap pixmap; + GxsIdDetails::loadPixmapFromData(mPost.mImage.mData, mPost.mImage.mSize, pixmap,GxsIdDetails::ORIGINAL); + // Wiping data - as its been passed to thumbnail. + + QPixmap sqpixmap = pixmap.scaled(desired_width,desired_height, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); + ui->thumbnailLabel->setPixmap(sqpixmap); + ui->thumbnailLabel->setToolTip(tr("Click to view Picture")); + + QPixmap scaledpixmap; + if(pixmap.width() > 800){ + QPixmap scaledpixmap = pixmap.scaledToWidth(800, Qt::SmoothTransformation); + ui->pictureLabel->setPixmap(scaledpixmap); + }else{ + ui->pictureLabel->setPixmap(pixmap); + } + } + else if (urlOkay && (mPost.mImage.mData == NULL)) + { + ui->expandButton->setDisabled(true); + ui->thumbnailLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(LINK_IMAGE)); + } + else + { + ui->expandButton->setDisabled(true); + ui->thumbnailLabel->setPixmap(sqpixmap2); + } } //QString score = "Hot" + QString::number(post.mHotScore); - //score += " Top" + QString::number(post.mTopScore); + //score += " Top" + QString::number(post.mTopScore); //score += " New" + QString::number(post.mNewScore); QString score = QString::number(mPost.mTopScore); @@ -354,7 +606,7 @@ void PostedItem::fill() QTextDocument doc; doc.setHtml(ui->notes->text()); - + if(doc.toPlainText().trimmed().isEmpty()) ui->notesButton->hide(); // differences between Feed or Top of Comment. @@ -392,11 +644,13 @@ void PostedItem::fill() { ui->clearButton->hide(); ui->readAndClearButton->hide(); + ui->nameLabel->hide(); } else { ui->clearButton->show(); ui->readAndClearButton->show(); + ui->nameLabel->show(); } // disable voting buttons - if they have already voted. @@ -408,7 +662,7 @@ void PostedItem::fill() #if 0 uint32_t up, down, nComments; - + bool ok = rsPosted->retrieveScores(mPost.mMeta.mServiceString, up, down, nComments); if(ok) @@ -429,79 +683,18 @@ void PostedItem::fill() emit sizeChanged(this); } -const RsPostedPost &PostedItem::getPost() const -{ - return mPost; -} - -RsPostedPost &PostedItem::post() -{ - return mPost; -} - -QString PostedItem::groupName() -{ - return QString::fromUtf8(mGroup.mMeta.mGroupName.c_str()); -} - -QString PostedItem::messageName() -{ - return QString::fromUtf8(mPost.mMeta.mMsgName.c_str()); -} - -void PostedItem::makeDownVote() -{ - RsGxsGrpMsgIdPair msgId; - msgId.first = mPost.mMeta.mGroupId; - msgId.second = mPost.mMeta.mMsgId; - - ui->voteUpButton->setEnabled(false); - ui->voteDownButton->setEnabled(false); - - emit vote(msgId, false); -} - -void PostedItem::makeUpVote() -{ - RsGxsGrpMsgIdPair msgId; - msgId.first = mPost.mMeta.mGroupId; - msgId.second = mPost.mMeta.mMsgId; - - ui->voteUpButton->setEnabled(false); - ui->voteDownButton->setEnabled(false); - - emit vote(msgId, true); -} - -void PostedItem::loadComments() -{ - std::cerr << "PostedItem::loadComments()"; - std::cerr << std::endl; - - if (mFeedHolder) - { - QString title = QString::fromUtf8(mPost.mMeta.mMsgName.c_str()); - -#warning (csoler) Posted item versions not handled yet. When it is the case, start here. - - QVector post_versions ; - post_versions.push_back(mPost.mMeta.mMsgId) ; - - mFeedHolder->openComments(0, mPost.mMeta.mGroupId, post_versions,mPost.mMeta.mMsgId, title); - } -} void PostedItem::setReadStatus(bool isNew, bool isUnread) { if (isUnread) { ui->readButton->setChecked(true); - ui->readButton->setIcon(QIcon(":/images/message-state-unread.png")); + ui->readButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/message-state-unread.png")); } else { ui->readButton->setChecked(false); - ui->readButton->setIcon(QIcon(":/images/message-state-read.png")); + ui->readButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/message-state-read.png")); } ui->newLabel->setVisible(isNew); @@ -511,30 +704,6 @@ void PostedItem::setReadStatus(bool isNew, bool isUnread) ui->mainFrame->style()->polish( ui->mainFrame); } -void PostedItem::readToggled(bool checked) -{ - if (mInFill) { - return; - } - - RsGxsGrpMsgIdPair msgPair = std::make_pair(groupId(), messageId()); - - uint32_t token; - rsPosted->setMessageReadStatus(token, msgPair, !checked); - - setReadStatus(false, checked); -} - -void PostedItem::readAndClearItem() -{ -#ifdef DEBUG_ITEM - std::cerr << "PostedItem::readAndClearItem()"; - std::cerr << std::endl; -#endif - - readToggled(false); - removeItem(); -} void PostedItem::toggle() { @@ -546,13 +715,13 @@ void PostedItem::doExpand(bool open) if (open) { ui->frame_picture->show(); - ui->expandButton->setIcon(QIcon(QString(":/images/decrease.png"))); + ui->expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/images/decrease.png"))); ui->expandButton->setToolTip(tr("Hide")); } else { ui->frame_picture->hide(); - ui->expandButton->setIcon(QIcon(QString(":/images/expand.png"))); + ui->expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/images/expand.png"))); ui->expandButton->setToolTip(tr("Expand")); } @@ -560,21 +729,6 @@ void PostedItem::doExpand(bool open) } -void PostedItem::copyMessageLink() -{ - if (groupId().isNull() || mMessageId.isNull()) { - return; - } - - RetroShareLink link = RetroShareLink::createGxsMessageLink(RetroShareLink::TYPE_POSTED, groupId(), mMessageId, messageName()); - - if (link.valid()) { - QList urls; - urls.push_back(link); - RSLinkClipboard::copyLinks(urls); - } -} - void PostedItem::toggleNotes() { if (ui->notesButton->isChecked()) @@ -582,33 +736,8 @@ void PostedItem::toggleNotes() ui->frame_notes->show(); } else - { + { ui->frame_notes->hide(); } } - -void PostedItem::viewPicture() -{ - if(mPost.mImage.mData == NULL) { - return; - } - - QString timestamp = misc::timeRelativeToNow(mPost.mMeta.mPublishTs); - QPixmap pixmap; - GxsIdDetails::loadPixmapFromData(mPost.mImage.mData, mPost.mImage.mSize, pixmap,GxsIdDetails::ORIGINAL); - RsGxsId authorID = mPost.mMeta.mAuthorId; - - PhotoView *PView = new PhotoView(this); - - PView->setPixmap(pixmap); - PView->setTitle(messageName()); - PView->setName(authorID); - PView->setTime(timestamp); - PView->setGroupId(groupId()); - PView->setMessageId(mMessageId); - - PView->show(); - - /* window will destroy itself! */ -} diff --git a/retroshare-gui/src/gui/Posted/PostedItem.h b/retroshare-gui/src/gui/Posted/PostedItem.h index 97b6974d0..d726cf830 100644 --- a/retroshare-gui/src/gui/Posted/PostedItem.h +++ b/retroshare-gui/src/gui/Posted/PostedItem.h @@ -31,66 +31,94 @@ class PostedItem; } class FeedHolder; -class RsPostedPost; +struct RsPostedPost; -class PostedItem : public GxsFeedItem +class BasePostedItem : public GxsFeedItem { Q_OBJECT public: - PostedItem(FeedHolder *parent, uint32_t feedId, const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, bool isHome, bool autoUpdate); - PostedItem(FeedHolder *parent, uint32_t feedId, const RsPostedGroup &group, const RsPostedPost &post, bool isHome, bool autoUpdate); - PostedItem(FeedHolder *parent, uint32_t feedId, const RsPostedPost &post, bool isHome, bool autoUpdate); - virtual ~PostedItem(); + BasePostedItem(FeedHolder *parent, uint32_t feedId, const RsGxsGroupId& groupId, const RsGxsMessageId& messageId, bool isHome, bool autoUpdate); + BasePostedItem(FeedHolder *parent, uint32_t feedId, const RsGroupMetaData& group_meta, const RsGxsMessageId& post_id, bool isHome, bool autoUpdate); + virtual ~BasePostedItem(); - bool setGroup(const RsPostedGroup& group, bool doFill = true); bool setPost(const RsPostedPost& post, bool doFill = true); - const RsPostedPost &getPost() const; - RsPostedPost &post(); + const RsPostedPost& getPost() const { return mPost ; } + RsPostedPost& getPost() { return mPost ; } uint64_t uniqueIdentifier() const override { return hash_64bits("PostedItem " + messageId().toStdString()); } -protected: - /* FeedItem */ - virtual void doExpand(bool open); private slots: void loadComments(); - void makeUpVote(); - void makeDownVote(); void readToggled(bool checked); void readAndClearItem(); - void toggle() override; void copyMessageLink(); - void toggleNotes(); void viewPicture(); + void showAuthorInPeople(); signals: void vote(const RsGxsGrpMsgIdPair& msgId, bool up); protected: + /* FeedItem */ + virtual void paintEvent(QPaintEvent *) override; + /* GxsGroupFeedItem */ - virtual QString groupName(); - virtual void loadGroup(const uint32_t &token); - virtual RetroShareLink::enumType getLinkType() { return RetroShareLink::TYPE_UNKNOWN; } + virtual QString groupName() override; + virtual void loadGroup() override; + virtual RetroShareLink::enumType getLinkType() override { return RetroShareLink::TYPE_UNKNOWN; } /* GxsFeedItem */ - virtual QString messageName(); - virtual void loadMessage(const uint32_t &token); - virtual void loadComment(const uint32_t &token); + virtual QString messageName() override; -private: - void setup(); - void fill(); - void setReadStatus(bool isNew, bool isUnread); + virtual void loadMessage() override; + virtual void loadComment() override; -private: bool mInFill; - - RsPostedGroup mGroup; + RsGroupMetaData mGroupMeta; RsPostedPost mPost; - RsGxsMessageId mMessageId; + virtual void setup()=0; + virtual void fill()=0; + virtual void doExpand(bool open) override =0; + virtual void setComment(const RsGxsComment&)=0; + virtual void setReadStatus(bool isNew, bool isUnread)=0; + virtual void setCommentsSize(int comNb)=0; + virtual void makeUpVote()=0; + virtual void makeDownVote()=0; + virtual void toggleNotes()=0; + +private: + bool mLoaded; + bool mIsLoadingGroup; + bool mIsLoadingMessage; + bool mIsLoadingComment; +}; + +class PostedItem: public BasePostedItem +{ + Q_OBJECT + +public: + PostedItem(FeedHolder *parent, uint32_t feedId, const RsGxsGroupId& groupId, const RsGxsMessageId& messageId, bool isHome, bool autoUpdate); + PostedItem(FeedHolder *parent, uint32_t feedId, const RsGroupMetaData& group_meta, const RsGxsMessageId& post_id, bool isHome, bool autoUpdate); + +protected: + void setup() override; + void fill() override; + void setComment(const RsGxsComment&) override; + void setReadStatus(bool isNew, bool isUnread) override; + void setCommentsSize(int comNb) override; + +private slots: + void doExpand(bool open); + void toggle(); + void makeUpVote(); + void makeDownVote(); + void toggleNotes() ; + +private: /** Qt Designer generated object */ Ui::PostedItem *ui; }; diff --git a/retroshare-gui/src/gui/Posted/PostedItem.ui b/retroshare-gui/src/gui/Posted/PostedItem.ui index f470fea00..f7c69008f 100644 --- a/retroshare-gui/src/gui/Posted/PostedItem.ui +++ b/retroshare-gui/src/gui/Posted/PostedItem.ui @@ -320,6 +320,23 @@ 6 + + + + + 75 + true + true + + + + Name + + + true + + + diff --git a/retroshare-gui/src/gui/Posted/PostedListWidget.cpp b/retroshare-gui/src/gui/Posted/PostedListWidget.cpp index 0d7960804..a88a1d6b5 100644 --- a/retroshare-gui/src/gui/Posted/PostedListWidget.cpp +++ b/retroshare-gui/src/gui/Posted/PostedListWidget.cpp @@ -27,8 +27,10 @@ #include "util/qtthreadsutils.h" #include "gui/gxs/GxsIdDetails.h" #include "PostedCreatePostDialog.h" +#include "gui/settings/rsharesettings.h" #include "PostedItem.h" #include "PostedCardView.h" +#include "gui/common/FilesDefs.h" #include "gui/common/UIStateHelper.h" #include "gui/RetroShareLink.h" #include "util/HandleRichText.h" @@ -41,9 +43,9 @@ #define POSTED_DEFAULT_LISTING_LENGTH 10 #define POSTED_MAX_INDEX 10000 -#define DEBUG_POSTED_LIST_WIDGET +//#define DEBUG_POSTED_LIST_WIDGET -#define TOPIC_DEFAULT_IMAGE ":/icons/png/posted.png" +#define BOARD_DEFAULT_IMAGE ":/icons/png/posted.png" /* View mode */ #define VIEW_MODE_CLASSIC 1 @@ -105,9 +107,17 @@ PostedListWidget::PostedListWidget(const RsGxsGroupId &postedId, QWidget *parent /* load settings */ processSettings(true); + mEventHandlerId = 0; + // Needs to be asynced because this function is called by another thread! + rsEvents->registerEventsHandler( + [this](std::shared_ptr event) + { RsQThreadUtils::postToObject( [=]() { handleEvent_main_thread(event); }, this ); }, + mEventHandlerId, RsEventType::GXS_POSTED ); + /* Initialize GUI */ setGroupId(postedId); } + PostedListWidget::~PostedListWidget() { // save settings @@ -116,20 +126,63 @@ PostedListWidget::~PostedListWidget() delete(ui); } +void PostedListWidget::handleEvent_main_thread(std::shared_ptr event) +{ + const RsGxsPostedEvent *e = dynamic_cast(event.get()); + + if(!e) + return; + + switch(e->mPostedEventCode) + { + case RsPostedEventCode::UPDATED_POSTED_GROUP: + case RsPostedEventCode::NEW_POSTED_GROUP: + case RsPostedEventCode::UPDATED_MESSAGE: + case RsPostedEventCode::NEW_MESSAGE: + if(e->mPostedGroupId == groupId()) + updateDisplay(true); + break; + default: + break; + } +} + + void PostedListWidget::processSettings(bool load) { Settings->beginGroup(QString("PostedListWidget")); - if (load) { + if (load) + { // load settings - /* View mode */ setViewMode(Settings->value("viewMode", VIEW_MODE_CLASSIC).toInt()); - } else { + + RsGxsId voteId(Settings->value("defaultIdentity",QString()).toString().toStdString()); + + if(!voteId.isNull()) + ui->idChooser->setChosenId(voteId); + + } + else + { // save settings /* View mode */ Settings->setValue("viewMode", viewMode()); + + RsGxsId voteId; + + switch (ui->idChooser->getChosenId(voteId)) + { + case GxsIdChooser::KnowId: + case GxsIdChooser::UnKnowId: Settings->setValue("defaultIdentity",QString::fromStdString(voteId.toStdString())); + break; + default: + case GxsIdChooser::NoId: + case GxsIdChooser::None: + break; + } } Settings->endGroup(); @@ -200,7 +253,7 @@ void PostedListWidget::newPost() return; } - PostedCreatePostDialog *cp = new PostedCreatePostDialog(mTokenQueue, rsPosted, groupId(), this); + PostedCreatePostDialog *cp = new PostedCreatePostDialog(rsPosted, groupId(), this); cp->show(); /* window will destroy itself! */ @@ -283,10 +336,10 @@ void PostedListWidget::submitVote(const RsGxsGrpMsgIdPair &msgId, bool up) std::cerr << "PostedListWidget::createPost() ERROR GETTING AuthorId!, Vote Failed"; std::cerr << std::endl; - QMessageBox::warning(this, tr("RetroShare"),tr("Please create or choose a Signing Id before Voting"), QMessageBox::Ok, QMessageBox::Ok); + QMessageBox::warning(this, tr("RetroShare"),tr("Please create or choose a default identity to use for voting"), QMessageBox::Ok, QMessageBox::Ok); return; - }//switch (ui.idChooser->getChosenId(authorId)) + } RsGxsVote vote; @@ -295,11 +348,10 @@ void PostedListWidget::submitVote(const RsGxsGrpMsgIdPair &msgId, bool up) vote.mMeta.mParentId = msgId.second; vote.mMeta.mAuthorId = authorId; - if (up) { + if (up) vote.mVoteType = GXS_VOTE_UP; - } else { //if (up) + else vote.mVoteType = GXS_VOTE_DOWN; - }//if (up) #ifdef DEBUG_POSTED_LIST_WIDGET std::cerr << "PostedListWidget::submitVote()"; @@ -351,13 +403,13 @@ void PostedListWidget::insertPostedDetails(const RsPostedGroup &group) ui->subscribeToolButton->setHidden(IS_GROUP_SUBSCRIBED(group.mMeta.mSubscribeFlags)) ; /* IMAGE */ - QPixmap topicImage; + QPixmap boardImage; if (group.mGroupImage.mData != NULL) { - GxsIdDetails::loadPixmapFromData(group.mGroupImage.mData, group.mGroupImage.mSize, topicImage,GxsIdDetails::ORIGINAL); + GxsIdDetails::loadPixmapFromData(group.mGroupImage.mData, group.mGroupImage.mSize, boardImage,GxsIdDetails::ORIGINAL); } else { - topicImage = QPixmap(TOPIC_DEFAULT_IMAGE); + boardImage = FilesDefs::getPixmapFromQtResourcePath(BOARD_DEFAULT_IMAGE); } - ui->logoLabel->setPixmap(topicImage); + ui->logoLabel->setPixmap(boardImage); ui->namelabel->setText(QString::fromUtf8(group.mMeta.mGroupName.c_str())); ui->poplabel->setText(QString::number( group.mMeta.mPop)); @@ -430,26 +482,22 @@ void PostedListWidget::insertPostedDetails(const RsPostedGroup &group) /*********************** **** **** **** ***********************/ /*********************** **** **** **** ***********************/ -void PostedListWidget::loadPost(const RsPostedPost &post) +void PostedListWidget::loadPost(const RsPostedPost& post) { /* Group is not always available because of the TokenQueue */ - RsPostedGroup dummyGroup; - dummyGroup.mMeta.mGroupId = groupId(); - PostedItem *item = new PostedItem(this, 0, dummyGroup, post, true, false); + PostedItem *item = new PostedItem(this, 0, mGroup.mMeta, post.mMeta.mMsgId, true, false); connect(item, SIGNAL(vote(RsGxsGrpMsgIdPair,bool)), this, SLOT(submitVote(RsGxsGrpMsgIdPair,bool))); mPosts.insert(post.mMeta.mMsgId, item); mPostItems.push_back(item); } -void PostedListWidget::loadPostCardView(const RsPostedPost &post) +void PostedListWidget::loadPostCardView(const RsPostedPost& post) { /* Group is not always available because of the TokenQueue */ - RsPostedGroup dummyGroup; - dummyGroup.mMeta.mGroupId = groupId(); - PostedCardView *cvitem = new PostedCardView(this, 0, dummyGroup, post, true, false); + PostedCardView *cvitem = new PostedCardView(this, 0, mGroup.mMeta, post.mMeta.mMsgId, true, false); connect(cvitem, SIGNAL(vote(RsGxsGrpMsgIdPair,bool)), this, SLOT(submitVote(RsGxsGrpMsgIdPair,bool))); mCVPosts.insert(post.mMeta.mMsgId, cvitem); @@ -665,35 +713,45 @@ void PostedListWidget::applyRanking() foreach (PostedCardView *item, mPostCardView) { +#ifdef DEBUG_POSTED_LIST_WIDGET std::cerr << "PostedListWidget::applyRanking() Item: " << item; std::cerr << std::endl; +#endif if (item->getPost().mMeta.mPublishTs < min_ts) { +#ifdef DEBUG_POSTED_LIST_WIDGET std::cerr << "\t Skipping OLD"; std::cerr << std::endl; +#endif item->hide(); continue; } if (counter >= mPostIndex + mPostShow) { +#ifdef DEBUG_POSTED_LIST_WIDGET std::cerr << "\t END - Counter too high"; std::cerr << std::endl; +#endif item->hide(); } else if (counter >= mPostIndex) { +#ifdef DEBUG_POSTED_LIST_WIDGET std::cerr << "\t Adding to Layout"; std::cerr << std::endl; +#endif /* add it in! */ cviewlayout->addWidget(item); item->show(); } else { +#ifdef DEBUG_POSTED_LIST_WIDGET std::cerr << "\t Skipping to Low"; std::cerr << std::endl; +#endif item->hide(); } ++counter; @@ -826,30 +884,17 @@ void PostedListWidget::shallowClearPosts() } } -bool PostedListWidget::insertGroupData(const uint32_t &token, RsGroupMetaData &metaData) +bool PostedListWidget::insertGroupData(const RsGxsGenericGroupData *data) { - std::vector groups; - rsPosted->getGroupData(token, groups); - - if (groups.size() == 1) - { - insertPostedDetails(groups[0]); - metaData = groups[0].mMeta; - return true; - } - - return false; + insertPostedDetails(*dynamic_cast(data)); + return true; } -void PostedListWidget::insertAllPosts(const uint32_t &token, GxsMessageFramePostThread */*thread*/) +void PostedListWidget::insertAllPostedPosts(const std::vector& posts, GxsMessageFramePostThread */*thread*/) { - std::vector posts; - rsPosted->getPostData(token, posts); - - std::vector::iterator vit; - for(vit = posts.begin(); vit != posts.end(); ++vit) + for(auto vit = posts.begin(); vit != posts.end(); ++vit) { - RsPostedPost& p = *vit; + const RsPostedPost& p = *vit; loadPost(p); loadPostCardView(p); } @@ -857,15 +902,11 @@ void PostedListWidget::insertAllPosts(const uint32_t &token, GxsMessageFramePost applyRanking(); } -void PostedListWidget::insertPosts(const uint32_t &token) +void PostedListWidget::insertPostedPosts(const std::vector& posts) { - std::vector posts; - rsPosted->getPostData(token, posts); - - std::vector::iterator vit; - for(vit = posts.begin(); vit != posts.end(); ++vit) + for(auto vit = posts.begin(); vit != posts.end(); ++vit) { - RsPostedPost& p = *vit; + const RsPostedPost& p = *vit; // modify post content if(mPosts.find(p.mMeta.mMsgId) != mPosts.end()) @@ -884,7 +925,7 @@ void PostedListWidget::insertPosts(const uint32_t &token) std::cerr << std::endl; #endif /* insert new entry */ - loadPost(p); + //loadPost(p); loadPostCardView(p); } } @@ -893,7 +934,7 @@ void PostedListWidget::insertPosts(const uint32_t &token) QMap::iterator pit; for(pit = mPosts.begin(); pit != mPosts.end(); ++pit) { - (*pit)->post().calculateScores(now); + (*pit)->getPost().calculateScores(now); } applyRanking(); @@ -988,3 +1029,65 @@ void PostedListWidget::setViewMode(int viewMode) return; } } + +void PostedListWidget::getMsgData(const std::set& msgIds,std::vector& psts) +{ + std::vector posts; + std::vector comments; + std::vector votes; + + rsPosted->getBoardContent( groupId(),msgIds,posts,comments,votes ); + + psts.clear(); + + for(auto& post: posts) + psts.push_back(new RsPostedPost(post)); +} + +void PostedListWidget::getAllMsgData(std::vector& psts) +{ + std::vector posts; + std::vector comments; + std::vector votes; + + rsPosted->getBoardAllContent( groupId(),posts,comments,votes ); + + psts.clear(); + + for(auto& post: posts) + psts.push_back(new RsPostedPost(post)); +} + +bool PostedListWidget::getGroupData(RsGxsGenericGroupData*& data) +{ + std::vector groupInfo ; + data = nullptr; + + if(! rsPosted->getBoardsInfo(std::list({groupId()}),groupInfo) || groupInfo.size() != 1) + return false; + + mGroup = groupInfo[0]; + data = new RsPostedGroup(mGroup); + return true; +} + +void PostedListWidget::insertPosts(const std::vector& posts) +{ + std::vector cposts; + + for(auto post: posts) // This is not so nice but we have somehow to convert to RsGxsChannelPost at some timer, and the cposts list is being modified in the insert method. + cposts.push_back(*dynamic_cast(post)); + + insertPostedPosts(cposts); +} + +void PostedListWidget::insertAllPosts(const std::vector& posts, GxsMessageFramePostThread *thread) +{ + std::vector cposts; + + for(auto post: posts) // This is not so nice but we have somehow to convert to RsGxsChannelPost at some timer, and the cposts list is being modified in the insert method. + cposts.push_back(*dynamic_cast(post)); + + insertAllPostedPosts(cposts, NULL); +} + diff --git a/retroshare-gui/src/gui/Posted/PostedListWidget.h b/retroshare-gui/src/gui/Posted/PostedListWidget.h index fa9f25c8e..a9892d47c 100644 --- a/retroshare-gui/src/gui/Posted/PostedListWidget.h +++ b/retroshare-gui/src/gui/Posted/PostedListWidget.h @@ -23,6 +23,7 @@ #include +#include "retroshare/rsposted.h" #include "gui/gxs/GxsMessageFramePostWidget.h" #include "gui/feeds/FeedHolder.h" @@ -57,14 +58,23 @@ public: virtual void loadRequest(const TokenQueue *queue, const TokenRequest &req); protected: + void handleEvent_main_thread(std::shared_ptr event); + void insertAllPostedPosts(const std::vector& posts, GxsMessageFramePostThread *thread) ; + void insertPostedPosts(const std::vector& posts); + /* GxsMessageFramePostWidget */ - virtual bool insertGroupData(const uint32_t &token, RsGroupMetaData &metaData); - virtual void insertAllPosts(const uint32_t &token, GxsMessageFramePostThread *thread); - virtual void insertPosts(const uint32_t &token); virtual void clearPosts(); virtual void blank(); virtual bool navigatePostItem(const RsGxsMessageId& msgId); + virtual void getMsgData(const std::set& msgIds,std::vector& posts) override; + virtual void getAllMsgData(std::vector& posts) override; + virtual bool getGroupData(RsGxsGenericGroupData*& data) override; + + virtual bool insertGroupData(const RsGxsGenericGroupData *data) override; + virtual void insertPosts(const std::vector& posts) override; + virtual void insertAllPosts(const std::vector& posts, GxsMessageFramePostThread *thread) override; + /* GxsMessageFrameWidget */ virtual void setAllMessagesReadDo(bool read, uint32_t &token); @@ -118,6 +128,7 @@ private: uint32_t mTokenTypeVote; + RsPostedGroup mGroup; QMap mPosts; QList mPostItems; @@ -126,6 +137,7 @@ private: /* UI - from Designer */ Ui::PostedListWidget *ui; + RsEventsHandlerId_t mEventHandlerId ; }; #endif diff --git a/retroshare-gui/src/gui/Posted/PostedListWidget.ui b/retroshare-gui/src/gui/Posted/PostedListWidget.ui index 304a79c76..3fa7069e6 100644 --- a/retroshare-gui/src/gui/Posted/PostedListWidget.ui +++ b/retroshare-gui/src/gui/Posted/PostedListWidget.ui @@ -6,8 +6,8 @@ 0 0 - 616 - 595 + 950 + 771 @@ -89,7 +89,7 @@ - :/images/posted_24.png:/images/posted_24.png + :/images/write.png:/images/write.png @@ -223,8 +223,8 @@ - - :/images/arrow-left.png:/images/arrow-left.png + + :/icons/png/arrow-left.png:/icons/png/arrow-left.png @@ -244,13 +244,17 @@ - - :/images/arrow-right.png:/images/arrow-right.png + + :/icons/png/arrow-right.png:/icons/png/arrow-right.png - + + + <html><head/><body><p>Default identity used when voting</p></body></html> + + @@ -285,7 +289,7 @@ - Topic Details + Board Details false @@ -294,199 +298,10 @@ false - - - - - - - 64 - 64 - - - - - 64 - 64 - - - - :/icons/png/postedlinks.png - - - true - - - - - - - - 14 - - - - TextLabel - - - - - - - - - 6 - - - - - - 75 - true - - - - Popularity - - - - - - - - 75 - true - - - - 0 - - - - - - - - 0 - 0 - - - - - 75 - true - - - - Posts - - - - - - - - 75 - true - - - - 0 - - - - - - - - 75 - true - - - - Created - - - - - - - unknown - - - - - - - - 75 - true - - - - Administrator: - - - - - - - unknown - - - true - - - - - - - - 75 - true - - - - Distribution: - - - - - - - unknown - - - - - - - - 0 - 0 - - - - - 75 - true - - - - Last Post: - - - - - - - unknown - - - - - - + + 6 + + Qt::Horizontal @@ -499,7 +314,7 @@ - + @@ -522,6 +337,194 @@ p, li { white-space: pre-wrap; } + + + + 0 + + + + + + 75 + true + + + + Popularity + + + + + + + + 75 + true + + + + 0 + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Posts + + + + + + + + 75 + true + + + + 0 + + + + + + + + 75 + true + + + + Created + + + + + + + unknown + + + + + + + + 75 + true + + + + Administrator: + + + + + + + unknown + + + true + + + + + + + + 75 + true + + + + Distribution: + + + + + + + unknown + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Last Post: + + + + + + + unknown + + + + + + + + 64 + 64 + + + + + 64 + 64 + + + + :/icons/png/postedlinks.png + + + true + + + + + + + + 14 + + + + TextLabel + + + + + @@ -557,7 +560,7 @@ p, li { white-space: pre-wrap; } 0 0 - 614 + 98 16 @@ -613,7 +616,7 @@ p, li { white-space: pre-wrap; } 0 0 - 614 + 948 16 @@ -667,9 +670,9 @@ p, li { white-space: pre-wrap; } - + diff --git a/retroshare-gui/src/gui/Posted/PostedUserNotify.cpp b/retroshare-gui/src/gui/Posted/PostedUserNotify.cpp index ef0cfa31d..bbc58e436 100644 --- a/retroshare-gui/src/gui/Posted/PostedUserNotify.cpp +++ b/retroshare-gui/src/gui/Posted/PostedUserNotify.cpp @@ -18,30 +18,32 @@ * * *******************************************************************************/ +#include "retroshare/rsposted.h" #include "PostedUserNotify.h" #include "gui/MainWindow.h" +#include "gui/common/FilesDefs.h" -PostedUserNotify::PostedUserNotify(RsGxsIfaceHelper *ifaceImpl, QObject *parent) : - GxsUserNotify(ifaceImpl, parent) +PostedUserNotify::PostedUserNotify(RsGxsIfaceHelper *ifaceImpl, const GxsGroupFrameDialog *g, QObject *parent) : + GxsUserNotify(ifaceImpl, g, parent) { } bool PostedUserNotify::hasSetting(QString *name, QString *group) { - if (name) *name = tr("Posted"); - if (group) *group = "Posted"; + if (name) *name = tr("Board Post"); + if (group) *group = "Board"; return true; } QIcon PostedUserNotify::getIcon() { - return QIcon(":/icons/png/posted.png"); + return FilesDefs::getIconFromQtResourcePath(":/icons/png/posted.png"); } QIcon PostedUserNotify::getMainIcon(bool hasNew) { - return hasNew ? QIcon(":/icons/png/posted-notify.png") : QIcon(":/icons/png/posted.png"); + return hasNew ? FilesDefs::getIconFromQtResourcePath(":/icons/png/posted-notify.png") : FilesDefs::getIconFromQtResourcePath(":/icons/png/posted.png"); } void PostedUserNotify::iconClicked() diff --git a/retroshare-gui/src/gui/Posted/PostedUserNotify.h b/retroshare-gui/src/gui/Posted/PostedUserNotify.h index 11552669a..2541a9d39 100644 --- a/retroshare-gui/src/gui/Posted/PostedUserNotify.h +++ b/retroshare-gui/src/gui/Posted/PostedUserNotify.h @@ -28,9 +28,10 @@ class PostedUserNotify : public GxsUserNotify Q_OBJECT public: - PostedUserNotify(RsGxsIfaceHelper *ifaceImpl, QObject *parent = 0); + PostedUserNotify(RsGxsIfaceHelper *ifaceImpl, const GxsGroupFrameDialog *g, QObject *parent = 0); virtual bool hasSetting(QString *name, QString *group); + virtual QString textInfo() const override { return tr("new board post(s)"); } private: virtual QIcon getIcon(); diff --git a/retroshare-gui/src/gui/Posted/Posted_images.qrc b/retroshare-gui/src/gui/Posted/Posted_images.qrc index 9c2be0db4..0710a04e6 100644 --- a/retroshare-gui/src/gui/Posted/Posted_images.qrc +++ b/retroshare-gui/src/gui/Posted/Posted_images.qrc @@ -1,16 +1,5 @@ - images/posted_16.png - images/posted_24.png - images/posted_32.png - images/posted_48.png - images/posted_64.png - images/posted_add_24.png - images/posted_add_32.png - images/posted_add_64.png - images/hot_24.png - images/new_24.png - images/posted_32_new.png images/expand.png images/decrease.png images/down-arrow.png @@ -29,5 +18,6 @@ images/down-hover.png images/up-hover.png images/trashcan.png + images/write.png diff --git a/retroshare-gui/src/gui/Posted/images/hot_128.png b/retroshare-gui/src/gui/Posted/images/hot_128.png deleted file mode 100644 index 652e2b5d4..000000000 Binary files a/retroshare-gui/src/gui/Posted/images/hot_128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/Posted/images/hot_24.png b/retroshare-gui/src/gui/Posted/images/hot_24.png deleted file mode 100644 index cf939b9b3..000000000 Binary files a/retroshare-gui/src/gui/Posted/images/hot_24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/Posted/images/hot_256.png b/retroshare-gui/src/gui/Posted/images/hot_256.png deleted file mode 100644 index 018b87708..000000000 Binary files a/retroshare-gui/src/gui/Posted/images/hot_256.png and /dev/null differ diff --git a/retroshare-gui/src/gui/Posted/images/hot_32.png b/retroshare-gui/src/gui/Posted/images/hot_32.png deleted file mode 100644 index 59e9c3702..000000000 Binary files a/retroshare-gui/src/gui/Posted/images/hot_32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/Posted/images/hot_48.png b/retroshare-gui/src/gui/Posted/images/hot_48.png deleted file mode 100644 index 164570180..000000000 Binary files a/retroshare-gui/src/gui/Posted/images/hot_48.png and /dev/null differ diff --git a/retroshare-gui/src/gui/Posted/images/hot_64.png b/retroshare-gui/src/gui/Posted/images/hot_64.png deleted file mode 100644 index 9f9ccc31e..000000000 Binary files a/retroshare-gui/src/gui/Posted/images/hot_64.png and /dev/null differ diff --git a/retroshare-gui/src/gui/Posted/images/new_128.png b/retroshare-gui/src/gui/Posted/images/new_128.png deleted file mode 100644 index 86827c30b..000000000 Binary files a/retroshare-gui/src/gui/Posted/images/new_128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/Posted/images/new_24.png b/retroshare-gui/src/gui/Posted/images/new_24.png deleted file mode 100644 index 93cafc107..000000000 Binary files a/retroshare-gui/src/gui/Posted/images/new_24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/Posted/images/new_32.png b/retroshare-gui/src/gui/Posted/images/new_32.png deleted file mode 100644 index d932b6957..000000000 Binary files a/retroshare-gui/src/gui/Posted/images/new_32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/Posted/images/new_48.png b/retroshare-gui/src/gui/Posted/images/new_48.png deleted file mode 100644 index fad00e9de..000000000 Binary files a/retroshare-gui/src/gui/Posted/images/new_48.png and /dev/null differ diff --git a/retroshare-gui/src/gui/Posted/images/new_64.png b/retroshare-gui/src/gui/Posted/images/new_64.png deleted file mode 100644 index 2325bf8fa..000000000 Binary files a/retroshare-gui/src/gui/Posted/images/new_64.png and /dev/null differ diff --git a/retroshare-gui/src/gui/Posted/images/posted_128.png b/retroshare-gui/src/gui/Posted/images/posted_128.png deleted file mode 100644 index 178143057..000000000 Binary files a/retroshare-gui/src/gui/Posted/images/posted_128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/Posted/images/posted_16.png b/retroshare-gui/src/gui/Posted/images/posted_16.png deleted file mode 100644 index 6c07665a4..000000000 Binary files a/retroshare-gui/src/gui/Posted/images/posted_16.png and /dev/null differ diff --git a/retroshare-gui/src/gui/Posted/images/posted_24.png b/retroshare-gui/src/gui/Posted/images/posted_24.png deleted file mode 100644 index 8de05ae32..000000000 Binary files a/retroshare-gui/src/gui/Posted/images/posted_24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/Posted/images/posted_256.png b/retroshare-gui/src/gui/Posted/images/posted_256.png deleted file mode 100644 index 901f51100..000000000 Binary files a/retroshare-gui/src/gui/Posted/images/posted_256.png and /dev/null differ diff --git a/retroshare-gui/src/gui/Posted/images/posted_32.png b/retroshare-gui/src/gui/Posted/images/posted_32.png deleted file mode 100644 index 36ef018c3..000000000 Binary files a/retroshare-gui/src/gui/Posted/images/posted_32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/Posted/images/posted_32_new.png b/retroshare-gui/src/gui/Posted/images/posted_32_new.png deleted file mode 100644 index 773e5dd27..000000000 Binary files a/retroshare-gui/src/gui/Posted/images/posted_32_new.png and /dev/null differ diff --git a/retroshare-gui/src/gui/Posted/images/posted_48.png b/retroshare-gui/src/gui/Posted/images/posted_48.png deleted file mode 100644 index b71b1cff3..000000000 Binary files a/retroshare-gui/src/gui/Posted/images/posted_48.png and /dev/null differ diff --git a/retroshare-gui/src/gui/Posted/images/posted_64.png b/retroshare-gui/src/gui/Posted/images/posted_64.png deleted file mode 100644 index fbfaf5e58..000000000 Binary files a/retroshare-gui/src/gui/Posted/images/posted_64.png and /dev/null differ diff --git a/retroshare-gui/src/gui/Posted/images/posted_add_128.png b/retroshare-gui/src/gui/Posted/images/posted_add_128.png deleted file mode 100644 index 40da463d1..000000000 Binary files a/retroshare-gui/src/gui/Posted/images/posted_add_128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/Posted/images/posted_add_24.png b/retroshare-gui/src/gui/Posted/images/posted_add_24.png deleted file mode 100644 index 87ecdde14..000000000 Binary files a/retroshare-gui/src/gui/Posted/images/posted_add_24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/Posted/images/posted_add_256.png b/retroshare-gui/src/gui/Posted/images/posted_add_256.png deleted file mode 100644 index e6ab57407..000000000 Binary files a/retroshare-gui/src/gui/Posted/images/posted_add_256.png and /dev/null differ diff --git a/retroshare-gui/src/gui/Posted/images/posted_add_32.png b/retroshare-gui/src/gui/Posted/images/posted_add_32.png deleted file mode 100644 index 996452344..000000000 Binary files a/retroshare-gui/src/gui/Posted/images/posted_add_32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/Posted/images/posted_add_48.png b/retroshare-gui/src/gui/Posted/images/posted_add_48.png deleted file mode 100644 index 0c2418a53..000000000 Binary files a/retroshare-gui/src/gui/Posted/images/posted_add_48.png and /dev/null differ diff --git a/retroshare-gui/src/gui/Posted/images/posted_add_64.png b/retroshare-gui/src/gui/Posted/images/posted_add_64.png deleted file mode 100644 index d87421a52..000000000 Binary files a/retroshare-gui/src/gui/Posted/images/posted_add_64.png and /dev/null differ diff --git a/retroshare-gui/src/gui/Posted/images/write.png b/retroshare-gui/src/gui/Posted/images/write.png new file mode 100644 index 000000000..f5702bfbd Binary files /dev/null and b/retroshare-gui/src/gui/Posted/images/write.png differ diff --git a/retroshare-gui/src/gui/QuickStartWizard.cpp b/retroshare-gui/src/gui/QuickStartWizard.cpp index d7ba25941..f442d2f1b 100644 --- a/retroshare-gui/src/gui/QuickStartWizard.cpp +++ b/retroshare-gui/src/gui/QuickStartWizard.cpp @@ -33,13 +33,14 @@ #include #include "settings/rsharesettings.h" #include "util/QtVersion.h" +#include "gui/common/FilesDefs.h" QuickStartWizard::QuickStartWizard(QWidget *parent) : QDialog(parent) { ui.setupUi(this); - ui.headerFrame->setHeaderImage(QPixmap(":/images/rs_wizard.png")); + ui.headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/images/rs_wizard.png")); ui.headerFrame->setHeaderText("RetroShare"); ui.pagesWizard->setCurrentIndex(0); diff --git a/retroshare-gui/src/gui/RemoteDirModel.cpp b/retroshare-gui/src/gui/RemoteDirModel.cpp index 974195746..49d9b2d6e 100644 --- a/retroshare-gui/src/gui/RemoteDirModel.cpp +++ b/retroshare-gui/src/gui/RemoteDirModel.cpp @@ -20,7 +20,8 @@ #include "RemoteDirModel.h" -#include "RsAutoUpdatePage.h" +#include + #include "gui/common/FilesDefs.h" #include "gui/common/GroupDefs.h" #include "gui/common/RsCollection.h" @@ -100,11 +101,9 @@ static bool isNewerThanEpoque(uint32_t ts) void RetroshareDirModel::treeStyle() { - categoryIcon.addPixmap(QPixmap(":/images/folder16.png"), - QIcon::Normal, QIcon::Off); - categoryIcon.addPixmap(QPixmap(":/images/folder_video.png"), - QIcon::Normal, QIcon::On); - peerIcon = QIcon(":/images/user/identity16.png"); + categoryIcon.addPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/folder.png"), QIcon::Normal, QIcon::Off); + categoryIcon.addPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/folderopen.png"), QIcon::Normal, QIcon::On); + peerIcon = FilesDefs::getIconFromQtResourcePath(":/images/user/identity16.png"); } void TreeStyle_RDM::update() { @@ -337,31 +336,32 @@ QString RetroshareDirModel::getAgeIndicatorString(const DirDetails &details) con const QIcon& RetroshareDirModel::getFlagsIcon(FileStorageFlags flags) { static QIcon *static_icons[8] = {NULL}; + if(static_icons[0] == NULL) + static_icons[0] = new QIcon(); int n=0; if(flags & DIR_FLAGS_ANONYMOUS_DOWNLOAD) n += 1 ; if(flags & DIR_FLAGS_ANONYMOUS_SEARCH ) n += 2 ; if(flags & DIR_FLAGS_BROWSABLE ) n += 4 ; - n-= 1; if(static_icons[n] == NULL) { QList icons ; if(flags & DIR_FLAGS_ANONYMOUS_SEARCH) - icons.push_back(QIcon(":icons/search_red_128.png")) ; + icons.push_back(FilesDefs::getIconFromQtResourcePath(":icons/search_red_128.png")) ; else - icons.push_back(QIcon(":icons/void_128.png")) ; + icons.push_back(FilesDefs::getIconFromQtResourcePath(":icons/void_128.png")) ; if(flags & DIR_FLAGS_ANONYMOUS_DOWNLOAD) - icons.push_back(QIcon(":icons/anonymous_blue_128.png")) ; + icons.push_back(FilesDefs::getIconFromQtResourcePath(":icons/anonymous_blue_128.png")) ; else - icons.push_back(QIcon(":icons/void_128.png")) ; + icons.push_back(FilesDefs::getIconFromQtResourcePath(":icons/void_128.png")) ; if(flags & DIR_FLAGS_BROWSABLE) - icons.push_back(QIcon(":icons/browsable_green_128.png")) ; + icons.push_back(FilesDefs::getIconFromQtResourcePath(":icons/browsable_green_128.png")) ; else - icons.push_back(QIcon(":icons/void_128.png")) ; + icons.push_back(FilesDefs::getIconFromQtResourcePath(":icons/void_128.png")) ; QPixmap pix ; GxsIdDetails::GenerateCombinedPixmap(pix, icons, 128); @@ -399,13 +399,13 @@ QVariant RetroshareDirModel::decorationRole(const DirDetails& details,int coln) time_t now = time(NULL) ; if(ageIndicator != IND_ALWAYS && now > details.max_mtime + ageIndicator) - return QIcon(":/images/folder_grey.png"); + return FilesDefs::getIconFromQtResourcePath(":/images/folder_grey.png"); else if (ageIndicator == IND_LAST_DAY ) - return QIcon(":/images/folder_green.png"); + return FilesDefs::getIconFromQtResourcePath(":/images/folder_green.png"); else if (ageIndicator == IND_LAST_WEEK ) - return QIcon(":/images/folder_yellow.png"); + return FilesDefs::getIconFromQtResourcePath(":/images/folder_yellow.png"); else if (ageIndicator == IND_LAST_MONTH ) - return QIcon(":/images/folder_red.png"); + return FilesDefs::getIconFromQtResourcePath(":/images/folder_red.png"); else return (QIcon(peerIcon)); } @@ -414,13 +414,13 @@ QVariant RetroshareDirModel::decorationRole(const DirDetails& details,int coln) time_t now = time(NULL) ; if(ageIndicator != IND_ALWAYS && now > details.max_mtime + ageIndicator) - return QIcon(":/images/folder_grey.png"); + return FilesDefs::getIconFromQtResourcePath(":/images/folder_grey.png"); else if (ageIndicator == IND_LAST_DAY ) - return QIcon(":/images/folder_green.png"); + return FilesDefs::getIconFromQtResourcePath(":/images/folder_green.png"); else if (ageIndicator == IND_LAST_WEEK ) - return QIcon(":/images/folder_yellow.png"); + return FilesDefs::getIconFromQtResourcePath(":/images/folder_yellow.png"); else if (ageIndicator == IND_LAST_MONTH ) - return QIcon(":/images/folder_red.png"); + return FilesDefs::getIconFromQtResourcePath(":/images/folder_red.png"); else return QIcon(categoryIcon); } @@ -428,9 +428,9 @@ QVariant RetroshareDirModel::decorationRole(const DirDetails& details,int coln) { // extensions predefined if(details.hash.isNull()) - return QIcon(":/images/reset.png") ; // file is being hashed + return FilesDefs::getIconFromQtResourcePath(":/images/reset.png") ; // file is being hashed else - return FilesDefs::getIconFromFilename(QString::fromUtf8(details.name.c_str())); + return FilesDefs::getIconFromFileType(QString::fromUtf8(details.name.c_str())); } else return QVariant(); @@ -521,7 +521,19 @@ QVariant TreeStyle_RDM::displayRole(const DirDetails& details,int coln) const case COLUMN_SIZE: return misc::friendlyUnit(details.count); case COLUMN_AGE: - return (details.type == DIR_TYPE_FILE)?(misc::timeRelativeToNow(details.max_mtime)):QString(); + { + if(details.type == DIR_TYPE_FILE) + return misc::timeRelativeToNow(details.max_mtime); + else if(details.type == DIR_TYPE_EXTRA_FILE) + { + FileInfo fi; + if (rsFiles->FileDetails(details.hash, RS_FILE_HINTS_EXTRA , fi)) + return misc::timeRelativeToNow((rstime_t)fi.age-(30 * 3600 * 24)); // AFI_DEFAULT_PERIOD + return QString(); + } + else + return QString(); + } case COLUMN_FRIEND_ACCESS: return QVariant(); case COLUMN_WN_VISU_DIR: @@ -1104,15 +1116,9 @@ Qt::ItemFlags RetroshareDirModel::flags( const QModelIndex & index ) const /* Callback from Core*/ void RetroshareDirModel::preMods() { - emit layoutAboutToBeChanged(); mUpdating = true ; -#if QT_VERSION < 0x050000 - reset(); -#else - beginResetModel(); - endResetModel(); -#endif + beginResetModel(); #ifdef RDM_DEBUG std::cerr << "RetroshareDirModel::preMods()" << std::endl; #endif @@ -1121,20 +1127,14 @@ void RetroshareDirModel::preMods() /* Callback from Core*/ void RetroshareDirModel::postMods() { -// emit layoutAboutToBeChanged(); mUpdating = false ; -#if QT_VERSION >= 0x040600 - beginResetModel(); -#endif - #ifdef RDM_DEBUG std::cerr << "RetroshareDirModel::postMods()" << std::endl; #endif -#if QT_VERSION >= 0x040600 endResetModel(); -#endif - emit layoutChanged(); + + emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(rowCount()-1,COLUMN_COUNT-1,(void*)NULL)); } void FlatStyle_RDM::postMods() @@ -1382,7 +1382,7 @@ void RetroshareDirModel::getFileInfoFromIndexList(const QModelIndexList& list, s * OLD RECOMMEND SYSTEM - DISABLED ******/ -void RetroshareDirModel::openSelected(const QModelIndexList &qmil) +void RetroshareDirModel::openSelected(const QModelIndexList &qmil, bool openDir) { #ifdef RDM_DEBUG std::cerr << "RetroshareDirModel::openSelected()" << std::endl; @@ -1405,12 +1405,15 @@ void RetroshareDirModel::openSelected(const QModelIndexList &qmil) QDir dir(QString::fromUtf8((*it).path.c_str())); QString dest; - if ((*it).type & DIR_TYPE_FILE || (*it).type & DIR_TYPE_EXTRA_FILE) { + if ((*it).type & DIR_TYPE_FILE || (!openDir && (*it).type & DIR_TYPE_EXTRA_FILE)) { dest = dir.absoluteFilePath(QString::fromUtf8(it->name.c_str())); } else if ((*it).type & DIR_TYPE_DIR) { dest = dir.absolutePath(); + } else if (openDir) // extra + { + QDir d = QFileInfo(it->name.c_str()).absoluteDir(); + dest = d.absolutePath(); } - std::cerr << "Opening this file: " << dest.toStdString() << std::endl ; RsUrlHandler::openUrl(QUrl::fromLocalFile(dest)); diff --git a/retroshare-gui/src/gui/RemoteDirModel.h b/retroshare-gui/src/gui/RemoteDirModel.h index 7184ae8d2..115ba5b20 100644 --- a/retroshare-gui/src/gui/RemoteDirModel.h +++ b/retroshare-gui/src/gui/RemoteDirModel.h @@ -82,7 +82,7 @@ class RetroshareDirModel : public QAbstractItemModel int getType ( const QModelIndex & index ) const ; void getFileInfoFromIndexList(const QModelIndexList& list, std::list& files_info) ; - void openSelected(const QModelIndexList &list); + void openSelected(const QModelIndexList &list, bool openDir = false); void getFilePaths(const QModelIndexList &list, std::list &fullpaths); void getFilePath(const QModelIndex& index, std::string& fullpath); void changeAgeIndicator(uint32_t indicator) { ageIndicator = indicator; } diff --git a/retroshare-gui/src/gui/RetroShareLink.cpp b/retroshare-gui/src/gui/RetroShareLink.cpp index fe7374c90..72d6d7464 100644 --- a/retroshare-gui/src/gui/RetroShareLink.cpp +++ b/retroshare-gui/src/gui/RetroShareLink.cpp @@ -1706,15 +1706,13 @@ static void processList(const QStringList &list, const QString &textSingular, co } break; - case TYPE_FILE_TREE: - { - FileTree *ft = FileTree::create(link.radix().toStdString()) ; - - RsCollection(*ft).downloadFiles() ; - - delete ft; - } + case TYPE_FILE_TREE: + { + auto ft = RsFileTree::fromRadix64( + link.radix().toStdString() ); + RsCollection(*ft).downloadFiles(); break; + } case TYPE_CHAT_ROOM: { diff --git a/retroshare-gui/src/gui/RsAutoUpdatePage.cpp b/retroshare-gui/src/gui/RsAutoUpdatePage.cpp index 03d231cf0..4c18d294b 100644 --- a/retroshare-gui/src/gui/RsAutoUpdatePage.cpp +++ b/retroshare-gui/src/gui/RsAutoUpdatePage.cpp @@ -19,7 +19,8 @@ *******************************************************************************/ #include -#include "RsAutoUpdatePage.h" + +#include bool RsAutoUpdatePage::_locked = false ; diff --git a/retroshare-gui/src/gui/ServicePermissionDialog.cpp b/retroshare-gui/src/gui/ServicePermissionDialog.cpp index e90f08f63..9eb898058 100644 --- a/retroshare-gui/src/gui/ServicePermissionDialog.cpp +++ b/retroshare-gui/src/gui/ServicePermissionDialog.cpp @@ -23,6 +23,7 @@ #include "ServicePermissionDialog.h" #include "ui_ServicePermissionDialog.h" #include "settings/rsharesettings.h" +#include "gui/common/FilesDefs.h" static ServicePermissionDialog *servicePermissionDialog = NULL; @@ -36,7 +37,7 @@ ServicePermissionDialog::ServicePermissionDialog() : Settings->loadWidgetInformation(this); - ui->headerFrame->setHeaderImage(QPixmap(":/images/user/servicepermissions64.png")); + ui->headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/images/user/servicepermissions64.png")); ui->headerFrame->setHeaderText(tr("Service Permissions")); connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(setPermissions())); diff --git a/retroshare-gui/src/gui/ShareManager.cpp b/retroshare-gui/src/gui/ShareManager.cpp index c0b234999..8f4e32542 100644 --- a/retroshare-gui/src/gui/ShareManager.cpp +++ b/retroshare-gui/src/gui/ShareManager.cpp @@ -38,10 +38,11 @@ #include "gui/notifyqt.h" #include "util/QtVersion.h" #include "util/misc.h" +#include "gui/common/FilesDefs.h" /* Images for context menu icons */ #define IMAGE_CANCEL ":/images/delete.png" -#define IMAGE_EDIT ":/images/edit_16.png" +#define IMAGE_EDIT ":/icons/png/pencil-edit-button.png" #define COLUMN_PATH 0 #define COLUMN_VIRTUALNAME 1 @@ -57,7 +58,7 @@ ShareManager::ShareManager() /* Invoke Qt Designer generated QObject setup routine */ ui.setupUi(this); - ui.headerFrame->setHeaderImage(QPixmap(":/images/fileshare64.png")); + ui.headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/images/fileshare64.png")); ui.headerFrame->setHeaderText(tr("Share Manager")); isLoading = false; @@ -250,21 +251,23 @@ void ShareManager::load() listWidget->item(row,COLUMN_GROUPS)->setText(getGroupString(mDirInfos[row].parent_groups)); - QFont font = listWidget->item(row,COLUMN_GROUPS)->font(); - font.setBold(mDirInfos[row].shareflags & DIR_FLAGS_BROWSABLE) ; - listWidget->item(row,COLUMN_GROUPS)->setTextColor( (mDirInfos[row].shareflags & DIR_FLAGS_BROWSABLE)? (Qt::black):(Qt::lightGray)) ; - listWidget->item(row,COLUMN_GROUPS)->setFont(font); + //TODO (Phenom): Add qproperty for these text colors in stylesheets + // As palette is not updated by stylesheet + QFont font = listWidget->item(row,COLUMN_GROUPS)->font(); + font.setBold(mDirInfos[row].shareflags & DIR_FLAGS_BROWSABLE) ; + listWidget->item(row,COLUMN_GROUPS)->setData(Qt::ForegroundRole, QColor((mDirInfos[row].shareflags & DIR_FLAGS_BROWSABLE) ? (Qt::black):(Qt::lightGray)) ); + listWidget->item(row,COLUMN_GROUPS)->setFont(font); - if(QDir(QString::fromUtf8(mDirInfos[row].filename.c_str())).exists()) - { - listWidget->item(row,COLUMN_PATH)->setTextColor(Qt::black); + if(QDir(QString::fromUtf8(mDirInfos[row].filename.c_str())).exists()) + { + listWidget->item(row,COLUMN_PATH)->setData(Qt::ForegroundRole, QColor(Qt::black)); listWidget->item(row,COLUMN_PATH)->setToolTip(tr("Double click to change shared directory path")) ; - } - else - { - listWidget->item(row,COLUMN_PATH)->setTextColor(Qt::lightGray); + } + else + { + listWidget->item(row,COLUMN_PATH)->setData(Qt::ForegroundRole, QColor(Qt::lightGray)); listWidget->item(row,COLUMN_PATH)->setToolTip(tr("Directory does not exist! Double click to change shared directory path")) ; - } + } } listWidget->setColumnWidth(COLUMN_SHARE_FLAGS,132 * QFontMetricsF(font()).height()/14.0) ; diff --git a/retroshare-gui/src/gui/StartDialog.cpp b/retroshare-gui/src/gui/StartDialog.cpp index beb1175dc..175456b21 100644 --- a/retroshare-gui/src/gui/StartDialog.cpp +++ b/retroshare-gui/src/gui/StartDialog.cpp @@ -29,6 +29,7 @@ #include #include +#include #include @@ -46,6 +47,10 @@ StartDialog::StartDialog(QWidget *parent) #endif Settings->loadWidgetInformation(this); + + /* Put the Login dialog in the screen center */ + const QRect screen = QApplication::desktop()->screenGeometry(); + this->move( screen.center() - this->rect().center() ); /* get all available pgp private certificates.... * mark last one as default. diff --git a/retroshare-gui/src/gui/TheWire/PulseAddDialog.cpp b/retroshare-gui/src/gui/TheWire/PulseAddDialog.cpp index 94361b8dd..0bdd3c338 100644 --- a/retroshare-gui/src/gui/TheWire/PulseAddDialog.cpp +++ b/retroshare-gui/src/gui/TheWire/PulseAddDialog.cpp @@ -1,7 +1,7 @@ /******************************************************************************* * gui/TheWire/PulseAddDialog.cpp * * * - * Copyright (c) 2012 Robert Fernie * + * Copyright (c) 2012-2020 Robert Fernie * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Affero General Public License as * @@ -18,57 +18,204 @@ * * *******************************************************************************/ -#include "gui/TheWire/PulseAddDialog.h" - -#include "gui/PhotoShare/PhotoDrop.h" - #include +#include + +#include "PulseReply.h" + +#include "PulseAddDialog.h" + +const uint32_t PULSE_MAX_SIZE = 1000; // 1k char. /** Constructor */ PulseAddDialog::PulseAddDialog(QWidget *parent) -: QWidget(parent) +: QWidget(parent), mIsReply(false) { ui.setupUi(this); connect(ui.pushButton_Post, SIGNAL( clicked( void ) ), this, SLOT( postPulse( void ) ) ); connect(ui.pushButton_AddURL, SIGNAL( clicked( void ) ), this, SLOT( addURL( void ) ) ); - connect(ui.pushButton_AddTo, SIGNAL( clicked( void ) ), this, SLOT( addTo( void ) ) ); + connect(ui.pushButton_ClearDisplayAs, SIGNAL( clicked( void ) ), this, SLOT( clearDisplayAs( void ) ) ); connect(ui.pushButton_Cancel, SIGNAL( clicked( void ) ), this, SLOT( cancelPulse( void ) ) ); -#if 0 - connect(ui.scrollAreaWidgetContents, SIGNAL( buttonStatus( uint32_t ) ), this, SLOT( updateMoveButtons( uint32_t ) ) ); - connect(ui.pushButton_ShiftRight, SIGNAL( clicked( void ) ), ui.scrollAreaWidgetContents, SLOT( moveRight( void ) ) ); - connect(ui.pushButton_EditPhotoDetails, SIGNAL( clicked( void ) ), this, SLOT( showPhotoDetails( void ) ) ); + connect(ui.textEdit_Pulse, SIGNAL( textChanged( void ) ), this, SLOT( pulseTextChanged( void ) ) ); - connect(ui.pushButton_Publish, SIGNAL( clicked( void ) ), this, SLOT( publishAlbum( void ) ) ); -#endif + setAcceptDrops(true); +} - mPhotoDetails = NULL; +void PulseAddDialog::setGroup(RsWireGroup &group) +{ + ui.label_groupName->setText(QString::fromStdString(group.mMeta.mGroupName)); + ui.label_idName->setText(QString::fromStdString(group.mMeta.mAuthorId.toStdString())); + mGroup = group; +} + +// set ReplyWith Group. +void PulseAddDialog::setGroup(const RsGxsGroupId &grpId) +{ + /* fetch in the background */ + RsWireGroupSPtr pGroup; + rsWire->getWireGroup(grpId, pGroup); + + setGroup(*pGroup); +} + +void PulseAddDialog::cleanup() +{ + if (mIsReply) + { + std::cerr << "PulseAddDialog::cleanup() cleaning up old replyto"; + std::cerr << std::endl; + QLayout *layout = ui.widget_replyto->layout(); + // completely delete layout and sublayouts + QLayoutItem * item; + QWidget * widget; + while ((item = layout->takeAt(0))) + { + if ((widget = item->widget()) != 0) + { + std::cerr << "PulseAddDialog::cleanup() removing widget"; + std::cerr << std::endl; + widget->hide(); + delete widget; + } + else + { + std::cerr << "PulseAddDialog::cleanup() removing item"; + std::cerr << std::endl; + delete item; + } + } + // then finally + delete layout; + mIsReply = false; + } + ui.frame_reply->setVisible(false); + ui.comboBox_sentiment->setCurrentIndex(0); + ui.lineEdit_URL->setText(""); + ui.lineEdit_DisplayAs->setText(""); + ui.textEdit_Pulse->setPlainText(""); + // disable URL until functionality finished. + ui.frame_URL->setEnabled(false); + + ui.pushButton_Post->setEnabled(false); + ui.pushButton_Post->setText("Post Pulse to Wire"); + ui.frame_input->setVisible(true); + ui.widget_sentiment->setVisible(true); + + // cleanup images. + mImage1.clear(); + ui.label_image1->clear(); + ui.label_image1->setText("Drag and Drop Image"); + + mImage2.clear(); + ui.label_image2->clear(); + ui.label_image2->setText("Drag and Drop Image"); + + mImage3.clear(); + ui.label_image3->clear(); + ui.label_image3->setText("Drag and Drop Image"); + + mImage4.clear(); + ui.label_image4->clear(); + ui.label_image4->setText("Drag and Drop Image"); +} + +void PulseAddDialog::pulseTextChanged() +{ + std::string pulseText = ui.textEdit_Pulse->toPlainText().toStdString(); + bool enable = (pulseText.size() > 0) && (pulseText.size() < PULSE_MAX_SIZE); + ui.pushButton_Post->setEnabled(enable); +} + +// Old Interface, deprecate / make internal. +// TODO: Convert mReplyToPulse to be an SPtr, and remove &pulse parameter. +void PulseAddDialog::setReplyTo(RsWirePulse &pulse, RsWirePulseSPtr pPulse, std::string &groupName, uint32_t replyType) +{ + mIsReply = true; + mReplyToPulse = pulse; + mReplyType = replyType; + ui.frame_reply->setVisible(true); + + { + PulseReply *reply = new PulseReply(NULL, pPulse); + + // add extra widget into layout. + QVBoxLayout *vbox = new QVBoxLayout(); + vbox->addWidget(reply); + vbox->setSpacing(1); + vbox->setContentsMargins(0,0,0,0); + ui.widget_replyto->setLayout(vbox); + ui.widget_replyto->setVisible(true); + } + + if (mReplyType & WIRE_PULSE_TYPE_REPLY) + { + ui.pushButton_Post->setText("Reply to Pulse"); + } + else + { + // cannot add msg for like / republish. + ui.pushButton_Post->setEnabled(true); + ui.frame_input->setVisible(false); + ui.widget_sentiment->setVisible(false); + if (mReplyType & WIRE_PULSE_TYPE_REPUBLISH) { + ui.pushButton_Post->setText("Republish Pulse"); + } + else if (mReplyType & WIRE_PULSE_TYPE_LIKE) { + ui.pushButton_Post->setText("Like Pulse"); + } + } } +void PulseAddDialog::setReplyTo(const RsGxsGroupId &grpId, const RsGxsMessageId &msgId, uint32_t replyType) +{ + /* fetch in the background */ + RsWireGroupSPtr pGroup; + if (!rsWire->getWireGroup(grpId, pGroup)) + { + std::cerr << "PulseAddDialog::setRplyTo() failed to fetch group"; + std::cerr << std::endl; + return; + } + + RsWirePulseSPtr pPulse; + if (!rsWire->getWirePulse(grpId, msgId, pPulse)) + { + std::cerr << "PulseAddDialog::setRplyTo() failed to fetch pulse"; + std::cerr << std::endl; + return; + } + + // update GroupPtr + // TODO - this should be handled in libretroshare if possible. + if (pPulse->mGroupPtr == NULL) { + pPulse->mGroupPtr = pGroup; + } + + setReplyTo(*pPulse, pPulse, pGroup->mMeta.mGroupName, replyType); +} void PulseAddDialog::addURL() { - std::cerr << "PulseAddDialog::addURL()"; - std::cerr << std::endl; + std::cerr << "PulseAddDialog::addURL()"; + std::cerr << std::endl; return; } - -void PulseAddDialog::addTo() +void PulseAddDialog::clearDisplayAs() { - std::cerr << "PulseAddDialog::addTo()"; - std::cerr << std::endl; - + std::cerr << "PulseAddDialog::clearDisplayAs()"; + std::cerr << std::endl; return; } void PulseAddDialog::cancelPulse() { - std::cerr << "PulseAddDialog::cancelPulse()"; - std::cerr << std::endl; + std::cerr << "PulseAddDialog::cancelPulse()"; + std::cerr << std::endl; clearDialog(); hide(); @@ -76,131 +223,243 @@ void PulseAddDialog::cancelPulse() return; } - - -void PulseAddDialog::updateMoveButtons(uint32_t status) -{ - std::cerr << "PulseAddDialog::updateMoveButtons(" << status << ")"; - std::cerr << std::endl; - -#if 0 - switch(status) - { - case PHOTO_SHIFT_NO_BUTTONS: - ui.pushButton_ShiftLeft->setEnabled(false); - ui.pushButton_ShiftRight->setEnabled(false); - break; - case PHOTO_SHIFT_LEFT_ONLY: - ui.pushButton_ShiftLeft->setEnabled(true); - ui.pushButton_ShiftRight->setEnabled(false); - break; - case PHOTO_SHIFT_RIGHT_ONLY: - ui.pushButton_ShiftLeft->setEnabled(false); - ui.pushButton_ShiftRight->setEnabled(true); - break; - case PHOTO_SHIFT_BOTH: - ui.pushButton_ShiftLeft->setEnabled(true); - ui.pushButton_ShiftRight->setEnabled(true); - break; - } -#endif -} - - -void PulseAddDialog::showPhotoDetails() -{ - -#if 0 - std::cerr << "PulseAddDialog::showPhotoDetails()"; - std::cerr << std::endl; - - if (!mPhotoDetails) - { - mPhotoDetails = new PhotoDetailsDialog(NULL); - } - - PhotoItem *item = ui.scrollAreaWidgetContents->getSelectedPhotoItem(); - - mPhotoDetails->setPhotoItem(item); - mPhotoDetails->show(); -#endif -} - - - - void PulseAddDialog::postPulse() { - std::cerr << "PulseAddDialog::postPulse()"; - std::cerr << std::endl; - -#if 0 - /* we need to iterate through each photoItem, and extract the details */ - - - RsPhotoAlbum album; - RsPhotoThumbnail albumThumb; - - album.mShareOptions.mShareType = 0; - album.mShareOptions.mShareGroupId = "unknown"; - album.mShareOptions.mPublishKey = "unknown"; - album.mShareOptions.mCommentMode = 0; - album.mShareOptions.mResizeMode = 0; - - album.mTitle = ui.lineEdit_Title->text().toStdString(); - album.mCategory = "Unknown"; - album.mCaption = ui.lineEdit_Caption->text().toStdString(); - album.mWhere = ui.lineEdit_Where->text().toStdString(); - album.mWhen = ui.lineEdit_When->text().toStdString(); - - if (rsPhoto->submitAlbumDetails(album, albumThumb)) + std::cerr << "PulseAddDialog::postPulse()"; + std::cerr << std::endl; + if (mIsReply) { - /* now have path and album id */ - int photoCount = ui.scrollAreaWidgetContents->getPhotoCount(); + postReplyPulse(); + } + else + { + postOriginalPulse(); + } +} - for(int i = 0; i < photoCount; ++i) - { - RsPhotoPhoto photo; - RsPhotoThumbnail thumbnail; - PhotoItem *item = ui.scrollAreaWidgetContents->getPhotoIdx(i); - photo = item->mDetails; - item->getPhotoThumbnail(thumbnail); - - photo.mAlbumId = album.mAlbumId; - photo.mOrder = i; - /* scale photo if needed */ - if (album.mShareOptions.mResizeMode) - { - /* */ +void PulseAddDialog::postOriginalPulse() +{ + std::cerr << "PulseAddDialog::postOriginalPulse()"; + std::cerr << std::endl; - } - /* save image to album path */ - photo.path = "unknown"; + RsWirePulseSPtr pPulse(new RsWirePulse()); - rsPhoto->submitPhoto(photo, thumbnail); - } + pPulse->mSentiment = WIRE_PULSE_SENTIMENT_NO_SENTIMENT; + pPulse->mPulseText = ui.textEdit_Pulse->toPlainText().toStdString(); + // set images here too. + pPulse->mImage1 = mImage1; + pPulse->mImage2 = mImage2; + pPulse->mImage3 = mImage3; + pPulse->mImage4 = mImage4; + + // this should be in async thread, so doesn't block UI thread. + if (!rsWire->createOriginalPulse(mGroup.mMeta.mGroupId, pPulse)) + { + std::cerr << "PulseAddDialog::postOriginalPulse() FAILED"; + std::cerr << std::endl; + return; } -#endif clearDialog(); - hide(); } +uint32_t PulseAddDialog::toPulseSentiment(int index) +{ + switch(index) + { + case 1: + return WIRE_PULSE_SENTIMENT_POSITIVE; + break; + case 2: + return WIRE_PULSE_SENTIMENT_NEUTRAL; + break; + case 3: + return WIRE_PULSE_SENTIMENT_NEGATIVE; + break; + case -1: + case 0: + default: + return WIRE_PULSE_SENTIMENT_NO_SENTIMENT; + break; + } + return 0; +} + +void PulseAddDialog::postReplyPulse() +{ + std::cerr << "PulseAddDialog::postReplyPulse()"; + std::cerr << std::endl; + + RsWirePulseSPtr pPulse(new RsWirePulse()); + + pPulse->mSentiment = toPulseSentiment(ui.comboBox_sentiment->currentIndex()); + pPulse->mPulseText = ui.textEdit_Pulse->toPlainText().toStdString(); + // set images here too. + pPulse->mImage1 = mImage1; + pPulse->mImage2 = mImage2; + pPulse->mImage3 = mImage3; + pPulse->mImage4 = mImage4; + + if (mReplyType & WIRE_PULSE_TYPE_REPUBLISH) { + // Copy details from parent, and override + pPulse->mSentiment = mReplyToPulse.mSentiment; + pPulse->mPulseText = mReplyToPulse.mPulseText; + + // Copy images. + pPulse->mImage1 = mReplyToPulse.mImage1; + pPulse->mImage2 = mReplyToPulse.mImage2; + pPulse->mImage3 = mReplyToPulse.mImage3; + pPulse->mImage4 = mReplyToPulse.mImage4; + } + + // this should be in async thread, so doesn't block UI thread. + if (!rsWire->createReplyPulse(mReplyToPulse.mMeta.mGroupId, + mReplyToPulse.mMeta.mOrigMsgId, + mGroup.mMeta.mGroupId, + mReplyType, + pPulse)) + { + std::cerr << "PulseAddDialog::postReplyPulse() FAILED"; + std::cerr << std::endl; + return; + } + + clearDialog(); + hide(); +} void PulseAddDialog::clearDialog() { - ui.textEdit_Pulse->setPlainText(""); -#if 0 - ui.lineEdit_Title->setText(QString("title")); - ui.lineEdit_Caption->setText(QString("Caption")); - ui.lineEdit_Where->setText(QString("Where")); - ui.lineEdit_When->setText(QString("When")); - - ui.scrollAreaWidgetContents->clearPhotos(); -#endif } - +//--------------------------------------------------------------------- +// Drag and Drop Images. + +void PulseAddDialog::dragEnterEvent(QDragEnterEvent *event) +{ + std::cerr << "PulseAddDialog::dragEnterEvent()"; + std::cerr << std::endl; + + if (event->mimeData()->hasUrls()) + { + std::cerr << "PulseAddDialog::dragEnterEvent() Accepting"; + std::cerr << std::endl; + event->accept(); + } + else + { + std::cerr << "PulseAddDialog::dragEnterEvent() Ignoring"; + std::cerr << std::endl; + event->ignore(); + } +} + +void PulseAddDialog::dragLeaveEvent(QDragLeaveEvent *event) +{ + std::cerr << "PulseAddDialog::dragLeaveEvent()"; + std::cerr << std::endl; + + event->ignore(); +} + +void PulseAddDialog::dragMoveEvent(QDragMoveEvent *event) +{ + std::cerr << "PulseAddDialog::dragMoveEvent()"; + std::cerr << std::endl; + + event->accept(); +} + +void PulseAddDialog::dropEvent(QDropEvent *event) +{ + std::cerr << "PulseAddDialog::dropEvent()"; + std::cerr << std::endl; + + if (event->mimeData()->hasUrls()) + { + std::cerr << "PulseAddDialog::dropEvent() Urls:" << std::endl; + + QList urls = event->mimeData()->urls(); + QList::iterator uit; + for (uit = urls.begin(); uit != urls.end(); ++uit) + { + QString localpath = uit->toLocalFile(); + std::cerr << "Whole URL: " << uit->toString().toStdString() << std::endl; + std::cerr << "or As Local File: " << localpath.toStdString() << std::endl; + + addImage(localpath); + } + event->setDropAction(Qt::CopyAction); + event->accept(); + } + else + { + std::cerr << "PulseAddDialog::dropEvent Ignoring"; + std::cerr << std::endl; + event->ignore(); + } +} + + +void PulseAddDialog::addImage(const QString &path) +{ + std::cerr << "PulseAddDialog::addImage() loading image from: " << path.toStdString(); + std::cerr << std::endl; + + QPixmap qtn = FilesDefs::getPixmapFromQtResourcePath(path); + if (qtn.isNull()) { + std::cerr << "PulseAddDialog::addImage() Invalid Image"; + std::cerr << std::endl; + return; + } + + QPixmap image; + if ((qtn.width() <= 512) && (qtn.height() <= 512)) { + image = qtn; + } else { + image = qtn.scaled(512, 512, Qt::KeepAspectRatio, Qt::SmoothTransformation); + } + + // scaled down for display, allow wide images. + QPixmap icon = qtn.scaled(256, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation); + QByteArray ba; + QBuffer buffer(&ba); + + buffer.open(QIODevice::WriteOnly); + image.save(&buffer, "JPG"); + + if (mImage1.empty()) { + std::cerr << "PulseAddDialog::addImage() Installing in Image1"; + std::cerr << std::endl; + ui.label_image1->setPixmap(icon); + mImage1.copy((uint8_t *) ba.data(), ba.size()); + std::cerr << "PulseAddDialog::addImage() Installing in Image1 Size: " << mImage1.mSize; + std::cerr << std::endl; + } + else if (mImage2.empty()) { + ui.label_image2->setPixmap(icon); + mImage2.copy((uint8_t *) ba.data(), ba.size()); + std::cerr << "PulseAddDialog::addImage() Installing in Image2 Size: " << mImage2.mSize; + std::cerr << std::endl; + } + else if (mImage3.empty()) { + ui.label_image3->setPixmap(icon); + mImage3.copy((uint8_t *) ba.data(), ba.size()); + std::cerr << "PulseAddDialog::addImage() Installing in Image3 Size: " << mImage3.mSize; + std::cerr << std::endl; + } + else if (mImage4.empty()) { + ui.label_image4->setPixmap(icon); + mImage4.copy((uint8_t *) ba.data(), ba.size()); + std::cerr << "PulseAddDialog::addImage() Installing in Image4 Size: " << mImage4.mSize; + std::cerr << std::endl; + } + else { + std::cerr << "PulseAddDialog::addImage() Images all full"; + std::cerr << std::endl; + } +} + diff --git a/retroshare-gui/src/gui/TheWire/PulseAddDialog.h b/retroshare-gui/src/gui/TheWire/PulseAddDialog.h index 79c82128f..51f92f4e9 100644 --- a/retroshare-gui/src/gui/TheWire/PulseAddDialog.h +++ b/retroshare-gui/src/gui/TheWire/PulseAddDialog.h @@ -1,7 +1,7 @@ /******************************************************************************* * gui/TheWire/PulseAddDialog.h * * * - * Copyright (c) 2012 Robert Fernie * + * Copyright (c) 2012-2020 Robert Fernie * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Affero General Public License as * @@ -23,9 +23,15 @@ #include "ui_PulseAddDialog.h" -#include +#include + + +QT_BEGIN_NAMESPACE +class QDragEnterEvent; +class QDropEvent; +class QMouseEvent; +QT_END_NAMESPACE -class PhotoDetailsDialog; class PulseAddDialog : public QWidget { @@ -34,21 +40,51 @@ class PulseAddDialog : public QWidget public: PulseAddDialog(QWidget *parent = 0); -private slots: - void showPhotoDetails(); - void updateMoveButtons(uint32_t status); + void cleanup(); + void setReplyTo(const RsGxsGroupId &grpId, const RsGxsMessageId &msgId, uint32_t replyType); + void setGroup(const RsGxsGroupId &grpId); + +private slots: void addURL(); - void addTo(); + void clearDisplayAs(); void postPulse(); void cancelPulse(); void clearDialog(); + void pulseTextChanged(); + +private: + // OLD VERSIONs, private now. + void setGroup(RsWireGroup &group); + void setReplyTo(RsWirePulse &pulse, RsWirePulseSPtr pPulse, std::string &groupName, uint32_t replyType); + + void postOriginalPulse(); + void postReplyPulse(); + + uint32_t toPulseSentiment(int index); protected: + void dragEnterEvent(QDragEnterEvent *event); + void dragLeaveEvent(QDragLeaveEvent *event); + void dragMoveEvent(QDragMoveEvent *event); + void dropEvent(QDropEvent *event); + + void addImage(const QString &path); + + RsWireGroup mGroup; // replyWith. + + // if this is a reply + bool mIsReply; + RsWirePulse mReplyToPulse; + uint32_t mReplyType; + + // images + RsGxsImage mImage1; + RsGxsImage mImage2; + RsGxsImage mImage3; + RsGxsImage mImage4; - PhotoDetailsDialog *mPhotoDetails; Ui::PulseAddDialog ui; - }; #endif diff --git a/retroshare-gui/src/gui/TheWire/PulseAddDialog.ui b/retroshare-gui/src/gui/TheWire/PulseAddDialog.ui index 08110c603..0b6c4a70c 100644 --- a/retroshare-gui/src/gui/TheWire/PulseAddDialog.ui +++ b/retroshare-gui/src/gui/TheWire/PulseAddDialog.ui @@ -7,282 +7,327 @@ 0 0 720 - 586 + 633 - - - - - Qt::Vertical + + + + + + 0 + 0 + - - - - 160 - 16777215 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 - - - 0 - - - - - - 0 - 0 - - - - true - - - - - 0 - 0 - 156 - 184 - - - - - - - - 12 - 75 - true - - - - Post From: - - - - - - - Account 1 - - - - - - - Account 2 - - - - - - - Account 3 - - - - - - - Qt::Vertical - - - - 20 - 0 - - - - - - - - - - - - - true - - - - - 0 - 0 - 158 - 372 - - - - - - - Add to Pulse - - - - - - - filter - - - - - - - true - - - - - 0 - 0 - 138 - 286 - - - - - - - Account 1 - - - - - - - Account 2 - - - - - - - Account 3 - - - - - - - Qt::Vertical - - - - 20 - 70 - - - - - - - - - - - - - - - - - - - - URL Adder + + + 16777215 + 16777215 + - - - - - Qt::Horizontal - - + + QFrame::StyledPanel + + + QFrame::Raised + + + + + - 331 - 24 + 100 + 16777215 - - - - + + + 12 + 75 + true + + - Add to Pulse + Post From: - - + + - Display As + GroupLabel - - - - - + + - URL + IDLabel - - - - - - - Cancel + + + + QFrame::StyledPanel + + QFrame::Raised + + + + + + + + + + + + Response Sentiment: + + + + + + + + 0 + 0 + + + + + 150 + 0 + + + + + No Sentiment + + + + + Positive + + + + + Neutral + + + + + Negative + + + + + + + + Qt::Horizontal + + + + 238 + 20 + + + + + + + + - - - - Qt::Horizontal + + + + QFrame::StyledPanel - - - 298 - 24 - - - - - - - - Post Pulse to Wire + + QFrame::Raised + + + + + + + + + 0 + 100 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 80 + 80 + + + + Drag and Drop Image + + + Qt::AlignCenter + + + + + + + + 80 + 80 + + + + Drag and Drop Image + + + Qt::AlignCenter + + + + + + + + 80 + 80 + + + + Drag and Drop Image + + + Qt::AlignCenter + + + + + + + + 80 + 80 + + + + Drag and Drop Image + + + Qt::AlignCenter + + + + + + + + + + + + + URL + + + + + + + + + + Add to Pulse + + + + + + + Display As + + + + + + + + + + Clear Display As + + + + + + + + + + + + + Cancel + + + + + + + Qt::Horizontal + + + + 298 + 24 + + + + + + + + Post Pulse to Wire + + + + + + + + GxsIdLabel + QLabel +

gui/gxs/GxsIdLabel.h
+ + - + diff --git a/retroshare-gui/src/gui/TheWire/PulseItem.cpp b/retroshare-gui/src/gui/TheWire/PulseItem.cpp deleted file mode 100644 index 2eb328b3e..000000000 --- a/retroshare-gui/src/gui/TheWire/PulseItem.cpp +++ /dev/null @@ -1,251 +0,0 @@ -/******************************************************************************* - * gui/TheWire/PulseItem.cpp * - * * - * Copyright (c) 2012 Robert Fernie * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Affero General Public License as * - * published by the Free Software Foundation, either version 3 of the * - * License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Affero General Public License for more details. * - * * - * You should have received a copy of the GNU Affero General Public License * - * along with this program. If not, see . * - * * - *******************************************************************************/ - -#include -#include -#include -#include - -#include "PulseItem.h" - -#include - -#include -#include - -/**** - * #define DEBUG_ITEM 1 - ****/ - -/** Constructor */ -PulseItem::PulseItem(PulseHolder *parent, const RsPhotoAlbum &album, const RsPhotoThumbnail &thumbnail) -:QWidget(NULL), mParent(parent), mType(PHOTO_ITEM_TYPE_ALBUM) -{ - setupUi(this); - - setAttribute ( Qt::WA_DeleteOnClose, true ); - - mDetails = *( (RsPhotoPhoto *) &(album)); - updateAlbumText(album); - updateImage(thumbnail); - - setSelected(false); -} - - -PulseItem::PulseItem(PulseHolder *parent, const RsPhotoPhoto &photo, const RsPhotoThumbnail &thumbnail) -:QWidget(NULL), mParent(parent), mType(PHOTO_ITEM_TYPE_PHOTO) -{ - setupUi(this); - - setAttribute ( Qt::WA_DeleteOnClose, true ); - - mDetails = *( (RsPhotoPhoto *) &(photo)); - - updatePhotoText(photo); - updateImage(thumbnail); - - setSelected(false); -} - - -PulseItem::PulseItem(PulseHolder *parent, std::string path) // for new photos. -:QWidget(NULL), mParent(parent), mType(PHOTO_ITEM_TYPE_NEW) -{ - setupUi(this); - - setAttribute ( Qt::WA_DeleteOnClose, true ); - -#if 0 - QString dummyString("dummytext"); - titleLabel->setText(QString("NEW PHOTO")); - - fromBoldLabel->setText(QString("From:")); - fromLabel->setText(QString("Ourselves")); - - statusBoldLabel->setText(QString("Status:")); - statusLabel->setText(QString("new photo")); - - dateBoldLabel->setText(QString("Date:")); - dateLabel->setText(QString("now")); - - int width = 120; - int height = 120; - - //QPixmap qtn = QPixmap(QString::fromStdString(path)).scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - QPixmap qtn = QPixmap(QString::fromStdString(path)).scaled(width, height, Qt::KeepAspectRatio, Qt::SmoothTransformation); - imgLabel->setPixmap(qtn); - setSelected(false); -#endif -} - -void PulseItem::updateAlbumText(const RsPhotoAlbum &album) -{ -#if 0 - QString dummyString("dummytext"); - titleLabel->setText(QString("TITLE")); - - fromBoldLabel->setText(QString("From:")); - fromLabel->setText(QString("Unknown")); - - statusBoldLabel->setText(QString("Status:")); - statusLabel->setText(QString("new photo")); - - dateBoldLabel->setText(QString("Date:")); - dateLabel->setText(QString("now")); - - //QDateTime qtime; - //qtime.setTime_t(msg.ts); - //QString timestamp = qtime.toString("dd.MMMM yyyy hh:mm"); - //timestamplabel->setText(timestamp); - - dateBoldLabel->setText(dummyString); - dateLabel->setText(dummyString); -#endif -} - -void PulseItem::updatePhotoText(const RsPhotoPhoto &photo) -{ -#if 0 - QString dummyString("dummytext"); - titleLabel->setText(QString("TITLE")); - - fromBoldLabel->setText(QString("From:")); - fromLabel->setText(QString("Unknown")); - - statusBoldLabel->setText(QString("Status:")); - statusLabel->setText(QString("new photo")); - - dateBoldLabel->setText(QString("Date:")); - dateLabel->setText(QString("now")); -#endif -} - - -void PulseItem::updateImage(const RsPhotoThumbnail &thumbnail) -{ -#if 0 - if (thumbnail.data != NULL) - { - QPixmap qtn; - qtn.loadFromData(thumbnail.data, thumbnail.size, thumbnail.type.c_str()); - imgLabel->setPixmap(qtn); - } -#endif -} - -bool PulseItem::getPhotoThumbnail(RsPhotoThumbnail &nail) -{ -#if 0 - const QPixmap *tmppix = imgLabel->pixmap(); - - QByteArray ba; - QBuffer buffer(&ba); - - if(!tmppix->isNull()) - { - // send chan image - - buffer.open(QIODevice::WriteOnly); - tmppix->save(&buffer, "PNG"); // writes image into ba in PNG format - - RsPhotoThumbnail tmpnail; - tmpnail.data = (uint8_t *) ba.data(); - tmpnail.size = ba.size(); - - nail.copyFrom(tmpnail); - - return true; - } - - nail.data = NULL; - nail.size = 0; -#endif - return false; -} - - -void PulseItem::removeItem() -{ -#if 0 -#ifdef DEBUG_ITEM - std::cerr << "PulseItem::removeItem()"; - std::cerr << std::endl; -#endif - hide(); - if (mParent) - { - mParent->deletePulseItem(this, mType); - } -#endif -} - - -void PulseItem::setSelected(bool on) -{ -#if 0 - mSelected = on; - if (mSelected) - { - mParent->notifySelection(this, mType); - frame->setStyleSheet("QFrame#frame{border: 2px solid #55CC55;\nbackground: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #55EE55, stop: 1 #CCCCCC);\nborder-radius: 10px}"); - } - else - { - frame->setStyleSheet("QFrame#frame{border: 2px solid #CCCCCC;\nbackground: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #EEEEEE, stop: 1 #CCCCCC);\nborder-radius: 10px}"); - } - update(); -#endif -} - -bool PulseItem::isSelected() -{ - return mSelected; -} - - -void PulseItem::mousePressEvent(QMouseEvent *event) -{ - /* We can be very cunning here? - * grab out position. - * flag ourselves as selected. - * then pass the mousePressEvent up for handling by the parent - */ - - QPoint pos = event->pos(); - - std::cerr << "PulseItem::mousePressEvent(" << pos.x() << ", " << pos.y() << ")"; - std::cerr << std::endl; - - setSelected(true); - - QWidget::mousePressEvent(event); -} - - -const QPixmap *PulseItem::getPixmap() -{ -#if 0 - return imgLabel->pixmap(); -#endif - return NULL; -} - - diff --git a/retroshare-gui/src/gui/TheWire/PulseItem.ui b/retroshare-gui/src/gui/TheWire/PulseItem.ui deleted file mode 100644 index 6a0286af8..000000000 --- a/retroshare-gui/src/gui/TheWire/PulseItem.ui +++ /dev/null @@ -1,214 +0,0 @@ - - - PulseItem - - - - 0 - 0 - 615 - 232 - - - - - 9 - - - - - - - - - - - 0 - 0 - - - - QFrame#frame{border: 2px solid #CCCCCC; -background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #EEEEEE, stop: 1 #CCCCCC); -border-radius: 10px} - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - 75 - 75 - - - - - 75 - 75 - - - - QLabel#label{border: 2px solid black; -background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 #EEEEEE, stop: 1 #CCCCCC); -border-radius: 10px} - - - - - - :/images/konversation.png - - - Qt::AlignCenter - - - - - - - - - - 0 - 0 - - - - - 9 - 75 - true - - - - From - - - - - - - - 0 - 0 - - - - - 9 - - - - Signed by - - - true - - - - - - - - - - - - 0 - 0 - - - - - 9 - 75 - true - - - - Date - - - - - - - - 0 - 0 - - - - - 9 - - - - You eyes only - - - true - - - - - - - - - - - - Qt::Vertical - - - - 48 - 75 - - - - - - - - - - ... - - - - - - - ... - - - - - - - ... - - - - - - - - - - - - - - - diff --git a/retroshare-gui/src/gui/TheWire/PulseMessage.cpp b/retroshare-gui/src/gui/TheWire/PulseMessage.cpp new file mode 100644 index 000000000..91af09b3a --- /dev/null +++ b/retroshare-gui/src/gui/TheWire/PulseMessage.cpp @@ -0,0 +1,141 @@ +/******************************************************************************* + * gui/TheWire/PulseMessage.cpp * + * * + * Copyright (c) 2020-2020 Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include "PulseMessage.h" + +/** Constructor */ + +PulseMessage::PulseMessage(QWidget *parent) +:QWidget(parent) +{ + setupUi(this); +} + +void PulseMessage::setup(RsWirePulseSPtr pulse) +{ + if (!pulse) { + return; + } + + setMessage(QString::fromStdString(pulse->mPulseText)); + + // show indent if republish (both RESPONSE or REF) + bool showIndent = (pulse->mPulseType & WIRE_PULSE_TYPE_REPUBLISH); + widget_indent->setVisible(showIndent); + + // setup images. + int width = 256; + int height = 128; + bool imagesShown = false; + + if (pulse->mImage2.empty()) { + // allow wider space for image 1. + width = 512; + } + + if (!pulse->mImage1.empty()) { + // install image. + QPixmap qtn; + qtn.loadFromData(pulse->mImage1.mData, pulse->mImage1.mSize); + label_image1->setPixmap(qtn.scaled(width, height, + Qt::KeepAspectRatio, Qt::SmoothTransformation)); + imagesShown = true; + } else { + label_image1->setVisible(false); + } + + if (!pulse->mImage2.empty()) { + // install image. + QPixmap qtn; + qtn.loadFromData(pulse->mImage2.mData, pulse->mImage2.mSize); + label_image2->setPixmap(qtn.scaled(width, height, + Qt::KeepAspectRatio, Qt::SmoothTransformation)); + imagesShown = true; + } else { + label_image2->setVisible(false); + } + + width = 256; + if (pulse->mImage4.empty()) { + // allow wider space for image 3. + width = 512; + } + + if (!pulse->mImage3.empty()) { + // install image. + QPixmap qtn; + qtn.loadFromData(pulse->mImage3.mData, pulse->mImage3.mSize); + label_image3->setPixmap(qtn.scaled(width, height, + Qt::KeepAspectRatio, Qt::SmoothTransformation)); + imagesShown = true; + } else { + label_image3->setVisible(false); + } + + if (!pulse->mImage4.empty()) { + // install image. + QPixmap qtn; + qtn.loadFromData(pulse->mImage4.mData, pulse->mImage4.mSize); + label_image4->setPixmap(qtn.scaled(width, height, + Qt::KeepAspectRatio, Qt::SmoothTransformation)); + imagesShown = true; + } else { + label_image4->setVisible(false); + } + + frame_expand->setVisible(imagesShown); +} + +void PulseMessage::setMessage(QString msg) +{ + textBrowser->setPlainText(msg); +} + +void PulseMessage::setRefImageCount(uint32_t count) +{ + QString msg = "Follow to see Image"; + label_image1->setText(msg); + label_image2->setText(msg); + label_image3->setText(msg); + label_image4->setText(msg); + + label_image1->setVisible(false); + label_image2->setVisible(false); + label_image3->setVisible(false); + label_image4->setVisible(false); + + switch(count) { + case 4: + label_image4->setVisible(true); + case 3: + label_image3->setVisible(true); + case 2: + label_image2->setVisible(true); + case 1: + label_image1->setVisible(true); + default: + break; + } + + if (count < 1) { + frame_expand->setVisible(false); + } +} + diff --git a/retroshare-gui/src/gui/PhotoShare/AddCommentDialog.h b/retroshare-gui/src/gui/TheWire/PulseMessage.h similarity index 72% rename from retroshare-gui/src/gui/PhotoShare/AddCommentDialog.h rename to retroshare-gui/src/gui/TheWire/PulseMessage.h index f2003c132..7e94a23db 100644 --- a/retroshare-gui/src/gui/PhotoShare/AddCommentDialog.h +++ b/retroshare-gui/src/gui/TheWire/PulseMessage.h @@ -1,43 +1,40 @@ -/******************************************************************************* - * retroshare-gui/src/gui/PhotoShare/AddCommentDialog.h * - * * - * Copyright (C) 2018 by Retroshare Team * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Affero General Public License as * - * published by the Free Software Foundation, either version 3 of the * - * License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Affero General Public License for more details. * - * * - * You should have received a copy of the GNU Affero General Public License * - * along with this program. If not, see . * - * * - *******************************************************************************/ - -#ifndef ADDCOMMENTDIALOG_H -#define ADDCOMMENTDIALOG_H - -#include - -namespace Ui { - class AddCommentDialog; -} - -class AddCommentDialog : public QDialog -{ - Q_OBJECT - -public: - explicit AddCommentDialog(QWidget *parent = 0); - ~AddCommentDialog(); - QString getComment() const; - -private: - Ui::AddCommentDialog *ui; -}; - -#endif // ADDCOMMENTDIALOG_H +/******************************************************************************* + * gui/TheWire/PulseMessage.h * + * * + * Copyright (c) 2020-2020 Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#ifndef MRK_PULSE_MSG_H +#define MRK_PULSE_MSG_H + +#include "ui_PulseMessage.h" + +#include + +class PulseMessage : public QWidget, private Ui::PulseMessage +{ + Q_OBJECT + +public: + PulseMessage(QWidget *parent); + + void setup(RsWirePulseSPtr pulse); + void setMessage(QString msg); + void setRefImageCount(uint32_t count); +}; + +#endif diff --git a/retroshare-gui/src/gui/TheWire/PulseMessage.ui b/retroshare-gui/src/gui/TheWire/PulseMessage.ui new file mode 100644 index 000000000..a1ce9831d --- /dev/null +++ b/retroshare-gui/src/gui/TheWire/PulseMessage.ui @@ -0,0 +1,128 @@ + + + PulseMessage + + + + 0 + 0 + 570 + 376 + + + + Form + + + + + + true + + + + + + + 20 + 0 + + + + QFrame::Plain + + + 5 + + + Qt::Vertical + + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 128 + 128 + + + + Image + + + Qt::AlignCenter + + + + + + + + 128 + 128 + + + + Image + + + Qt::AlignCenter + + + + + + + + 128 + 128 + + + + Image + + + Qt::AlignCenter + + + + + + + + 128 + 128 + + + + Image + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/retroshare-gui/src/gui/TheWire/PulseReply.cpp b/retroshare-gui/src/gui/TheWire/PulseReply.cpp new file mode 100644 index 000000000..92dccabf5 --- /dev/null +++ b/retroshare-gui/src/gui/TheWire/PulseReply.cpp @@ -0,0 +1,155 @@ +/******************************************************************************* + * gui/TheWire/PulseReply.cpp * + * * + * Copyright (c) 2020-2020 Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include +#include +#include +#include + +#include "PulseReply.h" + +#include +#include + +/** Constructor */ + +PulseReply::PulseReply(PulseViewHolder *holder, RsWirePulseSPtr pulse) +:PulseDataItem(holder, pulse) +{ + setupUi(this); + setAttribute ( Qt::WA_DeleteOnClose, true ); + + setup(); + + if (mPulse) { + showPulse(); + } + + widget_prefix->setVisible(false); +} + +void PulseReply::setup() +{ + // connect(pushButton_tmpViewGroup, SIGNAL(clicked()), this, SLOT(actionViewGroup())); + // connect(pushButton_tmpViewParent, SIGNAL(clicked()), this, SLOT(actionViewParent())); + + connect(toolButton_follow, SIGNAL(clicked()), this, SLOT(actionFollow())); + // connect(toolButton_rate, SIGNAL(clicked()), this, SLOT(rate())); + + connect(toolButton_reply, SIGNAL(clicked()), this, SLOT(actionReply())); + connect(toolButton_republish, SIGNAL(clicked()), this, SLOT(actionRepublish())); + connect(toolButton_like, SIGNAL(clicked()), this, SLOT(actionLike())); + connect(toolButton_view, SIGNAL(clicked()), this, SLOT(actionViewPulse())); +} + +void PulseReply::showReplyLine(bool enable) +{ + line_replyLine->setVisible(enable); +} + +// PulseDataInterface =========== + // Group +void PulseReply::setHeadshot(const QPixmap &pixmap) +{ + label_headshot->setPixmap(pixmap); +} + +void PulseReply::setGroupNameString(QString name) +{ + label_groupName->setText("@" + name); +} + +void PulseReply::setAuthorString(QString name) +{ + label_authorName->setText(BoldString(name)); +} + + // Msg +void PulseReply::setRefMessage(QString msg, uint32_t image_count) +{ + widget_message->setMessage(msg); + widget_message->setRefImageCount(image_count); +} + +void PulseReply::setMessage(RsWirePulseSPtr pulse) +{ + widget_message->setup(pulse); +} + +void PulseReply::setDateString(QString date) +{ + label_date->setText(date); +} + + // Refs +void PulseReply::setLikesString(QString likes) +{ + label_likes->setText(likes); +} + +void PulseReply::setRepublishesString(QString repub) +{ + label_republishes->setText(repub); +} + +void PulseReply::setRepliesString(QString reply) +{ + label_replies->setText(reply); +} + +void PulseReply::setPulseStatus(PulseStatus status) +{ + widget_actions->setVisible(status == PulseStatus::FULL); + widget_follow->setVisible(status != PulseStatus::FULL); + toolButton_follow->setEnabled(status == PulseStatus::UNSUBSCRIBED); + + switch(status) + { + case PulseStatus::FULL: + break; + case PulseStatus::UNSUBSCRIBED: + break; + case PulseStatus::NO_GROUP: + label_follow_msg->setText("Group unavailable"); + break; + case PulseStatus::REF_MSG: + label_follow_msg->setText("Full Pulse unavailable"); + break; + } +} + +void PulseReply::setReferenceString(QString ref) +{ + if (ref.size() == 0) + { + widget_reply_header->setVisible(false); + } + else + { + label_reference->setText(ref); + } +} +// PulseDataInterface =========== + +void PulseReply::mousePressEvent(QMouseEvent *event) +{ +} + + diff --git a/retroshare-gui/src/gui/TheWire/PulseReply.h b/retroshare-gui/src/gui/TheWire/PulseReply.h new file mode 100644 index 000000000..c2c9545f5 --- /dev/null +++ b/retroshare-gui/src/gui/TheWire/PulseReply.h @@ -0,0 +1,65 @@ +/******************************************************************************* + * gui/TheWire/PulseReply.h * + * * + * Copyright (c) 2020-2020 Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#ifndef MRK_PULSE_REPLY_H +#define MRK_PULSE_REPLY_H + +#include "ui_PulseReply.h" +#include "PulseViewItem.h" + +#include + +class PulseReply : public PulseDataItem, private Ui::PulseReply +{ + Q_OBJECT + +public: + PulseReply(PulseViewHolder *holder, RsWirePulseSPtr pulse); + + void showReplyLine(bool enable); + +protected: + void setup(); + +// PulseDataInterface =========== + // Group + virtual void setHeadshot(const QPixmap &pixmap) override; + virtual void setGroupNameString(QString name) override; + virtual void setAuthorString(QString name) override; + + // Msg + virtual void setRefMessage(QString msg, uint32_t image_count) override; + virtual void setMessage(RsWirePulseSPtr pulse) override; + virtual void setDateString(QString date) override; + + // Refs + virtual void setLikesString(QString likes) override; + virtual void setRepublishesString(QString repub) override; + virtual void setRepliesString(QString reply) override; + + // + virtual void setReferenceString(QString ref) override; + virtual void setPulseStatus(PulseStatus status) override; +// PulseDataInterface =========== + + void mousePressEvent(QMouseEvent *event); +}; + +#endif diff --git a/retroshare-gui/src/gui/TheWire/PulseReply.ui b/retroshare-gui/src/gui/TheWire/PulseReply.ui new file mode 100644 index 000000000..9925d19bd --- /dev/null +++ b/retroshare-gui/src/gui/TheWire/PulseReply.ui @@ -0,0 +1,613 @@ + + + PulseReply + + + + 0 + 0 + 579 + 478 + + + + + 0 + 0 + + + + + 9 + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 21 + 18 + + + + + + + + + 0 + 0 + + + + icn + + + + + + + retweeted + + + + + + + Qt::Horizontal + + + + 222 + 20 + + + + + + + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + REPLY + + + + :/images/reply.png:/images/reply.png + + + Qt::ToolButtonTextBesideIcon + + + + + + + 1 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + REPUBLISH + + + + :/images/retweet.png:/images/retweet.png + + + Qt::ToolButtonTextBesideIcon + + + + + + + 1 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + LIKE + + + + :/images/like.png:/images/like.png + + + Qt::ToolButtonTextBesideIcon + + + + + + + 1 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + SHOW + + + + :/images/external-link.svg:/images/external-link.svg + + + Qt::ToolButtonTextBesideIcon + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + + + Qt::Horizontal + + + + 104 + 20 + + + + + + + + FOLLOW + + + + :/images/invite.png:/images/invite.png + + + + 16 + 16 + + + + Qt::ToolButtonTextBesideIcon + + + + + + + for response statistics + + + + + + + Qt::Horizontal + + + + 104 + 20 + + + + + + + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Sidler</span></p></body></html> + + + + + + + <html><head/><body><p><span style=" color:#555753;">@sidler_here</span></p></body></html> + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 10 + 19 + + + + + + + + <html><head/><body><p><span style=" color:#2e3436;">· Apr 13 ·</span></p></body></html> + + + + + + + Qt::Horizontal + + + + 64 + 18 + + + + + + + + + + + + + + 0 + 0 + + + + + 50 + 50 + + + + <html><head/><body><p>Head</p><p>Shot</p></body></html> + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Minimum + + + + 5 + 20 + + + + + + + + Qt::Vertical + + + + + + + + 0 + 0 + + + + + 1 + 0 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Minimum + + + + 5 + 20 + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + + + <html><head/><body><p><span style=" color:#555753;">Replying to @sidler</span></p></body></html> + + + + + + + Qt::Horizontal + + + + 215 + 18 + + + + + + + + + + + + + + + + + + PulseMessage + QWidget +
gui/TheWire/PulseMessage.h
+ 1 +
+
+ + + + +
diff --git a/retroshare-gui/src/gui/PhotoShare/AddCommentDialog.cpp b/retroshare-gui/src/gui/TheWire/PulseReplySeperator.cpp similarity index 71% rename from retroshare-gui/src/gui/PhotoShare/AddCommentDialog.cpp rename to retroshare-gui/src/gui/TheWire/PulseReplySeperator.cpp index 7481d4a47..e9bf0d3e9 100644 --- a/retroshare-gui/src/gui/PhotoShare/AddCommentDialog.cpp +++ b/retroshare-gui/src/gui/TheWire/PulseReplySeperator.cpp @@ -1,39 +1,32 @@ -/******************************************************************************* - * retroshare-gui/src/gui/PhotoShare/AddCommentDialog.cpp * - * * - * Copyright (C) 2018 by Retroshare Team * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Affero General Public License as * - * published by the Free Software Foundation, either version 3 of the * - * License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Affero General Public License for more details. * - * * - * You should have received a copy of the GNU Affero General Public License * - * along with this program. If not, see . * - * * - *******************************************************************************/ - -#include "AddCommentDialog.h" -#include "ui_AddCommentDialog.h" - -AddCommentDialog::AddCommentDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::AddCommentDialog) -{ - ui->setupUi(this); -} - -AddCommentDialog::~AddCommentDialog() -{ - delete ui; -} - -QString AddCommentDialog::getComment() const -{ - return ui->textEditAddComment->document()->toPlainText(); -} +/******************************************************************************* + * gui/TheWire/PulseReplySeperator.cpp * + * * + * Copyright (c) 2020-2020 Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include "PulseReplySeperator.h" + +/** Constructor */ + +PulseReplySeperator::PulseReplySeperator() +:PulseViewItem(NULL) +{ + setupUi(this); + setAttribute ( Qt::WA_DeleteOnClose, true ); +} + + diff --git a/retroshare-gui/src/gui/TheWire/PulseReplySeperator.h b/retroshare-gui/src/gui/TheWire/PulseReplySeperator.h new file mode 100644 index 000000000..08b236a0c --- /dev/null +++ b/retroshare-gui/src/gui/TheWire/PulseReplySeperator.h @@ -0,0 +1,35 @@ +/******************************************************************************* + * gui/TheWire/PulseReplySeperator.h * + * * + * Copyright (c) 2020-2020 Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#ifndef MRK_PULSE_REPLY_SEPERATOR_H +#define MRK_PULSE_REPLY_SEPERATOR_H + +#include "ui_PulseReplySeperator.h" +#include "PulseViewItem.h" + +class PulseReplySeperator : public PulseViewItem, private Ui::PulseReplySeperator +{ + Q_OBJECT + +public: + PulseReplySeperator(); +}; + +#endif diff --git a/retroshare-gui/src/gui/TheWire/PulseReplySeperator.ui b/retroshare-gui/src/gui/TheWire/PulseReplySeperator.ui new file mode 100644 index 000000000..4a605a219 --- /dev/null +++ b/retroshare-gui/src/gui/TheWire/PulseReplySeperator.ui @@ -0,0 +1,102 @@ + + + PulseReplySeperator + + + + 0 + 0 + 781 + 57 + + + + + 0 + 0 + + + + + 9 + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + QFrame#frame{border: 2px solid #CCCCCC; +background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #EEEEEE, stop: 1 #CCCCCC); +border-radius: 10px} + + + QFrame::StyledPanel + + + QFrame::Raised + + + 100 + + + + + + Qt::Vertical + + + + 20 + 3 + + + + + + + + + 0 + 0 + + + + + 0 + 5 + + + + 100 + + + Qt::Horizontal + + + + + + + + + + + + + diff --git a/retroshare-gui/src/gui/TheWire/PulseTopLevel.cpp b/retroshare-gui/src/gui/TheWire/PulseTopLevel.cpp new file mode 100644 index 000000000..c8e26d987 --- /dev/null +++ b/retroshare-gui/src/gui/TheWire/PulseTopLevel.cpp @@ -0,0 +1,155 @@ +/******************************************************************************* + * gui/TheWire/PulseTopLevel.cpp * + * * + * Copyright (c) 2020-2020 Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include +#include +#include +#include + +#include "PulseTopLevel.h" + +#include +#include + +/** Constructor */ + +PulseTopLevel::PulseTopLevel(PulseViewHolder *holder, RsWirePulseSPtr pulse) +:PulseDataItem(holder, pulse) +{ + setupUi(this); + setAttribute ( Qt::WA_DeleteOnClose, true ); + setup(); + + if (mPulse) { + showPulse(); + } + +} + +void PulseTopLevel::setup() +{ + connect(toolButton_viewGroup, SIGNAL(clicked()), this, SLOT(actionViewGroup())); + connect(toolButton_viewParent, SIGNAL(clicked()), this, SLOT(actionViewParent())); + connect(toolButton_follow, SIGNAL(clicked()), this, SLOT(actionFollow())); + connect(toolButton_followParent, SIGNAL(clicked()), this, SLOT(actionFollowParent())); + // connect(toolButton_rate, SIGNAL(clicked()), this, SLOT(rate())); + + connect(toolButton_reply, SIGNAL(clicked()), this, SLOT(actionReply())); + connect(toolButton_republish, SIGNAL(clicked()), this, SLOT(actionRepublish())); + connect(toolButton_like, SIGNAL(clicked()), this, SLOT(actionLike())); + connect(toolButton_view, SIGNAL(clicked()), this, SLOT(actionViewPulse())); +} + +void PulseTopLevel::setRefMessage(QString msg, uint32_t image_count) +{ + // This should never happen. + //widget_message->setRefMessage(msg, image_count); +} + +void PulseTopLevel::setMessage(RsWirePulseSPtr pulse) +{ + widget_message->setup(pulse); +} + +// Set UI elements. +void PulseTopLevel::setHeadshot(const QPixmap &pixmap) +{ + label_headshot->setPixmap(pixmap); +} + +void PulseTopLevel::setGroupNameString(QString name) +{ + label_groupName->setText("@" + name); +} + +void PulseTopLevel::setAuthorString(QString name) +{ + label_authorName->setText(BoldString(name)); +} + +void PulseTopLevel::setDateString(QString date) +{ + label_date->setText(date); +} + +void PulseTopLevel::setLikesString(QString likes) +{ + label_extra_likes->setText(BoldString(likes)); + label_likes->setText(likes); +} + +void PulseTopLevel::setRepublishesString(QString repub) +{ + label_extra_republishes->setText(BoldString(repub)); + label_republishes->setText(repub); +} + +void PulseTopLevel::setRepliesString(QString reply) +{ + label_extra_replies->setText(BoldString(reply)); + label_replies->setText(reply); +} + +void PulseTopLevel::setPulseStatus(PulseStatus status) +{ + widget_replies->setVisible(true); // this is only reachable if we have ORIG so show always. + widget_actions->setVisible(status == PulseStatus::FULL); + widget_actionsFollow->setVisible(status == PulseStatus::UNSUBSCRIBED); +} + +void PulseTopLevel::setReferenceString(QString ref) +{ + if (ref.size() == 0) + { + widget_prefix->setVisible(false); + } + else + { + label_reference->setText(ref); + + // set ref icon + if (mPulse->mPulseType & WIRE_PULSE_TYPE_REPUBLISH) { + label_reficon->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/retweet.png")); + } else { + label_reficon->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/reply.png")); + } + } + + if (mPulse->mRefGroupPtr) { + if (mPulse->mRefGroupPtr->mMeta.mSubscribeFlags & + (GXS_SERV::GROUP_SUBSCRIBE_ADMIN | + GXS_SERV::GROUP_SUBSCRIBE_SUBSCRIBED)) { + toolButton_viewParent->setVisible(true); + toolButton_followParent->setVisible(false); + } else { + toolButton_viewParent->setVisible(false); + toolButton_followParent->setVisible(true); + } + } else { + toolButton_viewParent->setVisible(false); + toolButton_followParent->setVisible(false); + } +} + +void PulseTopLevel::mousePressEvent(QMouseEvent *event) +{ +} + + diff --git a/retroshare-gui/src/gui/PhotoShare/AlbumCreateDialog.h b/retroshare-gui/src/gui/TheWire/PulseTopLevel.h similarity index 52% rename from retroshare-gui/src/gui/PhotoShare/AlbumCreateDialog.h rename to retroshare-gui/src/gui/TheWire/PulseTopLevel.h index 41b0896ba..c04550023 100644 --- a/retroshare-gui/src/gui/PhotoShare/AlbumCreateDialog.h +++ b/retroshare-gui/src/gui/TheWire/PulseTopLevel.h @@ -1,7 +1,7 @@ /******************************************************************************* - * retroshare-gui/src/gui/PhotoShare/AlbumCreateDialog.h * + * gui/TheWire/PulseTopLevel.h * * * - * Copyright (C) 2018 by Retroshare Team * + * Copyright (c) 2020-2020 Robert Fernie * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Affero General Public License as * @@ -18,54 +18,48 @@ * * *******************************************************************************/ -#ifndef ALBUMCREATEDIALOG_H -#define ALBUMCREATEDIALOG_H +#ifndef MRK_PULSE_TOP_LEVEL_H +#define MRK_PULSE_TOP_LEVEL_H -#include -#include "util/TokenQueue.h" -#include "retroshare/rsphoto.h" -#include "retroshare/rsphoto.h" -#include "PhotoShareItemHolder.h" -#include "PhotoItem.h" -#include "PhotoDrop.h" +#include "ui_PulseTopLevel.h" -namespace Ui { - class AlbumCreateDialog; -} +#include "PulseViewItem.h" +#include - -class AlbumCreateDialog : public QDialog, public PhotoShareItemHolder +class PulseTopLevel : public PulseDataItem, private Ui::PulseTopLevel { - Q_OBJECT + Q_OBJECT public: - explicit AlbumCreateDialog(TokenQueue* photoQueue, RsPhoto* rs_photo, QWidget *parent = 0); - ~AlbumCreateDialog(); - - void notifySelection(PhotoShareItem* selection); - + PulseTopLevel(PulseViewHolder *holder, RsWirePulseSPtr pulse); -private slots: - void publishAlbum(); - void publishPhotos(); - void addAlbumThumbnail(); - void changePage(); - void backPage(); - -private: +protected: + void setup(); - bool getAlbumThumbnail(RsPhotoThumbnail &nail); -private: - Ui::AlbumCreateDialog *ui; +// PulseDataInterface =========== + // Group + virtual void setHeadshot(const QPixmap &pixmap) override; + virtual void setGroupNameString(QString name) override; + virtual void setAuthorString(QString name) override; - TokenQueue* mPhotoQueue; - RsPhoto* mRsPhoto; - QPixmap mThumbNail; - PhotoDrop* mPhotoDrop; - PhotoItem* mPhotoSelected; + // Msg + virtual void setRefMessage(QString msg, uint32_t image_count) override; + virtual void setMessage(RsWirePulseSPtr pulse) override; + virtual void setDateString(QString date) override; + + // Refs + virtual void setLikesString(QString likes) override; + virtual void setRepublishesString(QString repub) override; + virtual void setRepliesString(QString reply) override; + + // + virtual void setReferenceString(QString ref) override; + virtual void setPulseStatus(PulseStatus status) override; +// PulseDataInterface =========== + +protected: + void mousePressEvent(QMouseEvent *event); }; - - -#endif // ALBUMCREATEDIALOG_H +#endif diff --git a/retroshare-gui/src/gui/TheWire/PulseTopLevel.ui b/retroshare-gui/src/gui/TheWire/PulseTopLevel.ui new file mode 100644 index 000000000..f3365825a --- /dev/null +++ b/retroshare-gui/src/gui/TheWire/PulseTopLevel.ui @@ -0,0 +1,785 @@ + + + PulseTopLevel + + + + 0 + 0 + 732 + 551 + + + + + 0 + 0 + + + + + 9 + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 21 + 18 + + + + + + + + + 0 + 0 + + + + + 20 + 20 + + + + + + + :/images/reply.png + + + true + + + + + + + retweeted + + + + + + + open Parent Pulse + + + SHOW PARENT + + + + :/images/external-link.svg:/images/external-link.svg + + + + 24 + 24 + + + + + + + + follow Parent Group + + + ... + + + + :/images/invite.png:/images/invite.png + + + + 24 + 24 + + + + + + + + Qt::Horizontal + + + + 537 + 20 + + + + + + + + + + + + 0 + 0 + + + + + 0 + 60 + + + + + + + Qt::Vertical + + + QSizePolicy::Minimum + + + + 20 + 10 + + + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + <html><head/><body><p><span style=" color:#555753;">@sidler_here</span></p></body></html> + + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + <html><head/><body><p><span style=" font-weight:600;">Sidler</span></p></body></html> + + + + + + + Qt::Horizontal + + + + 518 + 58 + + + + + + + + + 0 + 0 + + + + + 50 + 50 + + + + <html><head/><body><p>Head</p><p>Shot</p></body></html> + + + + + + + + 0 + 25 + + + + open Group + + + SHOW GROUP + + + + :/images/external-link.svg:/images/external-link.svg + + + + 24 + 24 + + + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + 16777215 + 16777215 + + + + + + + <html><head/><body><p><span style=" color:#2e3436;">3:58 AM · Apr 13, 2020 ·</span></p></body></html> + + + + + + + Qt::Horizontal + + + + 505 + 20 + + + + + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">1.2K</span></p></body></html> + + + + + + + <html><head/><body><p><span style=" color:#2e3436;">Replies</span></p></body></html> + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 40 + 20 + + + + + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">1.2K</span></p></body></html> + + + + + + + <html><head/><body><p><span style=" color:#2e3436;">Republishes</span></p></body></html> + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 40 + 20 + + + + + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">21.3K</span></p></body></html> + + + + + + + <html><head/><body><p><span style=" color:#2e3436;">Likes</span></p></body></html> + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + REPLY + + + + :/images/reply.png:/images/reply.png + + + Qt::ToolButtonTextBesideIcon + + + + + + + 1 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + REPUBLISH + + + + :/images/retweet.png:/images/retweet.png + + + Qt::ToolButtonTextBesideIcon + + + + + + + 1 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + LIKE + + + + :/images/like.png:/images/like.png + + + Qt::ToolButtonTextBesideIcon + + + + + + + 1 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + SHOW + + + + :/images/external-link.svg:/images/external-link.svg + + + Qt::ToolButtonTextBesideIcon + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + + + Qt::Horizontal + + + + 298 + 20 + + + + + + + + FOLLOW + + + + :/images/invite.png:/images/invite.png + + + Qt::ToolButtonTextBesideIcon + + + + + + + Qt::Horizontal + + + + 297 + 20 + + + + + + + + + + + + + + + PulseMessage + QWidget +
gui/TheWire/PulseMessage.h
+ 1 +
+
+ + + + +
diff --git a/retroshare-gui/src/gui/TheWire/PulseViewGroup.cpp b/retroshare-gui/src/gui/TheWire/PulseViewGroup.cpp new file mode 100644 index 000000000..27b085a01 --- /dev/null +++ b/retroshare-gui/src/gui/TheWire/PulseViewGroup.cpp @@ -0,0 +1,108 @@ +/******************************************************************************* + * gui/TheWire/PulseViewGroup.cpp * + * * + * Copyright (c) 2012-2020 Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include +#include +#include +#include + +#include "PulseViewGroup.h" + +#include "gui/gxs/GxsIdDetails.h" +#include "util/DateTime.h" + +/** Constructor */ + +PulseViewGroup::PulseViewGroup(PulseViewHolder *holder, RsWireGroupSPtr group) +:PulseViewItem(holder), mGroup(group) +{ + setupUi(this); + setAttribute ( Qt::WA_DeleteOnClose, true ); + setup(); +} + +void PulseViewGroup::setup() +{ + if (mGroup) { + connect(toolButton_follow, SIGNAL(clicked()), this, SLOT(actionFollow())); + + label_groupName->setText("@" + QString::fromStdString(mGroup->mMeta.mGroupName)); + label_authorName->setText(BoldString(QString::fromStdString(mGroup->mMeta.mAuthorId.toStdString()))); + label_date->setText(DateTime::formatDateTime(mGroup->mMeta.mPublishTs)); + label_tagline->setText(QString::fromStdString(mGroup->mTagline)); + label_location->setText(QString::fromStdString(mGroup->mLocation)); + + // need to draw mGroup->mMasthead, as background to headshot. + // TODO frame_headerBackground->setBackground() + + if (mGroup->mHeadshot.mData) + { + QPixmap pixmap; + if (GxsIdDetails::loadPixmapFromData( + mGroup->mHeadshot.mData, + mGroup->mHeadshot.mSize, + pixmap,GxsIdDetails::ORIGINAL)) + { + pixmap = pixmap.scaled(50,50); + label_headshot->setPixmap(pixmap); + } + } + else + { + // default. + QPixmap pixmap = FilesDefs::getPixmapFromQtResourcePath(":/icons/png/posted.png").scaled(50,50); + label_headshot->setPixmap(pixmap); + } + + if (mGroup->mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_SUBSCRIBED) + { + uint32_t pulses = mGroup->mGroupPulses + mGroup->mGroupReplies; + uint32_t replies = mGroup->mRefReplies; + uint32_t republishes = mGroup->mRefRepublishes; + uint32_t likes = mGroup->mRefLikes; + + label_extra_pulses->setText(BoldString(ToNumberUnits(pulses))); + label_extra_replies->setText(BoldString(ToNumberUnits(replies))); + label_extra_republishes->setText(BoldString(ToNumberUnits(republishes))); + label_extra_likes->setText(BoldString(ToNumberUnits(likes))); + + // hide follow. + widget_actions->setVisible(false); + } + else + { + // hide stats. + widget_replies->setVisible(false); + } + } +} + +void PulseViewGroup::actionFollow() +{ + RsGxsGroupId groupId = mGroup->mMeta.mGroupId; + std::cerr << "PulseViewGroup::actionFollow() following "; + std::cerr << groupId; + std::cerr << std::endl; + + if (mHolder) { + mHolder->PVHfollow(groupId); + } +} + diff --git a/retroshare-gui/src/gui/PhotoShare/PhotoCommentItem.h b/retroshare-gui/src/gui/TheWire/PulseViewGroup.h similarity index 69% rename from retroshare-gui/src/gui/PhotoShare/PhotoCommentItem.h rename to retroshare-gui/src/gui/TheWire/PulseViewGroup.h index eaf95fae7..4d00090ca 100644 --- a/retroshare-gui/src/gui/PhotoShare/PhotoCommentItem.h +++ b/retroshare-gui/src/gui/TheWire/PulseViewGroup.h @@ -1,7 +1,7 @@ /******************************************************************************* - * retroshare-gui/src/gui/PhotoShare/PhotoCommentItem.h * + * gui/TheWire/PulseViewGroup.h * * * - * Copyright (C) 2018 by Retroshare Team * + * Copyright (c) 2020-2020 Robert Fernie * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Affero General Public License as * @@ -18,32 +18,30 @@ * * *******************************************************************************/ -#ifndef PHOTOCOMMENTITEM_H -#define PHOTOCOMMENTITEM_H +#ifndef MRK_PULSE_VIEW_GROUP_H +#define MRK_PULSE_VIEW_GROUP_H -#include -#include "retroshare/rsphoto.h" +#include "ui_PulseViewGroup.h" -namespace Ui { - class PhotoCommentItem; -} +#include "PulseViewItem.h" +#include -class PhotoCommentItem : public QWidget +class PulseViewGroup : public PulseViewItem, private Ui::PulseViewGroup { - Q_OBJECT + Q_OBJECT public: - explicit PhotoCommentItem(const RsPhotoComment& comment, QWidget *parent = 0); - ~PhotoCommentItem(); + PulseViewGroup(PulseViewHolder *holder, RsWireGroupSPtr group); - const RsPhotoComment& getComment(); +private slots: + void actionFollow(); -private: +protected: + void setup(); - void setUp(); -private: - Ui::PhotoCommentItem *ui; - RsPhotoComment mComment; + +protected: + RsWireGroupSPtr mGroup; }; -#endif // PHOTOCOMMENTITEM_H +#endif diff --git a/retroshare-gui/src/gui/TheWire/PulseViewGroup.ui b/retroshare-gui/src/gui/TheWire/PulseViewGroup.ui new file mode 100644 index 000000000..c2f209a19 --- /dev/null +++ b/retroshare-gui/src/gui/TheWire/PulseViewGroup.ui @@ -0,0 +1,546 @@ + + + PulseViewGroup + + + + 0 + 0 + 745 + 483 + + + + + 0 + 0 + + + + + 9 + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + Qt::Horizontal + + + + 283 + 20 + + + + + + + + + 0 + 0 + + + + + 100 + 100 + + + + headshot + + + + + + + Qt::Horizontal + + + + 23 + 20 + + + + + + + + + + + + 0 + 0 + + + + + 0 + 60 + + + + + + + Qt::Horizontal + + + + 518 + 58 + + + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + <html><head/><body><p><span style=" color:#555753;">@sidler_here</span></p></body></html> + + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + <html><head/><body><p><span style=" font-weight:600;">Sidler</span></p></body></html> + + + + + + + Qt::Vertical + + + QSizePolicy::Minimum + + + + 20 + 10 + + + + + + + + + + + + 0 + 0 + + + + + 0 + 50 + + + + + 16777215 + 16777215 + + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + <html><head/><body><p><span style=" color:#2e3436;">3:58 AM · Apr 13, 2020 ·</span></p></body></html> + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + Location + + + + + + + Qt::Horizontal + + + + 2000 + 20 + + + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + Tag Line + + + + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">1.2K</span></p></body></html> + + + + + + + <html><head/><body><p><span style=" color:#2e3436;">Pulses</span></p></body></html> + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 40 + 20 + + + + + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">1.2K</span></p></body></html> + + + + + + + <html><head/><body><p><span style=" color:#2e3436;">Replies</span></p></body></html> + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 40 + 20 + + + + + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">1.2K</span></p></body></html> + + + + + + + <html><head/><body><p><span style=" color:#2e3436;">Republishes</span></p></body></html> + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 40 + 20 + + + + + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">21.3K</span></p></body></html> + + + + + + + <html><head/><body><p><span style=" color:#2e3436;">Likes</span></p></body></html> + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + + + Qt::Horizontal + + + + 298 + 20 + + + + + + + + FOLLOW + + + + :/images/invite.png:/images/invite.png + + + Qt::ToolButtonTextBesideIcon + + + + + + + Qt::Horizontal + + + + 297 + 20 + + + + + + + + + + + + + + + + + diff --git a/retroshare-gui/src/gui/TheWire/PulseViewItem.cpp b/retroshare-gui/src/gui/TheWire/PulseViewItem.cpp new file mode 100644 index 000000000..2025baf85 --- /dev/null +++ b/retroshare-gui/src/gui/TheWire/PulseViewItem.cpp @@ -0,0 +1,464 @@ +/******************************************************************************* + * gui/TheWire/PulseViewItem.cpp * + * * + * Copyright (c) 2012-2020 Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include +#include +#include +#include + +#include "PulseViewItem.h" + +#include "gui/gxs/GxsIdDetails.h" +#include "util/DateTime.h" + +/** Constructor */ + +PulseViewItem::PulseViewItem(PulseViewHolder *holder) +:QWidget(NULL), mHolder(holder) +{ +} + + +PulseDataItem::PulseDataItem(PulseViewHolder *holder, RsWirePulseSPtr pulse) +:PulseViewItem(holder), mPulse(pulse) +{ +} + +void PulseDataItem::actionReply() +{ + std::cerr << "PulseDataItem::actionReply()"; + std::cerr << std::endl; + + if (mHolder) { + if (mPulse->mPulseType & WIRE_PULSE_TYPE_REFERENCE) { + std::cerr << "PulseDataItem::actionReply() NO ACTION FOR REF"; + std::cerr << std::endl; + return; + } + mHolder->PVHreply(mPulse->mMeta.mGroupId, mPulse->mMeta.mMsgId); + } +} + +void PulseDataItem::actionRepublish() +{ + std::cerr << "PulseDataItem::actionRepublish()"; + std::cerr << std::endl; + + if (mHolder) { + if (mPulse->mPulseType & WIRE_PULSE_TYPE_REFERENCE) { + std::cerr << "PulseDataItem::actionRepublish() NO ACTION FOR REF"; + std::cerr << std::endl; + return; + } + mHolder->PVHrepublish(mPulse->mMeta.mGroupId, mPulse->mMeta.mMsgId); + } +} + +void PulseDataItem::actionLike() +{ + std::cerr << "PulseDataItem::actionLike()"; + std::cerr << std::endl; + + if (mHolder) { + if (mPulse->mPulseType & WIRE_PULSE_TYPE_REFERENCE) { + std::cerr << "PulseDataItem::actionLike() NO ACTION FOR REF"; + std::cerr << std::endl; + return; + } + mHolder->PVHlike(mPulse->mMeta.mGroupId, mPulse->mMeta.mMsgId); + } +} + +void PulseDataItem::actionViewGroup() +{ + std::cerr << "PulseDataItem::actionViewGroup()"; + std::cerr << std::endl; + + RsGxsGroupId groupId; + + if (mPulse) { + if ((mPulse->mPulseType & WIRE_PULSE_TYPE_ORIGINAL) || + (mPulse->mPulseType & WIRE_PULSE_TYPE_RESPONSE)) + { + /* use pulse group */ + groupId = mPulse->mMeta.mGroupId; + } + else + { + /* IS REF use pulse group */ + groupId = mPulse->mRefGroupId; + } + } + + if (mHolder) { + mHolder->PVHviewGroup(groupId); + } +} + +void PulseDataItem::actionViewParent() +{ + std::cerr << "PulseDataItem::actionViewParent()"; + std::cerr << std::endl; + + // TODO + RsGxsGroupId groupId; + RsGxsMessageId msgId; + + if (mPulse) { + if (mPulse->mPulseType & WIRE_PULSE_TYPE_ORIGINAL) + { + std::cerr << "PulseDataItem::actionViewParent() Error ORIGINAL no parent"; + std::cerr << std::endl; + } + else if (mPulse->mPulseType & WIRE_PULSE_TYPE_RESPONSE) + { + /* mRefs refer to parent */ + groupId = mPulse->mRefGroupId; + msgId = mPulse->mRefOrigMsgId; + } + else + { + /* type = REF, group / thread ref to parent */ + groupId = mPulse->mMeta.mGroupId; + msgId = mPulse->mMeta.mThreadId; + } + } + + if (mHolder) { + mHolder->PVHviewPulse(groupId, msgId); + } +} + +void PulseDataItem::actionViewPulse() +{ + std::cerr << "PulseDataItem::actionViewPulse()"; + std::cerr << std::endl; + + // TODO + RsGxsGroupId groupId; + RsGxsMessageId msgId; + + if (mPulse) { + if ((mPulse->mPulseType & WIRE_PULSE_TYPE_ORIGINAL) || + (mPulse->mPulseType & WIRE_PULSE_TYPE_RESPONSE)) + { + groupId = mPulse->mMeta.mGroupId; + msgId = mPulse->mMeta.mOrigMsgId; + } + else + { + /* type = REF, mRefs link to message */ + std::cerr << "PulseDataItem::actionViewPulse() REF unlikely retrievable"; + std::cerr << std::endl; + + groupId = mPulse->mRefGroupId; + msgId = mPulse->mRefOrigMsgId; + } + } + + if (mHolder) { + mHolder->PVHviewPulse(groupId, msgId); + } +} + +void PulseDataItem::actionFollow() +{ + std::cerr << "PulseDataItem::actionFollow()"; + std::cerr << std::endl; + + RsGxsGroupId groupId; + if (mPulse->mPulseType & WIRE_PULSE_TYPE_REFERENCE) { + std::cerr << "PulseDataItem::actionFollow() REF following Replier: "; + std::cerr << mPulse->mRefGroupId; + std::cerr << std::endl; + groupId = mPulse->mRefGroupId; + } else { + std::cerr << "PulseDataItem::actionFollow() RESPONSE following Group: "; + std::cerr << mPulse->mMeta.mGroupId; + std::cerr << std::endl; + groupId = mPulse->mMeta.mGroupId; + } + + if (mHolder) { + mHolder->PVHfollow(groupId); + } +} + +void PulseDataItem::actionFollowParent() +{ + std::cerr << "PulseDataItem::actionFollowParent()"; + std::cerr << std::endl; + + RsGxsGroupId groupId; + if (mPulse->mPulseType & WIRE_PULSE_TYPE_REFERENCE) { + std::cerr << "PulseDataItem::actionFollowParent() REF following Group: "; + std::cerr << mPulse->mMeta.mGroupId; + std::cerr << std::endl; + groupId = mPulse->mMeta.mGroupId; + } else { + std::cerr << "PulseDataItem::actionFollowParent() RESPONSE following RefGroup: "; + std::cerr << mPulse->mRefGroupId; + std::cerr << std::endl; + groupId = mPulse->mRefGroupId; + } + + if (mHolder) { + mHolder->PVHfollow(groupId); + } +} + +void PulseDataItem::actionRate() +{ + std::cerr << "PulseDataItem::actionRate()"; + std::cerr << std::endl; + + // TODO + RsGxsId authorId; + + if (mHolder) { + mHolder->PVHrate(authorId); + } +} + + + +void PulseDataItem::showPulse() +{ + std::cerr << "PulseDataItem::showPulse()"; + std::cerr << std::endl; + if (!mPulse) { + std::cerr << "PulseDataItem::showPulse() PULSE invalid - skipping"; + std::cerr << std::endl; + return; + } + + /* 3 Modes: + * ORIGINAL + * RESPONSE + * REFERENCE + * + * ORIG / RESPONSE are similar. + */ + + if (mPulse->mPulseType & WIRE_PULSE_TYPE_REFERENCE) + { + + // Group + bool headshotOkay = false; + if (mPulse->mRefGroupPtr) { + if (mPulse->mRefGroupPtr->mHeadshot.mData) + { + QPixmap pixmap; + if (GxsIdDetails::loadPixmapFromData( + mPulse->mRefGroupPtr->mHeadshot.mData, + mPulse->mRefGroupPtr->mHeadshot.mSize, + pixmap,GxsIdDetails::ORIGINAL)) + { + headshotOkay = true; + pixmap = pixmap.scaled(50,50); + setHeadshot(pixmap); + } + } + } + + if (!headshotOkay) + { + // default. + QPixmap pixmap = FilesDefs::getPixmapFromQtResourcePath(":/icons/png/posted.png").scaled(50,50); + setHeadshot(pixmap); + } + + // Group + setGroupName(mPulse->mRefGroupName); + setAuthor(mPulse->mRefAuthorId.toStdString()); + + // Msg + setRefMessage(QString::fromStdString(mPulse->mRefPulseText), mPulse->mRefImageCount); + setDate(mPulse->mRefPublishTs); + + // Workout Pulse status for Stats/Follow/Msgs. + // Its a REF so cannot be FULL. + PulseStatus status = PulseStatus::REF_MSG; + if (mPulse->mRefGroupPtr) { + // bitwise comparisons. + if (mPulse->mRefGroupPtr->mMeta.mSubscribeFlags & + (GXS_SERV::GROUP_SUBSCRIBE_ADMIN | + GXS_SERV::GROUP_SUBSCRIBE_SUBSCRIBED)) { + status = PulseStatus::REF_MSG; + } else { + status = PulseStatus::UNSUBSCRIBED; + } + } else { + status = PulseStatus::NO_GROUP; + } + + setPulseStatus(status); + + if (mPulse->mGroupPtr) { + setReference(mPulse->mPulseType & WIRE_PULSE_RESPONSE_MASK, mPulse->mMeta.mGroupId, mPulse->mGroupPtr->mMeta.mGroupName); + } else { + setReference(mPulse->mPulseType & WIRE_PULSE_RESPONSE_MASK, mPulse->mMeta.mGroupId, "UNKNOWN"); + } + + } + else // ORIG / RESPONSE. + { + + // Group + bool headshotOkay = false; + if (mPulse->mGroupPtr) { + setGroupName(mPulse->mGroupPtr->mMeta.mGroupName); + + if (mPulse->mGroupPtr->mHeadshot.mData) + { + QPixmap pixmap; + if (GxsIdDetails::loadPixmapFromData( + mPulse->mGroupPtr->mHeadshot.mData, + mPulse->mGroupPtr->mHeadshot.mSize, + pixmap,GxsIdDetails::ORIGINAL)) + { + headshotOkay = true; + pixmap = pixmap.scaled(50,50); + setHeadshot(pixmap); + } + } + } else { + setGroupName("GroupName UNKNOWN"); + } + + if (!headshotOkay) + { + // default. + QPixmap pixmap = FilesDefs::getPixmapFromQtResourcePath(":/icons/png/posted.png").scaled(50,50); + setHeadshot(pixmap); // QPixmap(":/icons/png/posted.png")); + } + + setAuthor(mPulse->mMeta.mAuthorId.toStdString()); + + // Msg + setMessage(mPulse); + setDate(mPulse->mMeta.mPublishTs); + + // Possible to have ORIG and be UNSUBSCRIBED. + PulseStatus status = PulseStatus::FULL; + if (mPulse->mGroupPtr) { + // bitwise comparisons. + if (mPulse->mGroupPtr->mMeta.mSubscribeFlags & + (GXS_SERV::GROUP_SUBSCRIBE_ADMIN | + GXS_SERV::GROUP_SUBSCRIBE_SUBSCRIBED)) { + status = PulseStatus::FULL; + } else { + status = PulseStatus::UNSUBSCRIBED; + } + } + setPulseStatus(status); + setLikes(mPulse->mLikes.size()); + setReplies(mPulse->mReplies.size()); + setRepublishes(mPulse->mRepublishes.size()); + + if (mPulse->mPulseType & WIRE_PULSE_TYPE_RESPONSE) + { + setReference(mPulse->mPulseType & WIRE_PULSE_RESPONSE_MASK, mPulse->mRefGroupId, mPulse->mRefGroupName); + } + else + { + // NO Parent, so only 0 is important. + setReference(0, mPulse->mRefGroupId, mPulse->mRefGroupName); + } + } +} + +void PulseDataItem::setGroupName(std::string name) +{ + setGroupNameString(QString::fromStdString(name)); +} + +void PulseDataItem::setAuthor(std::string name) +{ + setAuthorString(QString::fromStdString(name)); +} + +void PulseDataItem::setDate(rstime_t date) +{ + // could be more intelligent. + // eg. 3 Hr ago, if recent. + setDateString(DateTime::formatDateTime(date)); +} + +void PulseDataItem::setLikes(uint32_t count) +{ + setLikesString(ToNumberUnits(count)); +} + +void PulseDataItem::setRepublishes(uint32_t count) +{ + setRepublishesString(ToNumberUnits(count)); +} + +void PulseDataItem::setReplies(uint32_t count) +{ + std::cerr << "PulseDataItem::setReplies(" << count << ")"; + std::cerr << std::endl; + setRepliesString(ToNumberUnits(count)); +} + +void PulseDataItem::setReference(uint32_t response_type, RsGxsGroupId groupId, std::string groupName) +{ + QString ref; + if (response_type == WIRE_PULSE_TYPE_REPLY) { + ref = "In reply to @" + QString::fromStdString(groupName); + } + else if (response_type == WIRE_PULSE_TYPE_REPUBLISH) { + ref = "retweeting @" + QString::fromStdString(groupName); + } + else if (response_type == WIRE_PULSE_TYPE_LIKE) { + ref = "liking @" + QString::fromStdString(groupName); + } + + setReferenceString(ref); +} + +// Utils. +QString BoldString(QString msg) +{ + QString output = "

"; + output += msg; + output += "

"; + return output; +} + +QString ToNumberUnits(uint32_t count) +{ + QString ans; + if (count > 1000000) + { + ans.sprintf("%6.2fm", count / 1000000.0); + } + else if (count > 1000) + { + ans.sprintf("%6.2fk", count / 1000.0); + } + else + { + ans.sprintf("%6d", count); + } + return ans; +} + diff --git a/retroshare-gui/src/gui/TheWire/PulseViewItem.h b/retroshare-gui/src/gui/TheWire/PulseViewItem.h new file mode 100644 index 000000000..bee96f14b --- /dev/null +++ b/retroshare-gui/src/gui/TheWire/PulseViewItem.h @@ -0,0 +1,150 @@ +/******************************************************************************* + * gui/TheWire/PulseViewItem.h * + * * + * Copyright (c) 2020-2020 Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#ifndef MRK_PULSE_VIEW_ITEM_H +#define MRK_PULSE_VIEW_ITEM_H + +#include + +#include + +class PulseViewItem; + +class PulseViewHolder +{ +public: + virtual ~PulseViewHolder() {} + + // Actions. + virtual void PVHreply(const RsGxsGroupId &groupId, const RsGxsMessageId &msgId) = 0; + virtual void PVHrepublish(const RsGxsGroupId &groupId, const RsGxsMessageId &msgId) = 0; + virtual void PVHlike(const RsGxsGroupId &groupId, const RsGxsMessageId &msgId) = 0; + + virtual void PVHviewGroup(const RsGxsGroupId &groupId) = 0; + virtual void PVHviewPulse(const RsGxsGroupId &groupId, const RsGxsMessageId &msgId) = 0; + virtual void PVHviewReply(const RsGxsGroupId &groupId, const RsGxsMessageId &msgId) = 0; + + virtual void PVHfollow(const RsGxsGroupId &groupId) = 0; + virtual void PVHrate(const RsGxsId &authorId) = 0; +}; + +class PulseDataInterface +{ +public: + virtual ~PulseDataInterface() {} + + enum class PulseStatus { + FULL, // have Msg + Group: Show Stats + UNSUBSCRIBED, // Ref + unsubscribed to Group: Show Follow + NO_GROUP, // Ref Msg, unknown group: Show Missing Group. + REF_MSG // Subscribed, only Ref Msg: Show Missing Msg. + }; + +protected: + // Group + virtual void setHeadshot(const QPixmap &pixmap) = 0; + virtual void setGroupNameString(QString name) = 0; + virtual void setAuthorString(QString name) = 0; + + // Msg + virtual void setRefMessage(QString msg, uint32_t image_count) = 0; + virtual void setMessage(RsWirePulseSPtr pulse) = 0; + virtual void setDateString(QString date) = 0; + + // Refs + virtual void setLikesString(QString likes) = 0; + virtual void setRepublishesString(QString repub) = 0; + virtual void setRepliesString(QString reply) = 0; + + // + virtual void setReferenceString(QString ref) = 0; + virtual void setPulseStatus(PulseStatus status) = 0; +}; + + + +class PulseViewItem : public QWidget +{ + Q_OBJECT + +public: + PulseViewItem(PulseViewHolder *holder); + +protected: + PulseViewHolder *mHolder; +}; + + +class PulseDataItem : public PulseViewItem, public PulseDataInterface +{ + Q_OBJECT + +public: + PulseDataItem(PulseViewHolder *holder, RsWirePulseSPtr pulse); + + +private slots: + + // Action interfaces -------------------------- + void actionReply(); + void actionRepublish(); + void actionLike(); + + void actionViewGroup(); + void actionViewParent(); + void actionViewPulse(); + + void actionFollow(); + void actionFollowParent(); + void actionRate(); + // Action interfaces -------------------------- + +protected: + + // top-level set data onto UI. + virtual void showPulse(); + + // UI elements. + // Group + void setGroupName(std::string name); + void setAuthor(std::string name); + + // Msg + void setDate(rstime_t date); + + // Refs + void setLikes(uint32_t count); + void setRepublishes(uint32_t count); + void setReplies(uint32_t count); + + // + void setReference(uint32_t flags, RsGxsGroupId groupId, std::string groupName); + + // DATA. + RsWirePulseSPtr mPulse; +}; + + +// utilities. +QString BoldString(QString input); +QString ToNumberUnits(uint32_t count); + + +#endif diff --git a/retroshare-gui/src/gui/TheWire/TheWire_images.qrc b/retroshare-gui/src/gui/TheWire/TheWire_images.qrc new file mode 100644 index 000000000..2c344c6bd --- /dev/null +++ b/retroshare-gui/src/gui/TheWire/TheWire_images.qrc @@ -0,0 +1,11 @@ + + + images/compose.png + images/like.png + images/reply.png + images/retweet.png + images/link.svg + images/external-link.svg + images/invite.png + + diff --git a/retroshare-gui/src/gui/TheWire/WireDialog.cpp b/retroshare-gui/src/gui/TheWire/WireDialog.cpp index 90ce66ea1..06fa6ba6c 100644 --- a/retroshare-gui/src/gui/TheWire/WireDialog.cpp +++ b/retroshare-gui/src/gui/TheWire/WireDialog.cpp @@ -1,7 +1,7 @@ /******************************************************************************* * gui/TheWire/WireDialog.cpp * * * - * Copyright (c) 2012 Robert Fernie * + * Copyright (c) 2012-2020 Robert Fernie * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Affero General Public License as * @@ -20,95 +20,161 @@ #include "WireDialog.h" +#include "WireGroupDialog.h" +#include "WireGroupItem.h" + +#include "PulseViewGroup.h" +#include "PulseReplySeperator.h" + +#include "util/qtthreadsutils.h" + #include -#include +#include #include #include #include - -/****** - * #define PHOTO_DEBUG 1 - *****/ - +#include /**************************************************************** - * New Photo Display Widget. + * TheWire Display Widget. * - * This has two 'lists'. - * Top list shows Albums. - * Lower list is photos from the selected Album. - * - * Notes: - * Each Item will be an AlbumItem, which contains a thumbnail & random details. - * We will limit Items to < 100. With a 'Filter to see more message. - * - * Thumbnails will come from Service. - * Option to Share albums / pictures onward (if permissions allow). - * Option to Download the albums to a specified directory. (is this required if sharing an album?) - * - * Will introduce a FullScreen SlideShow later... first get basics happening. */ +#define GROUP_SET_ALL (0) +#define GROUP_SET_OWN (1) +#define GROUP_SET_SUBSCRIBED (2) +#define GROUP_SET_OTHERS (3) +// Future Extensions. +// #define GROUP_SET_AUTO (4) +// #define GROUP_SET_RECOMMENDED (5) +#define WIRE_TOKEN_TYPE_SUBSCRIBE_CHANGE 1 + /** Constructor */ WireDialog::WireDialog(QWidget *parent) -: MainPage(parent) +: MainPage(parent), mGroupSet(GROUP_SET_ALL) { ui.setupUi(this); mAddDialog = NULL; - mPulseSelected = NULL; + mGroupSelected = NULL; + mHistoryIndex = -1; - connect( ui.pushButton_Post, SIGNAL(clicked()), this, SLOT(OpenOrShowPulseAddDialog())); - //connect( ui.pushButton_Accounts, SIGNAL(clicked()), this, SLOT(OpenOrShowAccountDialog())); + connect( ui.toolButton_createAccount, SIGNAL(clicked()), this, SLOT(createGroup())); + connect( ui.toolButton_createPulse, SIGNAL(clicked()), this, SLOT(createPulse())); + connect( ui.toolButton_refresh, SIGNAL(clicked()), this, SLOT(refreshGroups())); + + connect(ui.comboBox_groupSet, SIGNAL(currentIndexChanged(int)), this, SLOT(selectGroupSet(int))); + connect(ui.comboBox_filterTime, SIGNAL(currentIndexChanged(int)), this, SLOT(selectFilterTime(int))); + + connect( ui.toolButton_back, SIGNAL(clicked()), this, SLOT(back())); + connect( ui.toolButton_forward, SIGNAL(clicked()), this, SLOT(forward())); + ui.toolButton_back->setEnabled(false); + ui.toolButton_forward->setEnabled(false); QTimer *timer = new QTimer(this); timer->connect(timer, SIGNAL(timeout()), this, SLOT(checkUpdate())); timer->start(1000); + /* setup TokenQueue */ + mWireQueue = new TokenQueue(rsWire->getTokenService(), this); + requestGroupData(); + + // just for testing + postTestTwitterView(); +} + +void WireDialog::refreshGroups() +{ + requestGroupData(); } -void WireDialog::notifySelection(PulseItem *item, int ptype) +void WireDialog::addGroup(QWidget *item) { - std::cerr << "WireDialog::notifySelection() from : " << ptype << " " << item; - std::cerr << std::endl; - - notifyPulseSelection(item); - -#if 0 - switch(ptype) - { - default: - case PHOTO_ITEM_TYPE_ALBUM: - notifyAlbumSelection(item); - break; - case PHOTO_ITEM_TYPE_PHOTO: - notifyPhotoSelection(item); - break; - } -#endif + QLayout *alayout = ui.scrollAreaWidgetContents_groups->layout(); + alayout->addWidget(item); } -void WireDialog::notifyPulseSelection(PulseItem *item) +bool WireDialog::setupPulseAddDialog() { - std::cerr << "WireDialog::notifyPulseSelection() from : " << item; + std::cerr << "WireDialog::setupPulseAddDialog()"; std::cerr << std::endl; - - if (mPulseSelected) + + if (!mAddDialog) { - std::cerr << "WireDialog::notifyPulseSelection() unselecting old one : " << mPulseSelected; - std::cerr << std::endl; - - mPulseSelected->setSelected(false); + mAddDialog = new PulseAddDialog(NULL); + mAddDialog->hide(); } - - mPulseSelected = item; + + mAddDialog->cleanup(); + + int idx = ui.groupChooser->currentIndex(); + if (idx < 0) { + std::cerr << "WireDialog::setupPulseAddDialog() ERROR GETTING AuthorId!"; + std::cerr << std::endl; + + QMessageBox::warning(this, tr("RetroShare"),tr("Please create or choose Wire Groupd first"), QMessageBox::Ok, QMessageBox::Ok); + return false; + } + + // publishing group. + RsWireGroup group = mOwnGroups[idx]; + mAddDialog->setGroup(group.mMeta.mGroupId); + + return true; +} + + +void WireDialog::subscribe(RsGxsGroupId &groupId) +{ + uint32_t token; + rsWire->subscribeToGroup(token, groupId, true); + mWireQueue->queueRequest(token, TOKENREQ_GROUPINFO, RS_TOKREQ_ANSTYPE_ACK, WIRE_TOKEN_TYPE_SUBSCRIBE_CHANGE); +} + +void WireDialog::unsubscribe(RsGxsGroupId &groupId) +{ + uint32_t token; + rsWire->subscribeToGroup(token, groupId, false); + mWireQueue->queueRequest(token, TOKENREQ_GROUPINFO, RS_TOKREQ_ANSTYPE_ACK, WIRE_TOKEN_TYPE_SUBSCRIBE_CHANGE); +} + +void WireDialog::notifyGroupSelection(WireGroupItem *item) +{ + std::cerr << "WireDialog::notifyGroupSelection() from : " << item; + std::cerr << std::endl; + + bool doSelection = true; + if (mGroupSelected) + { + std::cerr << "WireDialog::notifyGroupSelection() unselecting old one : " << mGroupSelected; + std::cerr << std::endl; + + mGroupSelected->setSelected(false); + if (mGroupSelected == item) + { + std::cerr << "WireDialog::notifyGroupSelection() current -> unselect"; + std::cerr << std::endl; + /* de-selection of current item */ + mGroupSelected = NULL; + doSelection = false; + } + } + + if (doSelection) + { + item->setSelected(true); + mGroupSelected = item; + } + + /* update display */ + showSelectedGroups(); } @@ -127,331 +193,792 @@ void WireDialog::checkUpdate() return; } - -/*************** New Photo Dialog ***************/ - -void WireDialog::OpenOrShowPulseAddDialog() +void WireDialog::createGroup() { - if (mAddDialog) + WireGroupDialog wireCreate(this); + wireCreate.exec(); +} + +void WireDialog::createPulse() +{ + if (!mAddDialog) { - mAddDialog->show(); + mAddDialog = new PulseAddDialog(NULL); + mAddDialog->hide(); + } + + int idx = ui.groupChooser->currentIndex(); + if (idx < 0) { + std::cerr << "WireDialog::createPulse() ERROR GETTING AuthorId!"; + std::cerr << std::endl; + + QMessageBox::warning(this, tr("RetroShare"),tr("Please create or choose Wire Groupd first"), QMessageBox::Ok, QMessageBox::Ok); + return; + } + + RsWireGroup group = mOwnGroups[idx]; + + mAddDialog->cleanup(); + mAddDialog->setGroup(group.mMeta.mGroupId); + mAddDialog->show(); +} + +void WireDialog::addGroup(const RsWireGroup &group) +{ + std::cerr << "WireDialog::addGroup() GroupId : " << group.mMeta.mGroupId; + std::cerr << std::endl; + + addGroup(new WireGroupItem(this, group)); +} + +void WireDialog::deleteGroups() +{ + std::cerr << "WireDialog::deleteGroups()"; + std::cerr << std::endl; + + mGroupSelected = NULL; + + QLayout *alayout = ui.scrollAreaWidgetContents_groups->layout(); + QLayoutItem *item; + int i = 0; + while (i < alayout->count()) + { + item = alayout->itemAt(i); + QWidget *widget = item->widget(); + if (NULL != dynamic_cast(widget)) + { + std::cerr << "WireDialog::deleteGroups() Removing Item at: " << i; + std::cerr << std::endl; + + item = alayout->takeAt(i); + delete item->widget(); + delete item; + } + else + { + std::cerr << "WireDialog::deleteGroups() Leaving Item at: " << i; + std::cerr << std::endl; + + i++; + } + } +} + +void WireDialog::updateGroups(std::vector& groups) +{ + mAllGroups.clear(); + mOwnGroups.clear(); + ui.groupChooser->clear(); + + std::vector::const_iterator it; + for(it = groups.begin(); it != groups.end(); it++) { + // save list of all groups. + mAllGroups[it->mMeta.mGroupId] = *it; + + if (it->mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN) + { + // grab own groups. + // setup Chooser too. + mOwnGroups.push_back(*it); + ui.groupChooser->addItem(QString::fromStdString(it->mMeta.mGroupName)); + } + } +} + + +void WireDialog::selectGroupSet(int index) +{ + std::cerr << "WireDialog::selectGroupSet(" << index << ")"; + std::cerr << std::endl; + + mGroupSet = index; + if (mGroupSet < 0) { + mGroupSet = GROUP_SET_ALL; + } + showGroups(); +} + +void WireDialog::selectFilterTime(int index) +{ + std::cerr << "WireDialog::selectFilterTime(" << index << ")"; + std::cerr << std::endl; + + showSelectedGroups(); +} + +void WireDialog::showSelectedGroups() +{ + ui.comboBox_filterTime->setEnabled(false); + if (mGroupSelected) + { + // request data. + std::list grpIds; + grpIds.push_back(mGroupSelected->groupId()); + + // show GroupFocus. + requestGroupFocus(mGroupSelected->groupId()); } else { - mAddDialog = new PulseAddDialog(NULL); + showGroups(); + } +} + +void WireDialog::showGroups() +{ + ui.comboBox_filterTime->setEnabled(false); + deleteGroups(); + + std::list allGroupIds; + + /* depends on the comboBox */ + std::map::const_iterator it; + for (it = mAllGroups.begin(); it != mAllGroups.end(); it++) + { + bool add = (mGroupSet == GROUP_SET_ALL); + if (it->second.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN) { + if (mGroupSet == GROUP_SET_OWN) { + add = true; + } + } + else if (it->second.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_SUBSCRIBED) { + if (mGroupSet == GROUP_SET_SUBSCRIBED) { + add = true; + } + } + else { + if (mGroupSet == GROUP_SET_OTHERS) { + add = true; + } + } + + if (add) { + addGroup(it->second); + // request data. + std::list grpIds; + grpIds.push_back(it->second.mMeta.mGroupId); + allGroupIds.push_back(it->second.mMeta.mGroupId); + } + } + + requestGroupsPulses(allGroupIds); +} + + +// LOAD DATA............................................... + +void WireDialog::requestGroupData() +{ + std::cerr << "WireDialog::requestGroupData()"; + std::cerr << std::endl; + + RsTokReqOptions opts; + uint32_t token; + opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; + mWireQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, 0); +} + +bool WireDialog::loadGroupData(const uint32_t &token) +{ + std::cerr << "WireDialog::loadGroupData()"; + std::cerr << std::endl; + + std::vector groups; + rsWire->getGroupData(token, groups); + + // save list of groups. + updateGroups(groups); + showGroups(); + return true; +} + +rstime_t WireDialog::getFilterTimestamp() +{ + rstime_t filterTimestamp = time(NULL); + switch(ui.comboBox_filterTime->currentIndex()) + { + case 1: // Last 24 Hours. + filterTimestamp -= (3600 * 24); + break; + case 2: // Last 7 Days. + filterTimestamp -= (3600 * 24 * 7); + break; + case 3: // Last 30 Days. + filterTimestamp -= (3600 * 24 * 30); + break; + case 0: // All Time. + case -1: // no index. + default: + filterTimestamp = 0; // back to Epoch! effectively all. + break; + } + return filterTimestamp; +} + +void WireDialog::acknowledgeGroup(const uint32_t &token, const uint32_t &userType) +{ + /* reload groups */ + std::cerr << "WireDialog::acknowledgeGroup(usertype: " << userType << ")"; + std::cerr << std::endl; + + RsGxsGroupId grpId; + rsWire->acknowledgeGrp(token, grpId); + + refreshGroups(); +} + + +/**************************** Request / Response Filling of Data ************************/ + +void WireDialog::loadRequest(const TokenQueue *queue, const TokenRequest &req) +{ + std::cerr << "WireDialog::loadRequest()"; + std::cerr << std::endl; + + if (queue == mWireQueue) + { + /* now switch on req */ + switch(req.mType) + { + case TOKENREQ_GROUPINFO: + switch(req.mAnsType) + { + case RS_TOKREQ_ANSTYPE_DATA: + loadGroupData(req.mToken); + break; + case RS_TOKREQ_ANSTYPE_ACK: + acknowledgeGroup(req.mToken, req.mUserType); + break; + default: + std::cerr << "WireDialog::loadRequest() ERROR: GROUP: INVALID ANS TYPE"; + std::cerr << std::endl; + break; + } + break; + default: + std::cerr << "WireDialog::loadRequest() ERROR: INVALID TYPE"; + std::cerr << std::endl; + break; + } + } +} + + +/**************************** Request / Response Filling of Data ************************/ + + + + + +/****************************************************************************************/ +// TWITTER VIEW. +/****************************************************************************************/ + +// TODO +// - Handle Groups +// - Add GroupPtr to WirePulseSPtrs (DONE) +// - Add HeadShot to WireGroup +// - Add GxsIdLabel to Pulse UI elements. +// +// - Create Groups. +// - Add HeadShot +// +// - Create Pulse. +// - Add Images. +// +// - Link up Reply / Republish / Like. +// +// - showGroupFocus +// - TODO +// +// - showPulseFocus +// - Basics (DONE). +// - MoreReplies +// - Show Actual Message. + +// +// - showReplyFocus +// - TODO + + +// PulseDataItem interface +// Actions. +void WireDialog::PVHreply(const RsGxsGroupId &groupId, const RsGxsMessageId &msgId) +{ + std::cerr << "WireDialog::PVHreply() GroupId: " << groupId; + std::cerr << "MsgId: " << msgId; + std::cerr << std::endl; + + if (setupPulseAddDialog()) + { + mAddDialog->setReplyTo(groupId, msgId, WIRE_PULSE_TYPE_REPLY); mAddDialog->show(); } } - -bool WireDialog::matchesAlbumFilter(const RsPhotoAlbum &album) +void WireDialog::PVHrepublish(const RsGxsGroupId &groupId, const RsGxsMessageId &msgId) { + std::cerr << "WireDialog::PVHrepublish() GroupId: " << groupId; + std::cerr << "MsgId: " << msgId; + std::cerr << std::endl; - return true; -} - -double WireDialog::AlbumScore(const RsPhotoAlbum &album) -{ - return 1; -} - - -bool WireDialog::matchesPhotoFilter(const RsPhotoPhoto &photo) -{ - - return true; -} - -double WireDialog::PhotoScore(const RsPhotoPhoto &photo) -{ - return 1; -} - - -bool WireDialog::FilterNSortAlbums(const std::list &albumIds, std::list &filteredAlbumIds, int count) -{ -#if 0 - std::multimap sortedAlbums; - std::multimap::iterator sit; - std::list::const_iterator it; - - for(it = albumIds.begin(); it != albumIds.end(); ++it) + if (setupPulseAddDialog()) { - RsPhotoAlbum album; - rsPhoto->getAlbum(*it, album); - - if (matchesAlbumFilter(album)) - { - double score = AlbumScore(album); - - sortedAlbums.insert(std::make_pair(score, *it)); - } + mAddDialog->setReplyTo(groupId, msgId, WIRE_PULSE_TYPE_REPUBLISH); + mAddDialog->show(); } - - int i; - for (sit = sortedAlbums.begin(), i = 0; (sit != sortedAlbums.end()) && (i < count); ++sit, ++i) - { - filteredAlbumIds.push_back(sit->second); - } -#endif - return true; } - -bool WireDialog::FilterNSortPhotos(const std::list &photoIds, std::list &filteredPhotoIds, int count) +void WireDialog::PVHlike(const RsGxsGroupId &groupId, const RsGxsMessageId &msgId) { -#if 0 - std::multimap sortedPhotos; - std::multimap::iterator sit; - std::list::const_iterator it; + std::cerr << "WireDialog::PVHlike() GroupId: " << groupId; + std::cerr << "MsgId: " << msgId; + std::cerr << std::endl; + if (setupPulseAddDialog()) + { + mAddDialog->setReplyTo(groupId, msgId, WIRE_PULSE_TYPE_LIKE); + mAddDialog->show(); + } +} + +void WireDialog::PVHviewGroup(const RsGxsGroupId &groupId) +{ + std::cerr << "WireDialog::PVHviewGroup("; + std::cerr << groupId.toStdString(); + std::cerr << ")"; + std::cerr << std::endl; + + requestGroupFocus(groupId); +} + +void WireDialog::PVHviewPulse(const RsGxsGroupId &groupId, const RsGxsMessageId &msgId) +{ + std::cerr << "WireDialog::PVHviewPulse("; + std::cerr << groupId.toStdString() << ","; + std::cerr << msgId.toStdString(); + std::cerr << ")"; + std::cerr << std::endl; + + requestPulseFocus(groupId, msgId); +} + +void WireDialog::PVHviewReply(const RsGxsGroupId &groupId, const RsGxsMessageId &msgId) +{ + std::cerr << "WireDialog::PVHviewReply("; + std::cerr << groupId.toStdString() << ","; + std::cerr << msgId.toStdString(); + std::cerr << ")"; + std::cerr << std::endl; + + // requestPulseFocus(groupId, msgId); +} + +void WireDialog::PVHfollow(const RsGxsGroupId &groupId) +{ + std::cerr << "WireDialog::PVHfollow("; + std::cerr << groupId.toStdString(); + std::cerr << ")"; + std::cerr << std::endl; + + uint32_t token; + rsWire->subscribeToGroup(token, groupId, true); + mWireQueue->queueRequest(token, TOKENREQ_GROUPINFO, RS_TOKREQ_ANSTYPE_ACK, WIRE_TOKEN_TYPE_SUBSCRIBE_CHANGE); +} + +void WireDialog::PVHrate(const RsGxsId &authorId) +{ + std::cerr << "WireDialog::PVHrate("; + std::cerr << authorId.toStdString(); + std::cerr << ") TODO"; + std::cerr << std::endl; +} + +void WireDialog::postTestTwitterView() +{ + clearTwitterView(); + + addTwitterView(new PulseTopLevel(NULL,RsWirePulseSPtr())); + addTwitterView(new PulseReply(NULL,RsWirePulseSPtr())); + addTwitterView(new PulseReply(NULL,RsWirePulseSPtr())); + addTwitterView(new PulseReply(NULL,RsWirePulseSPtr())); + addTwitterView(new PulseReply(NULL,RsWirePulseSPtr())); + addTwitterView(new PulseReply(NULL,RsWirePulseSPtr())); +} + +void WireDialog::clearTwitterView() +{ + std::cerr << "WireDialog::clearTwitterView()"; + std::cerr << std::endl; + + QLayout *alayout = ui.scrollAreaWidgetContents->layout(); + QLayoutItem *item; int i = 0; - for(it = photoIds.begin(); it != photoIds.end(); ++it, ++i) + while (i < alayout->count()) { - RsPhotoPhoto photo; - rsPhoto->getPhoto(*it, photo); - - if (matchesPhotoFilter(photo)) + item = alayout->itemAt(i); + QWidget *widget = item->widget(); + if (NULL != dynamic_cast(widget)) { - double score = i; //PhotoScore(album); - sortedPhotos.insert(std::make_pair(score, *it)); + std::cerr << "WireDialog::clearTwitterView() Removing Item at: " << i; + std::cerr << std::endl; + + item = alayout->takeAt(i); + delete item->widget(); + delete item; + } + else + { + std::cerr << "WireDialog::clearTwitterView() Leaving Item at: " << i; + std::cerr << std::endl; + + i++; } } - - for (sit = sortedPhotos.begin(), i = 0; (sit != sortedPhotos.end()) && (i < count); ++sit, ++i) - { - filteredPhotoIds.push_back(sit->second); - } -#endif - return true; } - - -void WireDialog::insertAlbums() +void WireDialog::addTwitterView(PulseViewItem *item) { -#if 0 - /* clear it all */ - clearAlbums(); - //ui.albumLayout->clear(); - - /* create a list of albums */ - - - std::list albumIds; - std::list filteredAlbumIds; - std::list::iterator it; - - rsPhoto->getAlbumList(albumIds); - - /* Filter Albums */ /* Sort Albums */ -#define MAX_ALBUMS 50 - - int count = MAX_ALBUMS; - FilterNSortAlbums(albumIds, filteredAlbumIds, count); - - for(it = filteredAlbumIds.begin(); it != filteredAlbumIds.end(); ++it) - { - addAlbum(*it); - } - - insertPhotosForAlbum(filteredAlbumIds); -#endif -} - -void WireDialog::insertPhotosForSelectedAlbum() -{ -#if 0 - std::cerr << "WireDialog::insertPhotosForSelectedAlbum()"; + std::cerr << "WireDialog::addTwitterView()"; std::cerr << std::endl; - clearPhotos(); - - std::list albumIds; - if (mAlbumSelected) - { - albumIds.push_back(mAlbumSelected->mDetails.mAlbumId); - - std::cerr << "WireDialog::insertPhotosForSelectedAlbum() AlbumId: " << mAlbumSelected->mDetails.mAlbumId; + /* ensure its a boxlayout */ + QLayout *alayout = ui.scrollAreaWidgetContents->layout(); + QBoxLayout *boxlayout = dynamic_cast(alayout); + if (boxlayout == NULL) { + std::cerr << "WireDialog::addTwitterView() ERROR not boxlayout, not Inserting"; std::cerr << std::endl; + return; } - insertPhotosForAlbum(albumIds); -#endif + // inserting as last item. + std::cerr << "WireDialog::addTwitterView() Inserting at end"; + std::cerr << std::endl; + boxlayout->addWidget(item); } +// HISTORY ------------------------------------------------------------------------------- -void WireDialog::addAlbum(const std::string &id) +void printWireViewHistory(const WireViewHistory &view) { -#if 0 - RsPhotoAlbum album; - rsPhoto->getAlbum(id, album); - - - RsPhotoThumbnail thumbnail; - rsPhoto->getAlbumThumbnail(id, thumbnail); - - std::cerr << " WireDialog::addAlbum() AlbumId: " << album.mAlbumId << std::endl; - - PulseItem *item = new PulseItem(this, album, thumbnail); - QLayout *alayout = ui.scrollAreaWidgetContents->layout(); - alayout->addWidget(item); -#endif + std::cerr << "WireViewHistory(" << (int) view.viewType << "): "; + switch(view.viewType) { + case WireViewType::PULSE_FOCUS: + std::cerr << " PULSE_FOCUS: grpId: " << view.groupId; + std::cerr << " msgId: " << view.msgId; + std::cerr << std::endl; + break; + case WireViewType::GROUP_FOCUS: + std::cerr << " GROUP_FOCUS: grpId: " << view.groupId; + std::cerr << std::endl; + break; + case WireViewType::GROUPS: + std::cerr << " GROUPS_PULSES: grpIds: TBD"; + std::cerr << std::endl; + break; + default: + break; + } } -void WireDialog::clearAlbums() +void WireDialog::AddToHistory(const WireViewHistory &view) { -#if 0 - std::cerr << "WireDialog::clearAlbums()" << std::endl; + std::cerr << "AddToHistory():"; + printWireViewHistory(view); - std::list photoItems; - std::list::iterator pit; - - QLayout *alayout = ui.scrollAreaWidgetContents->layout(); - int count = alayout->count(); - for(int i = 0; i < count; ++i) + /* clear future history */ + mHistory.resize(mHistoryIndex + 1); + + mHistory.push_back(view); + mHistoryIndex = mHistory.size() - 1; + + // at end of history. + // enable back, disable forward. + ui.toolButton_back->setEnabled(mHistoryIndex > 0); + ui.toolButton_forward->setEnabled(false); +} + +void WireDialog::back() +{ + LoadHistory(mHistoryIndex-1); +} + +void WireDialog::forward() +{ + LoadHistory(mHistoryIndex+1); +} + +void WireDialog::LoadHistory(uint32_t index) +{ + if (index >= mHistory.size()) { + return; + } + + mHistoryIndex = index; + WireViewHistory view = mHistory[index]; + + std::cerr << "LoadHistory:"; + printWireViewHistory(view); + + switch(view.viewType) { + case WireViewType::PULSE_FOCUS: + showPulseFocus(view.groupId, view.msgId); + break; + case WireViewType::GROUP_FOCUS: + showGroupFocus(view.groupId); + break; + case WireViewType::GROUPS: + showGroupsPulses(view.groupIds); + break; + default: + break; + } + + ui.toolButton_back->setEnabled(index > 0); + ui.toolButton_forward->setEnabled(index + 1 < mHistory.size()); +} +// HISTORY ------------------------------------------------------------------------------- + +void WireDialog::requestPulseFocus(const RsGxsGroupId groupId, const RsGxsMessageId msgId) +{ + WireViewHistory view; + view.viewType = WireViewType::PULSE_FOCUS; + view.groupId = groupId; + view.msgId = msgId; + + AddToHistory(view); + showPulseFocus(groupId, msgId); +} + +void WireDialog::showPulseFocus(const RsGxsGroupId groupId, const RsGxsMessageId msgId) +{ + clearTwitterView(); + + // background thread for loading. + RsThread::async([this, groupId, msgId]() { - QLayoutItem *litem = alayout->itemAt(i); - if (!litem) + // fetch data from backend. + RsWirePulseSPtr pPulse; + int type = 0; + bool success = rsWire->getPulseFocus(groupId, msgId, type, pPulse); + + // sleep(2); + + /* now insert the pulse + children into the layput */ + RsQThreadUtils::postToObject([pPulse,this]() { - std::cerr << "WireDialog::clearAlbums() missing litem"; - std::cerr << std::endl; - continue; - } - - PulseItem *item = dynamic_cast(litem->widget()); - if (item) + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ + + postPulseFocus(pPulse); + + }, this); + }); +} + + +void WireDialog::postPulseFocus(RsWirePulseSPtr pPulse) +{ + clearTwitterView(); + if (!pPulse) + { + std::cerr << "WireDialog::postPulseFocus() Invalid pulse"; + std::cerr << std::endl; + return; + } + + ui.label_viewMode->setText("Pulse Focus"); + + addTwitterView(new PulseTopLevel(this, pPulse)); + + std::list::iterator it; + for(it = pPulse->mReplies.begin(); it != pPulse->mReplies.end(); it++) + { + RsWirePulseSPtr reply = *it; + PulseReply *firstReply = new PulseReply(this, reply); + addTwitterView(firstReply); + + if (reply->mReplies.size() > 0) { - std::cerr << "WireDialog::clearAlbums() item: " << item; - std::cerr << std::endl; - - photoItems.push_back(item); + PulseReply *secondReply = new PulseReply(this, reply->mReplies.front()); + addTwitterView(secondReply); + firstReply->showReplyLine(true); + secondReply->showReplyLine(false); } else { - std::cerr << "WireDialog::clearAlbums() Found Child, which is not a PulseItem???"; - std::cerr << std::endl; + firstReply->showReplyLine(false); } + + + if (reply->mReplies.size() > 1) + { + // addTwitterView(new PulseMoreReplies(NULL, reply)); + } + + addTwitterView(new PulseReplySeperator()); } - - for(pit = photoItems.begin(); pit != photoItems.end(); ++pit) + + // Add big separator, and republishes. + if (pPulse->mReplies.size() > 0 && pPulse->mRepublishes.size() > 0) { - PulseItem *item = *pit; - alayout->removeWidget(item); - delete item; + addTwitterView(new PulseReplySeperator()); + addTwitterView(new PulseReplySeperator()); } - mAlbumSelected = NULL; -#endif + + for(it = pPulse->mRepublishes.begin(); it != pPulse->mRepublishes.end(); it++) + { + RsWirePulseSPtr repub = *it; + + PulseReply *firstRepub = new PulseReply(this, repub); + firstRepub->showReplyLine(false); + + addTwitterView(firstRepub); + addTwitterView(new PulseReplySeperator()); + } + + } -void WireDialog::clearPhotos() +void WireDialog::requestGroupFocus(const RsGxsGroupId groupId) { -#if 0 - std::cerr << "WireDialog::clearPhotos()" << std::endl; + WireViewHistory view; + view.viewType = WireViewType::GROUP_FOCUS; + view.groupId = groupId; - std::list photoItems; - std::list::iterator pit; - - QLayout *alayout = ui.scrollAreaWidgetContents_2->layout(); - int count = alayout->count(); - for(int i = 0; i < count; ++i) + AddToHistory(view); + showGroupFocus(groupId); +} + +void WireDialog::showGroupFocus(const RsGxsGroupId groupId) +{ + clearTwitterView(); + + // background thread for loading. + RsThread::async([this, groupId]() { - QLayoutItem *litem = alayout->itemAt(i); - if (!litem) + // fetch data from backend. + RsWireGroupSPtr grp; + std::list pulses; + + bool success = rsWire->getWireGroup(groupId, grp); + std::list groupIds = { groupId }; + success = rsWire->getPulsesForGroups(groupIds, pulses); + + // sleep(2); + + /* now insert the pulse + children into the layput */ + RsQThreadUtils::postToObject([grp, pulses,this]() { - std::cerr << "WireDialog::clearPhotos() missing litem"; - std::cerr << std::endl; - continue; - } - - PulseItem *item = dynamic_cast(litem->widget()); - if (item) - { - std::cerr << "WireDialog::clearPhotos() item: " << item; - std::cerr << std::endl; - - photoItems.push_back(item); - } - else - { - std::cerr << "WireDialog::clearPhotos() Found Child, which is not a PulseItem???"; - std::cerr << std::endl; - } - } - - for(pit = photoItems.begin(); pit != photoItems.end(); ++pit) - { - PulseItem *item = *pit; - alayout->removeWidget(item); - delete item; - } - - mPhotoSelected = NULL; -#endif - + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ + + postGroupFocus(grp, pulses); + + }, this); + }); } -void WireDialog::insertPhotosForAlbum(const std::list &albumIds) +void WireDialog::postGroupFocus(RsWireGroupSPtr group, std::list pulses) { -#if 0 - /* clear it all */ - clearPhotos(); - //ui.photoLayout->clear(); - - /* create a list of albums */ - - std::list ids; - std::list photoIds; - std::list filteredPhotoIds; - std::list::const_iterator it; - - for(it = albumIds.begin(); it != albumIds.end(); ++it) - { - rsPhoto->getPhotoList(*it, photoIds); - } - - /* Filter Albums */ /* Sort Albums */ -#define MAX_PHOTOS 50 - - int count = MAX_PHOTOS; - - FilterNSortPhotos(photoIds, filteredPhotoIds, MAX_PHOTOS); - - for(it = filteredPhotoIds.begin(); it != filteredPhotoIds.end(); ++it) - { - addPhoto(*it); - } -#endif -} - - -void WireDialog::addPhoto(const std::string &id) -{ -#if 0 - RsPhotoPhoto photo; - rsPhoto->getPhoto(id,photo); - - RsPhotoThumbnail thumbnail; - rsPhoto->getPhotoThumbnail(id, thumbnail); - - std::cerr << "WireDialog::addPhoto() AlbumId: " << photo.mAlbumId; - std::cerr << " PhotoId: " << photo.mId; + std::cerr << "WireDialog::postGroupFocus()"; std::cerr << std::endl; - PulseItem *item = new PulseItem(this, photo, thumbnail); - QLayout *alayout = ui.scrollAreaWidgetContents_2->layout(); - alayout->addWidget(item); -#endif + if (!group) + { + std::cerr << "WireDialog::postGroupFocus() group is INVALID"; + std::cerr << std::endl; + return; + } + + ui.label_viewMode->setText("Group Focus"); + + addTwitterView(new PulseViewGroup(this, group)); + + std::list::iterator it; + for(it = pulses.begin(); it != pulses.end(); it++) + { + RsWirePulseSPtr reply = *it; + + // don't show likes + if (reply->mPulseType & WIRE_PULSE_TYPE_LIKE) { + std::cerr << "WireDialog::postGroupFocus() Not showing LIKE"; + std::cerr << std::endl; + continue; + } + + PulseReply *firstReply = new PulseReply(this, reply); + addTwitterView(firstReply); + firstReply->showReplyLine(false); + + addTwitterView(new PulseReplySeperator()); + + } } - -void WireDialog::deletePulseItem(PulseItem *item, uint32_t type) +void WireDialog::requestGroupsPulses(const std::list groupIds) { + WireViewHistory view; + view.viewType = WireViewType::GROUPS; + view.groupIds = groupIds; - - return; + AddToHistory(view); + showGroupsPulses(groupIds); } +void WireDialog::showGroupsPulses(const std::list groupIds) +{ + clearTwitterView(); + // background thread for loading. + RsThread::async([this, groupIds]() + { + // fetch data from backend. + std::list pulses; + bool success = rsWire->getPulsesForGroups(groupIds, pulses); + + // sleep(2); + + /* now insert the pulse + children into the layput */ + RsQThreadUtils::postToObject([pulses,this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ + + postGroupsPulses(pulses); + + }, this); + }); +} + +void WireDialog::postGroupsPulses(std::list pulses) +{ + std::cerr << "WireDialog::postGroupsPulses()"; + std::cerr << std::endl; + + ui.label_viewMode->setText("Groups Pulses"); + + std::list::iterator it; + for(it = pulses.begin(); it != pulses.end(); it++) + { + RsWirePulseSPtr reply = *it; + // don't show likes + if (reply->mPulseType & WIRE_PULSE_TYPE_LIKE) { + std::cerr << "WireDialog::postGroupsPulses() Not showing LIKE"; + std::cerr << std::endl; + continue; + } + + PulseReply *firstReply = new PulseReply(this, reply); + addTwitterView(firstReply); + firstReply->showReplyLine(false); + + addTwitterView(new PulseReplySeperator()); + + } +} diff --git a/retroshare-gui/src/gui/TheWire/WireDialog.h b/retroshare-gui/src/gui/TheWire/WireDialog.h index 29b25c509..5a56c19ae 100644 --- a/retroshare-gui/src/gui/TheWire/WireDialog.h +++ b/retroshare-gui/src/gui/TheWire/WireDialog.h @@ -1,7 +1,7 @@ /******************************************************************************* * gui/TheWire/WireDialog.h * * * - * Copyright (c) 2012 Robert Fernie * + * Copyright (c) 2012-2020 Robert Fernie * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Affero General Public License as * @@ -24,60 +24,145 @@ #include "retroshare-gui/mainpage.h" #include "ui_WireDialog.h" -#include +#include #include -#include "gui/TheWire/PulseItem.h" +#include "gui/TheWire/WireGroupItem.h" #include "gui/TheWire/PulseAddDialog.h" -class WireDialog : public MainPage, public PulseHolder +#include "gui/TheWire/PulseViewItem.h" +#include "gui/TheWire/PulseTopLevel.h" +#include "gui/TheWire/PulseReply.h" + + +#include "util/TokenQueue.h" + +#define IMAGE_WIRE ":/icons/wire.png" + +//--------------------------- Classes for Wire View History +enum class WireViewType +{ + GROUPS, + GROUP_FOCUS, + PULSE_FOCUS, +}; + +enum class WireViewTimeRange +{ + FOREVER, + LAST_DAY, // last 24 hours. + LAST_WEEK, // actually last 7 days. + LAST_MONTH // actually last 30 days. +}; + +class WireViewHistory +{ +public: + WireViewType viewType; + WireViewTimeRange viewTimeRange; + + RsGxsGroupId groupId; + RsGxsMessageId msgId; + std::list groupIds; +}; +//--------------------------------------------------------- + +class WireDialog : public MainPage, public TokenResponse, public WireGroupHolder, public PulseViewHolder { Q_OBJECT public: WireDialog(QWidget *parent = 0); -virtual void deletePulseItem(PulseItem *, uint32_t type); -virtual void notifySelection(PulseItem *item, int ptype); + virtual QIcon iconPixmap() const { return QIcon(IMAGE_WIRE) ; } + virtual QString pageName() const { return tr("The Wire") ; } + virtual QString helpText() const { return ""; } - void notifyPulseSelection(PulseItem *item); + // WireGroupHolder interface. + virtual void subscribe(RsGxsGroupId &groupId) override; + virtual void unsubscribe(RsGxsGroupId &groupId) override; + virtual void notifyGroupSelection(WireGroupItem *item) override; + + // PulseViewItem interface + virtual void PVHreply(const RsGxsGroupId &groupId, const RsGxsMessageId &msgId) override; + virtual void PVHrepublish(const RsGxsGroupId &groupId, const RsGxsMessageId &msgId) override; + virtual void PVHlike(const RsGxsGroupId &groupId, const RsGxsMessageId &msgId) override; + + virtual void PVHviewGroup(const RsGxsGroupId &groupId) override; + virtual void PVHviewPulse(const RsGxsGroupId &groupId, const RsGxsMessageId &msgId) override; + virtual void PVHviewReply(const RsGxsGroupId &groupId, const RsGxsMessageId &msgId) override; + + virtual void PVHfollow(const RsGxsGroupId &groupId) override; + virtual void PVHrate(const RsGxsId &authorId) override; + + // New TwitterView + void postTestTwitterView(); + void clearTwitterView(); + void addTwitterView(PulseViewItem *item); + + // TwitterView History + void AddToHistory(const WireViewHistory &view); + void LoadHistory(uint32_t index); + + void requestPulseFocus(const RsGxsGroupId groupId, const RsGxsMessageId msgId); + void showPulseFocus(const RsGxsGroupId groupId, const RsGxsMessageId msgId); + void postPulseFocus(RsWirePulseSPtr pulse); + + void requestGroupFocus(const RsGxsGroupId groupId); + void showGroupFocus(const RsGxsGroupId groupId); + void postGroupFocus(RsWireGroupSPtr group, std::list pulses); + + void requestGroupsPulses(const std::list groupIds); + void showGroupsPulses(const std::list groupIds); + void postGroupsPulses(std::list pulses); private slots: + void createGroup(); + void createPulse(); void checkUpdate(); - void OpenOrShowPulseAddDialog(); + void refreshGroups(); + void selectGroupSet(int index); + void selectFilterTime(int index); + + // history navigation. + void back(); + void forward(); private: + bool setupPulseAddDialog(); + void addGroup(QWidget *item); + void addGroup(const RsWireGroup &group); - /* TODO: These functions must be filled in for proper filtering to work - * and tied to the GUI input - */ + void deleteGroups(); + void showGroups(); + void showSelectedGroups(); + void updateGroups(std::vector &groups); - bool matchesAlbumFilter(const RsPhotoAlbum &album); - double AlbumScore(const RsPhotoAlbum &album); - bool matchesPhotoFilter(const RsPhotoPhoto &photo); - double PhotoScore(const RsPhotoPhoto &photo); + // utils. + rstime_t getFilterTimestamp(); - /* Grunt work of setting up the GUI */ + // Loading Data. + void requestGroupData(); + bool loadGroupData(const uint32_t &token); + void acknowledgeGroup(const uint32_t &token, const uint32_t &userType); - bool FilterNSortAlbums(const std::list &albumIds, std::list &filteredAlbumIds, int count); - bool FilterNSortPhotos(const std::list &photoIds, std::list &filteredPhotoIds, int count); - void insertAlbums(); - void insertPhotosForAlbum(const std::list &albumIds); - void insertPhotosForSelectedAlbum(); + virtual void loadRequest(const TokenQueue *queue, const TokenRequest &req); - void addAlbum(const std::string &id); - void addPhoto(const std::string &id); - - void clearAlbums(); - void clearPhotos(); + int mGroupSet; PulseAddDialog *mAddDialog; + WireGroupItem *mGroupSelected; + TokenQueue *mWireQueue; - PulseItem *mPulseSelected; + std::map mAllGroups; + std::vector mOwnGroups; + + int32_t mHistoryIndex; + std::vector mHistory; /* UI - from Designer */ Ui::WireDialog ui; diff --git a/retroshare-gui/src/gui/TheWire/WireDialog.ui b/retroshare-gui/src/gui/TheWire/WireDialog.ui index 6cfa8774a..8b4f1d2a9 100644 --- a/retroshare-gui/src/gui/TheWire/WireDialog.ui +++ b/retroshare-gui/src/gui/TheWire/WireDialog.ui @@ -6,102 +6,27 @@ 0 0 - 726 - 557 + 804 + 586
+ + + 0 + 0 + + - - - - - - - TimeRange - - - - - - - - All - - - - - Last Month - - - - - Last Week - - - - - Today - - - - - New - - - - - - - - - from - - - - - until - - - - - - - - - - - Search/Filter - - - - - - - - - - Network Wide - - - - - - - - - Manage Accounts - - - - - - - - 160 - 16777215 - + + + + + + 0 + 1 + QFrame::StyledPanel @@ -109,213 +34,379 @@ QFrame::Raised - - - 0 - - - 0 - + - - - true + + + Create Account + + + + :/icons/png/add.png:/icons/png/add.png + + + Qt::ToolButtonTextBesideIcon + + + + + + + Post Pulse + + + + :/images/compose.png:/images/compose.png + + + Qt::ToolButtonTextBesideIcon + + + + + + + ... + + + + + + + Qt::Horizontal + + + + 296 + 20 + + + + + + + + Refresh + + + + + + + + + + false + + + Settings - - - - 0 - 0 - 156 - 469 - - - - - - - Showing: - - - - 0 - - - - - Yourself - - - - - - - Friends - - - - - - - Following - - - - - - - All - - - - - - - Qt::Horizontal - - - - - - - Custom - - - - - - - - - - Account 1 - - - - - - - Account 2 - - - - - - - Account 3 - - - - - - - CheckBox - - - - - - - CheckBox - - - - - - - CheckBox - - - - - - - CheckBox - - - - - - - CheckBox - - - - - - - Qt::Vertical - - - - 20 - 116 - - - - - - - - - - true + + + + + 0 + 100 + - - Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + Qt::Horizontal - - - - 0 - 0 - 540 - 465 - + + + + 1 + 0 + - - QWidget#scrollAreaWidgetContents{border: none;} + + + 250 + 0 + - - - 0 - - - 0 - + + + 400 + 16777215 + + + + QFrame::StyledPanel + + + QFrame::Raised + + - - - Qt::Vertical + + + + All + + + + + Yourself + + + + + Following + + + + + Others + + + + + + + + + 10 + 75 + true + - - - 20 - 40 - + + Who to Follow -
+
+
+ + + + true + + + + + 0 + 0 + 228 + 442 + + + + + + + Qt::Vertical + + + + 20 + 116 + + + + + + +
- -
- - - - Post Pulse to Wire - + + + + 3 + 0 + + + + 0 + + + + HomePage + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + < + + + + + + + > + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 10 + 20 + + + + + + + + Most Recent + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Show Posts from + + + + + + + + 0 + 0 + + + + + All Time + + + + + Last 24 hours + + + + + Last 7 days + + + + + Last 30 days + + + + + + + + + + + true + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + 0 + 0 + 508 + 435 + + + + QWidget#scrollAreaWidgetContents{border: none;} + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + +
- + + diff --git a/retroshare-gui/src/gui/TheWire/WireGroupDialog.cpp b/retroshare-gui/src/gui/TheWire/WireGroupDialog.cpp new file mode 100644 index 000000000..f29aee712 --- /dev/null +++ b/retroshare-gui/src/gui/TheWire/WireGroupDialog.cpp @@ -0,0 +1,211 @@ +/******************************************************************************* + * gui/TheWire/WireGroupDialog.cpp * + * * + * Copyright (C) 2020 by Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ +#include + +#include "WireGroupExtra.h" + +#include "WireGroupDialog.h" +#include "gui/gxs/GxsIdDetails.h" + +#include + +const uint32_t WireCreateEnabledFlags = ( + GXS_GROUP_FLAGS_NAME | + GXS_GROUP_FLAGS_ICON | + // GXS_GROUP_FLAGS_DESCRIPTION | + GXS_GROUP_FLAGS_DISTRIBUTION | + // GXS_GROUP_FLAGS_PUBLISHSIGN | + // GXS_GROUP_FLAGS_SHAREKEYS | // disabled because the UI doesn't handle it yet. + // GXS_GROUP_FLAGS_PERSONALSIGN | + // GXS_GROUP_FLAGS_COMMENTS | + GXS_GROUP_FLAGS_EXTRA | + 0); + +uint32_t WireCreateDefaultsFlags = ( GXS_GROUP_DEFAULTS_DISTRIB_PUBLIC | + // GXS_GROUP_DEFAULTS_DISTRIB_GROUP | + // GXS_GROUP_DEFAULTS_DISTRIB_LOCAL | + + GXS_GROUP_DEFAULTS_PUBLISH_OPEN | + // GXS_GROUP_DEFAULTS_PUBLISH_THREADS | + // GXS_GROUP_DEFAULTS_PUBLISH_REQUIRED | + // GXS_GROUP_DEFAULTS_PUBLISH_ENCRYPTED | + + // GXS_GROUP_DEFAULTS_PERSONAL_GPG | + GXS_GROUP_DEFAULTS_PERSONAL_REQUIRED | + // GXS_GROUP_DEFAULTS_PERSONAL_IFNOPUB | + + // GXS_GROUP_DEFAULTS_COMMENTS_YES | + GXS_GROUP_DEFAULTS_COMMENTS_NO | + 0); + +uint32_t WireEditEnabledFlags = WireCreateEnabledFlags; +uint32_t WireEditDefaultsFlags = WireCreateDefaultsFlags; + +WireGroupDialog::WireGroupDialog(QWidget *parent) + : GxsGroupDialog(WireCreateEnabledFlags, WireCreateDefaultsFlags, parent) +{ +} + +WireGroupDialog::WireGroupDialog(Mode mode, RsGxsGroupId groupId, QWidget *parent) + : GxsGroupDialog(mode, groupId, WireEditEnabledFlags, WireEditDefaultsFlags, parent) +{ +} + +void WireGroupDialog::initUi() +{ + switch (mode()) + { + case MODE_CREATE: + setUiText(UITYPE_SERVICE_HEADER, tr("Create New Wire")); + setUiText(UITYPE_BUTTONBOX_OK, tr("Create")); + break; + case MODE_SHOW: + setUiText(UITYPE_SERVICE_HEADER, tr("Wire")); + break; + case MODE_EDIT: + setUiText(UITYPE_SERVICE_HEADER, tr("Edit Wire")); + setUiText(UITYPE_BUTTONBOX_OK, tr("Update Wire")); + break; + } + + setUiText(UITYPE_ADD_ADMINS_CHECKBOX, tr("Add Wire Admins")); + setUiText(UITYPE_CONTACTS_DOCK, tr("Select Wire Admins")); + + mExtra = new WireGroupExtra(this); + injectExtraWidget(mExtra); +} + +QPixmap WireGroupDialog::serviceImage() +{ + return FilesDefs::getPixmapFromQtResourcePath(":/icons/wire-circle.png"); +} + +void WireGroupDialog::prepareWireGroup(RsWireGroup &group, const RsGroupMetaData &meta) +{ + group.mMeta = meta; + QPixmap pixmap = getLogo(); + + if (!pixmap.isNull()) { + QByteArray ba; + QBuffer buffer(&ba); + + buffer.open(QIODevice::WriteOnly); + pixmap.save(&buffer, "PNG"); // writes image into ba in PNG format + + group.mHeadshot.copy((uint8_t *) ba.data(), ba.size()); + } else { + group.mHeadshot.clear(); + } + + // from Extra Widget. + group.mTagline = mExtra->getTagline(); + group.mLocation = mExtra->getLocation(); + pixmap = mExtra->getMasthead(); + + if (!pixmap.isNull()) { + QByteArray ba; + QBuffer buffer(&ba); + + buffer.open(QIODevice::WriteOnly); + pixmap.save(&buffer, "JPG"); + + group.mMasthead.copy((uint8_t *) ba.data(), ba.size()); + } else { + group.mMasthead.clear(); + } +} + +bool WireGroupDialog::service_createGroup(RsGroupMetaData &meta) +{ + RsWireGroup grp; + prepareWireGroup(grp, meta); + + bool success = rsWire->createGroup(grp); + // TODO createGroup should refresh groupId or Data + return success; +} + +bool WireGroupDialog::service_updateGroup(const RsGroupMetaData &editedMeta) +{ + RsWireGroup grp; + prepareWireGroup(grp, editedMeta); + + std::cerr << "WireGroupDialog::service_updateGroup() submitting changes"; + std::cerr << std::endl; + + bool success = rsWire->updateGroup(grp); + // TODO updateGroup should refresh groupId or Data + return success; +} + +bool WireGroupDialog::service_loadGroup(const RsGxsGenericGroupData *data, Mode mode, QString &description) +{ + std::cerr << "WireGroupDialog::service_loadGroup()"; + std::cerr << std::endl; + + const RsWireGroup *pgroup = dynamic_cast(data); + if (pgroup == nullptr) + { + std::cerr << "WireGroupDialog::service_loadGroup() Error not a RsWireGroup"; + std::cerr << std::endl; + return false; + } + + const RsWireGroup &group = *pgroup; + // description = QString::fromUtf8(group.mDescription.c_str()); + +#if 0 + if (group.mThumbnail.mData) { + QPixmap pixmap; + if (GxsIdDetails::loadPixmapFromData(group.mThumbnail.mData, group.mThumbnail.mSize, pixmap,GxsIdDetails::ORIGINAL)) { + setLogo(pixmap); + } + } else { + setLogo(FilesDefs::getPixmapFromQtResourcePath(":/images/album_create_64.png")); + } +#endif + + return true; +} + +bool WireGroupDialog::service_getGroupData(const RsGxsGroupId &grpId, RsGxsGenericGroupData *&data) +{ + std::cerr << "WireGroupDialog::service_getGroupData(" << grpId << ")"; + std::cerr << std::endl; + + std::list groupIds({grpId}); + std::vector groups; + if (!rsWire->getGroups(groupIds, groups)) + { + std::cerr << "WireGroupDialog::service_loadGroup() Error getting GroupData"; + std::cerr << std::endl; + return false; + } + + if (groups.size() != 1) + { + std::cerr << "WireGroupDialog::service_loadGroup() Error Group.size() != 1"; + std::cerr << std::endl; + return false; + } + + data = new RsWireGroup(groups[0]); + return true; +} diff --git a/retroshare-gui/src/gui/TheWire/WireGroupDialog.h b/retroshare-gui/src/gui/TheWire/WireGroupDialog.h new file mode 100644 index 000000000..ca3f59e73 --- /dev/null +++ b/retroshare-gui/src/gui/TheWire/WireGroupDialog.h @@ -0,0 +1,52 @@ +/******************************************************************************* + * gui/TheWire/WireGroupDialog.h * + * * + * Copyright (C) 2020 by Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#ifndef _WIRE_GROUP_DIALOG_H +#define _WIRE_GROUP_DIALOG_H + +#include "gui/gxs/GxsGroupDialog.h" + +#include + +class WireGroupExtra; + +class WireGroupDialog : public GxsGroupDialog +{ + Q_OBJECT + +public: + WireGroupDialog(QWidget *parent); + WireGroupDialog(Mode mode, RsGxsGroupId groupId, QWidget *parent); + +protected: + virtual void initUi() override; + virtual QPixmap serviceImage() override; + virtual bool service_createGroup(RsGroupMetaData &meta) override; + virtual bool service_updateGroup(const RsGroupMetaData &editedMeta) override; + virtual bool service_loadGroup(const RsGxsGenericGroupData *data, Mode mode, QString &description) override; + virtual bool service_getGroupData(const RsGxsGroupId &grpId, RsGxsGenericGroupData *&data) override; + +private: + void prepareWireGroup(RsWireGroup &group, const RsGroupMetaData &meta); + + WireGroupExtra *mExtra; +}; + +#endif diff --git a/retroshare-gui/src/gui/TheWire/WireGroupExtra.cpp b/retroshare-gui/src/gui/TheWire/WireGroupExtra.cpp new file mode 100644 index 000000000..b6a1c5440 --- /dev/null +++ b/retroshare-gui/src/gui/TheWire/WireGroupExtra.cpp @@ -0,0 +1,83 @@ +/******************************************************************************* + * retroshare-gui/src/gui/TheWire/WireGroupExtra.cpp * + * * + * Copyright (C) 2020 by Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include "WireGroupExtra.h" +#include "util/misc.h" + +WireGroupExtra::WireGroupExtra(QWidget *parent) : + QWidget(NULL) +{ + ui.setupUi(this); + setUp(); +} + +WireGroupExtra::~WireGroupExtra() +{ +} + +void WireGroupExtra::setUp() +{ + connect(ui.pushButton_masthead, SIGNAL(clicked() ), this , SLOT(addMasthead())); +} + + +void WireGroupExtra::addMasthead() +{ + QPixmap img = misc::getOpenThumbnailedPicture(this, tr("Load Masthead"), 400, 100); + + if (img.isNull()) + return; + + setMasthead(img); +} + + +void WireGroupExtra::setTagline(const std::string &str) +{ + ui.lineEdit_Tagline->setText(QString::fromStdString(str)); +} + +void WireGroupExtra::setLocation(const std::string &str) +{ + ui.lineEdit_Location->setText(QString::fromStdString(str)); +} + +void WireGroupExtra::setMasthead(const QPixmap &pixmap) +{ + mMasthead = pixmap; + ui.label_masthead->setPixmap(mMasthead); +} + +std::string WireGroupExtra::getTagline() +{ + return ui.lineEdit_Tagline->text().toStdString(); +} + +std::string WireGroupExtra::getLocation() +{ + return ui.lineEdit_Location->text().toStdString(); +} + +QPixmap WireGroupExtra::getMasthead() +{ + return mMasthead; +} + + diff --git a/retroshare-gui/src/gui/TheWire/WireGroupExtra.h b/retroshare-gui/src/gui/TheWire/WireGroupExtra.h new file mode 100644 index 000000000..ce606feb3 --- /dev/null +++ b/retroshare-gui/src/gui/TheWire/WireGroupExtra.h @@ -0,0 +1,54 @@ +/******************************************************************************* + * retroshare-gui/src/gui/TheWire/WireGroupExtra.h * + * * + * Copyright (C) 2020 by Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#ifndef WIRE_GROUP_EXTRA_H +#define WIRE_GROUP_EXTRA_H + +#include +#include "ui_WireGroupExtra.h" + +class WireGroupExtra : public QWidget +{ + Q_OBJECT + +public: + explicit WireGroupExtra(QWidget *parent = 0); + virtual ~WireGroupExtra(); + + void setMasthead(const QPixmap &pixmap); + QPixmap getMasthead(); + + void setTagline(const std::string &str); + void setLocation(const std::string &str); + + std::string getTagline(); + std::string getLocation(); + +private slots: + void addMasthead(); + +private: + void setUp(); +private: + QPixmap mMasthead; + Ui::WireGroupExtra ui; +}; + +#endif // WIRE_GROUP_EXTRA_H diff --git a/retroshare-gui/src/gui/TheWire/WireGroupExtra.ui b/retroshare-gui/src/gui/TheWire/WireGroupExtra.ui new file mode 100644 index 000000000..fb1da5e73 --- /dev/null +++ b/retroshare-gui/src/gui/TheWire/WireGroupExtra.ui @@ -0,0 +1,68 @@ + + + WireGroupExtra + + + + 0 + 0 + 516 + 199 + + + + + 0 + 0 + + + + Form + + + + + + Masthead + + + + + + + MastHead background Image + + + + + + + Select Image + + + + + + + Tagline: + + + + + + + + + + Location: + + + + + + + + + + + diff --git a/retroshare-gui/src/gui/TheWire/WireGroupItem.cpp b/retroshare-gui/src/gui/TheWire/WireGroupItem.cpp new file mode 100644 index 000000000..81df64dd2 --- /dev/null +++ b/retroshare-gui/src/gui/TheWire/WireGroupItem.cpp @@ -0,0 +1,175 @@ +/******************************************************************************* + * gui/TheWire/WireGroupItem.cpp * + * * + * Copyright (c) 2020 Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include +#include +#include +#include + +#include "WireGroupItem.h" +#include "gui/gxs/GxsIdDetails.h" + +#include +#include + +/** Constructor */ + +WireGroupItem::WireGroupItem(WireGroupHolder *holder, const RsWireGroup &grp) +:QWidget(NULL), mHolder(holder), mGroup(grp) +{ + setupUi(this); + setAttribute ( Qt::WA_DeleteOnClose, true ); + setup(); + +} + +RsGxsGroupId &WireGroupItem::groupId() +{ + return mGroup.mMeta.mGroupId; +} + +void WireGroupItem::setup() +{ + label_groupName->setText(QString::fromStdString(mGroup.mMeta.mGroupName)); + label_authorId->setId(mGroup.mMeta.mAuthorId); + frame_details->setVisible(false); + + if (mGroup.mHeadshot.mData ) + { + QPixmap pixmap; + if (GxsIdDetails::loadPixmapFromData( + mGroup.mHeadshot.mData, + mGroup.mHeadshot.mSize, + pixmap,GxsIdDetails::ORIGINAL)) + { + pixmap = pixmap.scaled(32,32); + label_headshot->setPixmap(pixmap); + } + } + else + { + // default. + QPixmap pixmap = FilesDefs::getPixmapFromQtResourcePath(":/icons/wire.png").scaled(32,32); + label_headshot->setPixmap(pixmap); + } + + RsIdentityDetails idDetails ; + rsIdentity->getIdDetails(mGroup.mMeta.mAuthorId,idDetails); + + QPixmap pixmap ; + + if(idDetails.mAvatar.mSize == 0 || !GxsIdDetails::loadPixmapFromData(idDetails.mAvatar.mData, idDetails.mAvatar.mSize, pixmap,GxsIdDetails::SMALL)) + pixmap = GxsIdDetails::makeDefaultIcon(mGroup.mMeta.mAuthorId,GxsIdDetails::SMALL); + + pixmap = pixmap.scaled(24,24); + label_avatar->setPixmap(pixmap); + + connect(toolButton_show, SIGNAL(clicked()), this, SLOT(show())); + connect(toolButton_subscribe, SIGNAL(clicked()), this, SLOT(subscribe())); + setGroupSet(); +} + +void WireGroupItem::setGroupSet() +{ + if (mGroup.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN) { + toolButton_type->setText("Own"); + toolButton_subscribe->setText("N/A"); + toolButton_subscribe->setEnabled(false); + } + else if (mGroup.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_SUBSCRIBED) + { + toolButton_type->setText("Following"); + toolButton_subscribe->setText("Unfollow"); + } + else + { + toolButton_type->setText("Other"); + toolButton_subscribe->setText("Follow"); + } +} + +void WireGroupItem::show() +{ + frame_details->setVisible(!frame_details->isVisible()); +} + +void WireGroupItem::subscribe() +{ + if (mGroup.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_SUBSCRIBED) + { + mHolder->unsubscribe(mGroup.mMeta.mGroupId); + } + else + { + mHolder->subscribe(mGroup.mMeta.mGroupId); + } +} + + +void WireGroupItem::removeItem() +{ +} + +void WireGroupItem::setSelected(bool on) +{ + mSelected = on; + // set color too + if (mSelected) + { + setBackground(QColor (65, 159, 217)); + } + else + { + setBackground("gray"); + } +} + + +void WireGroupItem::setBackground(QColor color) +{ + QWidget *tocolor = this; + QPalette p = tocolor->palette(); + p.setColor(tocolor->backgroundRole(), QColor(color)); + tocolor->setPalette(p); + tocolor->setAutoFillBackground(true); +} + +bool WireGroupItem::isSelected() +{ + return mSelected; +} + +void WireGroupItem::mousePressEvent(QMouseEvent *event) +{ + QPoint pos = event->pos(); + + std::cerr << "WireGroupItem::mousePressEvent(" << pos.x() << ", " << pos.y() << ")"; + std::cerr << std::endl; + + // notify of selection. + // Holder will setSelected() flag. + mHolder->notifyGroupSelection(this); +} + +const QPixmap *WireGroupItem::getPixmap() +{ + return NULL; +} + diff --git a/retroshare-gui/src/gui/TheWire/PulseItem.h b/retroshare-gui/src/gui/TheWire/WireGroupItem.h similarity index 57% rename from retroshare-gui/src/gui/TheWire/PulseItem.h rename to retroshare-gui/src/gui/TheWire/WireGroupItem.h index bfbdc710c..2181c533e 100644 --- a/retroshare-gui/src/gui/TheWire/PulseItem.h +++ b/retroshare-gui/src/gui/TheWire/WireGroupItem.h @@ -1,7 +1,7 @@ /******************************************************************************* - * gui/TheWire/PulseItem.h * + * gui/TheWire/WireGroupItem.h * * * - * Copyright (c) 2012 Robert Fernie * + * Copyright (c) 2020 Robert Fernie * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Affero General Public License as * @@ -18,37 +18,31 @@ * * *******************************************************************************/ -#ifndef MRK_PULSE_ITEM_H -#define MRK_PULSE_ITEM_H +#ifndef MRK_WIRE_GROUP_ITEM_H +#define MRK_WIRE_GROUP_ITEM_H -#include "ui_PulseItem.h" +#include "ui_WireGroupItem.h" -#include +#include -class PulseItem; +class WireGroupItem; -class PulseHolder +class WireGroupHolder { - public: -virtual void deletePulseItem(PulseItem *, uint32_t ptype) = 0; -virtual void notifySelection(PulseItem *item, int ptype) = 0; +public: + virtual ~WireGroupHolder() {} + virtual void subscribe(RsGxsGroupId &groupId) = 0; + virtual void unsubscribe(RsGxsGroupId &groupId) = 0; + + virtual void notifyGroupSelection(WireGroupItem *item) = 0; }; - -#define PHOTO_ITEM_TYPE_ALBUM 0x0001 -#define PHOTO_ITEM_TYPE_PHOTO 0x0002 -#define PHOTO_ITEM_TYPE_NEW 0x0003 - -class PulseItem : public QWidget, private Ui::PulseItem +class WireGroupItem : public QWidget, private Ui::WireGroupItem { Q_OBJECT public: - PulseItem(PulseHolder *parent, const RsPhotoAlbum &album, const RsPhotoThumbnail &thumbnail); - PulseItem(PulseHolder *parent, const RsPhotoPhoto &photo, const RsPhotoThumbnail &thumbnail); - PulseItem(PulseHolder *parent, std::string url); // for new photos. - - bool getPhotoThumbnail(RsPhotoThumbnail &nail); + WireGroupItem(WireGroupHolder *holder, const RsWireGroup &grp); void removeItem(); @@ -56,28 +50,23 @@ public: bool isSelected(); const QPixmap *getPixmap(); + RsGxsGroupId &groupId(); - // details are public - so that can be easily edited. - RsPhotoPhoto mDetails; - -//private slots: - +private slots: + void show(); + void subscribe(); protected: void mousePressEvent(QMouseEvent *event); private: - void updateAlbumText(const RsPhotoAlbum &album); - void updatePhotoText(const RsPhotoPhoto &photo); - void updateImage(const RsPhotoThumbnail &thumbnail); + void setup(); + void setGroupSet(); + void setBackground(QColor color); - PulseHolder *mParent; - uint32_t mType; - - - bool mSelected; + WireGroupHolder *mHolder; + RsWireGroup mGroup; + bool mSelected; }; - #endif - diff --git a/retroshare-gui/src/gui/TheWire/WireGroupItem.ui b/retroshare-gui/src/gui/TheWire/WireGroupItem.ui new file mode 100644 index 000000000..eeb793614 --- /dev/null +++ b/retroshare-gui/src/gui/TheWire/WireGroupItem.ui @@ -0,0 +1,158 @@ + + + WireGroupItem + + + + 0 + 0 + 276 + 114 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + 32 + 32 + + + + Avatar + + + true + + + + + + + WireGroupName + + + + + + + Qt::Horizontal + + + + 118 + 20 + + + + + + + + Type + + + + + + + \/ + + + + :/icons/png/down-arrow.png:/icons/png/down-arrow.png + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Avatar + + + + + + + GxsIdLabel + + + + + + + Qt::Horizontal + + + + 61 + 20 + + + + + + + + Sub/Un + + + + + + + + + + + + + + GxsIdLabel + QLabel +
gui/gxs/GxsIdLabel.h
+
+
+ + + + +
diff --git a/retroshare-gui/src/gui/TheWire/images/compose.png b/retroshare-gui/src/gui/TheWire/images/compose.png new file mode 100644 index 000000000..3a033462b Binary files /dev/null and b/retroshare-gui/src/gui/TheWire/images/compose.png differ diff --git a/retroshare-gui/src/gui/TheWire/images/external-link.svg b/retroshare-gui/src/gui/TheWire/images/external-link.svg new file mode 100644 index 000000000..d3a249f34 --- /dev/null +++ b/retroshare-gui/src/gui/TheWire/images/external-link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/retroshare-gui/src/gui/TheWire/images/invite.png b/retroshare-gui/src/gui/TheWire/images/invite.png new file mode 100644 index 000000000..5fda4753e Binary files /dev/null and b/retroshare-gui/src/gui/TheWire/images/invite.png differ diff --git a/retroshare-gui/src/gui/TheWire/images/like.png b/retroshare-gui/src/gui/TheWire/images/like.png new file mode 100644 index 000000000..cd0cd99ef Binary files /dev/null and b/retroshare-gui/src/gui/TheWire/images/like.png differ diff --git a/retroshare-gui/src/gui/TheWire/images/link.svg b/retroshare-gui/src/gui/TheWire/images/link.svg new file mode 100644 index 000000000..fdee24b3c --- /dev/null +++ b/retroshare-gui/src/gui/TheWire/images/link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/retroshare-gui/src/gui/TheWire/images/reply.png b/retroshare-gui/src/gui/TheWire/images/reply.png new file mode 100644 index 000000000..c2f5fdf62 Binary files /dev/null and b/retroshare-gui/src/gui/TheWire/images/reply.png differ diff --git a/retroshare-gui/src/gui/TheWire/images/retweet.png b/retroshare-gui/src/gui/TheWire/images/retweet.png new file mode 100644 index 000000000..e5272b6ba Binary files /dev/null and b/retroshare-gui/src/gui/TheWire/images/retweet.png differ diff --git a/retroshare-gui/src/gui/WikiPoos/WikiDialog.cpp b/retroshare-gui/src/gui/WikiPoos/WikiDialog.cpp index a4be2189f..74284872b 100644 --- a/retroshare-gui/src/gui/WikiPoos/WikiDialog.cpp +++ b/retroshare-gui/src/gui/WikiPoos/WikiDialog.cpp @@ -28,7 +28,7 @@ #include "WikiDialog.h" #include "gui/WikiPoos/WikiAddDialog.h" #include "gui/WikiPoos/WikiEditDialog.h" - +#include "gui/settings/rsharesettings.h" #include "gui/gxs/WikiGroupDialog.h" #include @@ -73,13 +73,12 @@ #define IMAGE_NEWFORUM ":/images/new_forum16.png" #define IMAGE_FORUMAUTHD ":/images/konv_message2.png" #define IMAGE_COPYLINK ":/images/copyrslink.png" -#define IMAGE_WIKI ":/images/wikibook_32.png" -#define IMAGE_EDIT ":/images/edit_16.png" +#define IMAGE_WIKI ":/icons/png/wiki.png" +#define IMAGE_EDIT ":/icons/png/pencil-edit-button.png" /** Constructor */ -WikiDialog::WikiDialog(QWidget *parent) -: RsGxsUpdateBroadcastPage(rsWiki, parent) +WikiDialog::WikiDialog(QWidget *parent) : RsGxsUpdateBroadcastPage(rsWiki, parent) { /* Invoke the Qt Designer generated object setup routine */ ui.setupUi(this); @@ -88,7 +87,6 @@ WikiDialog::WikiDialog(QWidget *parent) mAddGroupDialog = NULL; mEditDialog = NULL; - connect( ui.toolButton_NewGroup, SIGNAL(clicked()), this, SLOT(OpenOrShowAddGroupDialog())); connect( ui.toolButton_NewPage, SIGNAL(clicked()), this, SLOT(OpenOrShowAddPageDialog())); connect( ui.toolButton_Edit, SIGNAL(clicked()), this, SLOT(OpenOrShowEditDialog())); connect( ui.toolButton_Republish, SIGNAL(clicked()), this, SLOT(OpenOrShowRepublishDialog())); @@ -106,20 +104,54 @@ WikiDialog::WikiDialog(QWidget *parent) /* setup TokenQueue */ mWikiQueue = new TokenQueue(rsWiki->getTokenService(), this); + // Set initial size of the splitter + ui.listSplitter->setStretchFactor(0, 0); + ui.listSplitter->setStretchFactor(1, 1); /* Setup Group Tree */ - mYourGroups = ui.groupTreeWidget->addCategoryItem(tr("My Groups"), QIcon(IMAGE_FOLDER), true); - mSubscribedGroups = ui.groupTreeWidget->addCategoryItem(tr("Subscribed Groups"), QIcon(IMAGE_FOLDERRED), true); - mPopularGroups = ui.groupTreeWidget->addCategoryItem(tr("Popular Groups"), QIcon(IMAGE_FOLDERGREEN), false); - mOtherGroups = ui.groupTreeWidget->addCategoryItem(tr("Other Groups"), QIcon(IMAGE_FOLDERYELLOW), false); + mYourGroups = ui.groupTreeWidget->addCategoryItem(tr("My Groups"), QIcon(), true); + mSubscribedGroups = ui.groupTreeWidget->addCategoryItem(tr("Subscribed Groups"), QIcon(), true); + mPopularGroups = ui.groupTreeWidget->addCategoryItem(tr("Popular Groups"), QIcon(), false); + mOtherGroups = ui.groupTreeWidget->addCategoryItem(tr("Other Groups"), QIcon(), false); + + /* Add the New Group button */ + QToolButton *newGroupButton = new QToolButton(this); + newGroupButton->setIcon(QIcon(":/icons/png/add.png")); + newGroupButton->setToolTip(tr("Create Group")); + connect(newGroupButton, SIGNAL(clicked()), this, SLOT(OpenOrShowAddGroupDialog())); + ui.groupTreeWidget->addToolButton(newGroupButton); + // load settings + processSettings(true); } WikiDialog::~WikiDialog() { + // save settings + processSettings(false); + delete(mWikiQueue); } +void WikiDialog::processSettings(bool load) +{ + Settings->beginGroup("WikiDialog"); + + if (load) { + // load settings + + // state of splitter + ui.listSplitter->restoreState(Settings->value("SplitterList").toByteArray()); + } else { + // save settings + + // state of splitter + Settings->setValue("SplitterList", ui.listSplitter->saveState()); + } + + Settings->endGroup(); +} + void WikiDialog::OpenOrShowAddPageDialog() { RsGxsGroupId groupId = getSelectedGroup(); @@ -155,7 +187,7 @@ void WikiDialog::OpenOrShowAddGroupDialog() void WikiDialog::newGroup() { - WikiGroupDialog cf(mWikiQueue, this); + WikiGroupDialog cf(this); cf.exec (); } @@ -169,7 +201,7 @@ void WikiDialog::showGroupDetails() return; } - WikiGroupDialog cf(mWikiQueue, rsWiki->getTokenService(), GxsGroupDialog::MODE_SHOW, groupId, this); + WikiGroupDialog cf(GxsGroupDialog::MODE_SHOW, groupId, this); cf.exec (); } @@ -183,7 +215,7 @@ void WikiDialog::editGroupDetails() return; } - WikiGroupDialog cf(mWikiQueue, rsWiki->getTokenService(), GxsGroupDialog::MODE_EDIT, groupId, this); + WikiGroupDialog cf(GxsGroupDialog::MODE_EDIT, groupId, this); cf.exec (); } @@ -436,8 +468,8 @@ void WikiDialog::requestWikiPage(const RsGxsGrpMsgIdPair &msgId) uint32_t token; GxsMsgReq msgIds; - std::vector &vect_msgIds = msgIds[msgId.first]; - vect_msgIds.push_back(msgId.second); + std::set &set_msgIds = msgIds[msgId.first]; + set_msgIds.insert(msgId.second); mWikiQueue->requestMsgInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, msgIds, WIKIDIALOG_WIKI_PAGE); } @@ -698,10 +730,10 @@ void WikiDialog::updateDisplay(bool complete) requestGroupMeta(); } else { /* Update all groups of changed messages */ - std::map > msgIds; + std::map > msgIds; getAllMsgIds(msgIds); - std::map >::iterator msgIt; + std::map >::iterator msgIt; for (msgIt = msgIds.begin(); msgIt != msgIds.end(); ++msgIt) { wikiGroupChanged(QString::fromStdString(msgIt->first.toStdString())); } diff --git a/retroshare-gui/src/gui/WikiPoos/WikiDialog.h b/retroshare-gui/src/gui/WikiPoos/WikiDialog.h index c9c18c8b9..cf4154326 100644 --- a/retroshare-gui/src/gui/WikiPoos/WikiDialog.h +++ b/retroshare-gui/src/gui/WikiPoos/WikiDialog.h @@ -32,7 +32,7 @@ #include -#define IMAGE_WIKI ":/images/wikibook_32.png" +#define IMAGE_WIKI ":/icons/png/wiki.png" class WikiAddDialog; class WikiEditDialog; @@ -44,7 +44,7 @@ class WikiDialog : public RsGxsUpdateBroadcastPage, public TokenResponse public: WikiDialog(QWidget *parent = 0); ~WikiDialog(); - + virtual QIcon iconPixmap() const { return QIcon(IMAGE_WIKI) ; } //MainPage virtual QString pageName() const { return tr("Wiki Pages") ; } //MainPage virtual QString helpText() const { return ""; } //MainPage @@ -69,39 +69,38 @@ private slots: // GroupTreeWidget stuff. void groupListCustomPopupMenu(QPoint point); - void subscribeToGroup(); - void unsubscribeToGroup(); + void subscribeToGroup(); + void unsubscribeToGroup(); void wikiGroupChanged(const QString &groupId); - + void todo(); private: -void clearWikiPage(); -void clearGroupTree(); + void clearWikiPage(); + void clearGroupTree(); -void updateWikiPage(const RsWikiSnapshot &page); + void updateWikiPage(const RsWikiSnapshot &page); -bool getSelectedPage(RsGxsGroupId &groupId, RsGxsMessageId &pageId, RsGxsMessageId &origPageId); -std::string getSelectedPage(); -const RsGxsGroupId &getSelectedGroup(); + bool getSelectedPage(RsGxsGroupId &groupId, RsGxsMessageId &pageId, RsGxsMessageId &origPageId); + std::string getSelectedPage(); + const RsGxsGroupId &getSelectedGroup(); + // Using GroupTreeWidget. + void wikiSubscribe(bool subscribe); + void GroupMetaDataToGroupItemInfo(const RsGroupMetaData &groupInfo, GroupItemInfo &groupItemInfo); + void insertGroupsData(const std::list &wikiList); - // Using GroupTreeWidget. -void wikiSubscribe(bool subscribe); -void GroupMetaDataToGroupItemInfo(const RsGroupMetaData &groupInfo, GroupItemInfo &groupItemInfo); -void insertGroupsData(const std::list &wikiList); + void processSettings(bool load); + void requestGroupMeta(); + void loadGroupMeta(const uint32_t &token); -void requestGroupMeta(); -void loadGroupMeta(const uint32_t &token); - -void requestPages(const std::list &groupIds); -void loadPages(const uint32_t &token); - -void requestWikiPage(const RsGxsGrpMsgIdPair &msgId); -void loadWikiPage(const uint32_t &token); + void requestPages(const std::list &groupIds); + void loadPages(const uint32_t &token); + void requestWikiPage(const RsGxsGrpMsgIdPair &msgId); + void loadWikiPage(const uint32_t &token); TokenQueue *mWikiQueue; @@ -110,7 +109,7 @@ void loadWikiPage(const uint32_t &token); WikiEditDialog *mEditDialog; std::string mGroupSelected; - RsGxsMessageId mPageSelected; + RsGxsMessageId mPageSelected; std::string mModSelected; @@ -118,7 +117,7 @@ void loadWikiPage(const uint32_t &token); QTreeWidgetItem *mSubscribedGroups; QTreeWidgetItem *mPopularGroups; QTreeWidgetItem *mOtherGroups; - RsGxsGroupId mGroupId; // From GroupTreeWidget + RsGxsGroupId mGroupId; // From GroupTreeWidget /* UI - from Designer */ Ui::WikiDialog ui; diff --git a/retroshare-gui/src/gui/WikiPoos/WikiDialog.ui b/retroshare-gui/src/gui/WikiPoos/WikiDialog.ui index e40acfcc4..7a86e6488 100644 --- a/retroshare-gui/src/gui/WikiPoos/WikiDialog.ui +++ b/retroshare-gui/src/gui/WikiPoos/WikiDialog.ui @@ -27,7 +27,7 @@ 0 - + Qt::Horizontal @@ -36,83 +36,6 @@ 0 - - - - - 16777215 - 30 - - - - QFrame::Box - - - QFrame::Sunken - - - - 2 - - - - - - 24 - 24 - - - - - - - :/images/wikibook_32.png - - - true - - - - - - - - 10 - 75 - true - - - - Wiki Pages - - - - - - - New Group - - - - :/images/resource-group-new.png:/images/resource-group-new.png - - - - 24 - 24 - - - - Qt::ToolButtonIconOnly - - - true - - - - - - @@ -123,12 +46,21 @@ 0 0 - 232 - 462 + 241 + 510 - + + 0 + + + 0 + + + 0 + + 0 @@ -186,7 +118,16 @@ QFrame::Sunken - + + 2 + + + 2 + + + 2 + + 2 @@ -204,8 +145,8 @@ << - - :/images/arrow-left.png:/images/arrow-left.png + + :/icons/png/arrow-left.png:/icons/png/arrow-left.png @@ -233,8 +174,8 @@ >> - - :/images/arrow-right.png:/images/arrow-right.png + + :/icons/png/arrow-right.png:/icons/png/arrow-right.png @@ -261,7 +202,7 @@ - :/images/republish.png:/images/republish.png + :/images/reload.png:/images/reload.png @@ -284,7 +225,7 @@ - :/images/story-editor.png:/images/story-editor.png + :/images/editpage.png:/images/editpage.png @@ -314,7 +255,7 @@ - :/images/appointment-new.png:/images/appointment-new.png + :/images/addpage.png:/images/addpage.png @@ -340,7 +281,7 @@ - :/images/republish.png:/images/republish.png + :/images/reload.png:/images/reload.png @@ -392,8 +333,8 @@ 0 0 - 501 - 462 + 510 + 480 @@ -443,6 +384,7 @@ + diff --git a/retroshare-gui/src/gui/WikiPoos/WikiEditDialog.cpp b/retroshare-gui/src/gui/WikiPoos/WikiEditDialog.cpp index 2c5fc1b8d..246df55e3 100644 --- a/retroshare-gui/src/gui/WikiPoos/WikiEditDialog.cpp +++ b/retroshare-gui/src/gui/WikiPoos/WikiEditDialog.cpp @@ -414,7 +414,7 @@ void WikiEditDialog::setNewPage() ui.groupBox_History->hide(); ui.pushButton_History->setText(tr("Show Edit History")); - ui.headerFrame->setHeaderImage(QPixmap(":/images/appointment-new_64.png")); + ui.headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/images/addpage.png")); ui.headerFrame->setHeaderText(tr("Create New Wiki Page")); setWindowTitle(tr("Create New Wiki Page")); @@ -569,7 +569,7 @@ void WikiEditDialog::setupData(const RsGxsGroupId &groupId, const RsGxsMessageId requestPage(msgId); } - ui.headerFrame->setHeaderImage(QPixmap(":/images/story-editor_48.png")); + ui.headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/images/editpage.png")); ui.headerFrame->setHeaderText(tr("Edit Wiki Page")); setWindowTitle(tr("Edit Wiki Page")); @@ -622,8 +622,8 @@ void WikiEditDialog::requestPage(const RsGxsGrpMsgIdPair &msgId) opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; GxsMsgReq msgIds; - std::vector &vect_msgIds = msgIds[msgId.first]; - vect_msgIds.push_back(msgId.second); + std::set &set_msgIds = msgIds[msgId.first]; + set_msgIds.insert(msgId.second); uint32_t token; mWikiQueue->requestMsgInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, msgIds, WIKIEDITDIALOG_PAGE); diff --git a/retroshare-gui/src/gui/WikiPoos/Wiki_images.qrc b/retroshare-gui/src/gui/WikiPoos/Wiki_images.qrc index 5bee8bea2..ec2c2118e 100644 --- a/retroshare-gui/src/gui/WikiPoos/Wiki_images.qrc +++ b/retroshare-gui/src/gui/WikiPoos/Wiki_images.qrc @@ -1,17 +1,7 @@ - - images/appointment-new.png - images/appointment-new_64.png - images/arrow-left.png - images/arrow-right.png - images/book2_32.png - images/story-editor.png - images/story-editor_48.png - images/republish.png - images/resource-group-new.png - images/resource-group_64.png - images/resource-group-new_48.png - images/resource-group-new_64.png - images/wikibook_32.png - + + images/addpage.png + images/editpage.png + images/reload.png + diff --git a/retroshare-gui/src/gui/WikiPoos/images/addpage.png b/retroshare-gui/src/gui/WikiPoos/images/addpage.png new file mode 100644 index 000000000..298d7eac6 Binary files /dev/null and b/retroshare-gui/src/gui/WikiPoos/images/addpage.png differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/appointment-new.png b/retroshare-gui/src/gui/WikiPoos/images/appointment-new.png deleted file mode 100644 index aa5207585..000000000 Binary files a/retroshare-gui/src/gui/WikiPoos/images/appointment-new.png and /dev/null differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/appointment-new_64.png b/retroshare-gui/src/gui/WikiPoos/images/appointment-new_64.png deleted file mode 100644 index 7b99cd877..000000000 Binary files a/retroshare-gui/src/gui/WikiPoos/images/appointment-new_64.png and /dev/null differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/arrow-left-double.png b/retroshare-gui/src/gui/WikiPoos/images/arrow-left-double.png deleted file mode 100644 index b5f026336..000000000 Binary files a/retroshare-gui/src/gui/WikiPoos/images/arrow-left-double.png and /dev/null differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/arrow-left.png b/retroshare-gui/src/gui/WikiPoos/images/arrow-left.png deleted file mode 100644 index befac61d1..000000000 Binary files a/retroshare-gui/src/gui/WikiPoos/images/arrow-left.png and /dev/null differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/arrow-right-double.png b/retroshare-gui/src/gui/WikiPoos/images/arrow-right-double.png deleted file mode 100644 index 082ba7135..000000000 Binary files a/retroshare-gui/src/gui/WikiPoos/images/arrow-right-double.png and /dev/null differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/arrow-right.png b/retroshare-gui/src/gui/WikiPoos/images/arrow-right.png deleted file mode 100644 index a8267ff09..000000000 Binary files a/retroshare-gui/src/gui/WikiPoos/images/arrow-right.png and /dev/null differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/book2_128.png b/retroshare-gui/src/gui/WikiPoos/images/book2_128.png deleted file mode 100644 index d612516f2..000000000 Binary files a/retroshare-gui/src/gui/WikiPoos/images/book2_128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/book2_16.png b/retroshare-gui/src/gui/WikiPoos/images/book2_16.png deleted file mode 100644 index b6c943717..000000000 Binary files a/retroshare-gui/src/gui/WikiPoos/images/book2_16.png and /dev/null differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/book2_32.png b/retroshare-gui/src/gui/WikiPoos/images/book2_32.png deleted file mode 100644 index 2f2432548..000000000 Binary files a/retroshare-gui/src/gui/WikiPoos/images/book2_32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/book2_48.png b/retroshare-gui/src/gui/WikiPoos/images/book2_48.png deleted file mode 100644 index a0ff7814d..000000000 Binary files a/retroshare-gui/src/gui/WikiPoos/images/book2_48.png and /dev/null differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/book2_64.png b/retroshare-gui/src/gui/WikiPoos/images/book2_64.png deleted file mode 100644 index ac460f5fd..000000000 Binary files a/retroshare-gui/src/gui/WikiPoos/images/book2_64.png and /dev/null differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/editpage.png b/retroshare-gui/src/gui/WikiPoos/images/editpage.png new file mode 100644 index 000000000..aae82487d Binary files /dev/null and b/retroshare-gui/src/gui/WikiPoos/images/editpage.png differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/reload.png b/retroshare-gui/src/gui/WikiPoos/images/reload.png new file mode 100644 index 000000000..13e859134 Binary files /dev/null and b/retroshare-gui/src/gui/WikiPoos/images/reload.png differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/republish.png b/retroshare-gui/src/gui/WikiPoos/images/republish.png deleted file mode 100644 index afa2a9d77..000000000 Binary files a/retroshare-gui/src/gui/WikiPoos/images/republish.png and /dev/null differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/resource-group-new.png b/retroshare-gui/src/gui/WikiPoos/images/resource-group-new.png deleted file mode 100644 index cd5ea5f14..000000000 Binary files a/retroshare-gui/src/gui/WikiPoos/images/resource-group-new.png and /dev/null differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/resource-group-new_48.png b/retroshare-gui/src/gui/WikiPoos/images/resource-group-new_48.png deleted file mode 100644 index ae785fc54..000000000 Binary files a/retroshare-gui/src/gui/WikiPoos/images/resource-group-new_48.png and /dev/null differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/resource-group-new_64.png b/retroshare-gui/src/gui/WikiPoos/images/resource-group-new_64.png deleted file mode 100644 index 70b8f13b3..000000000 Binary files a/retroshare-gui/src/gui/WikiPoos/images/resource-group-new_64.png and /dev/null differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/resource-group.png b/retroshare-gui/src/gui/WikiPoos/images/resource-group.png deleted file mode 100644 index 2b37baddd..000000000 Binary files a/retroshare-gui/src/gui/WikiPoos/images/resource-group.png and /dev/null differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/resource-group_22.png b/retroshare-gui/src/gui/WikiPoos/images/resource-group_22.png deleted file mode 100644 index 551a2c4a6..000000000 Binary files a/retroshare-gui/src/gui/WikiPoos/images/resource-group_22.png and /dev/null differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/resource-group_64.png b/retroshare-gui/src/gui/WikiPoos/images/resource-group_64.png deleted file mode 100644 index 84cfa89fa..000000000 Binary files a/retroshare-gui/src/gui/WikiPoos/images/resource-group_64.png and /dev/null differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/story-editor.png b/retroshare-gui/src/gui/WikiPoos/images/story-editor.png deleted file mode 100644 index 296e08797..000000000 Binary files a/retroshare-gui/src/gui/WikiPoos/images/story-editor.png and /dev/null differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/story-editor_48.png b/retroshare-gui/src/gui/WikiPoos/images/story-editor_48.png deleted file mode 100644 index 18e300d2e..000000000 Binary files a/retroshare-gui/src/gui/WikiPoos/images/story-editor_48.png and /dev/null differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/view-refresh.png b/retroshare-gui/src/gui/WikiPoos/images/view-refresh.png deleted file mode 100644 index afa2a9d77..000000000 Binary files a/retroshare-gui/src/gui/WikiPoos/images/view-refresh.png and /dev/null differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/wikibook_128.png b/retroshare-gui/src/gui/WikiPoos/images/wikibook_128.png deleted file mode 100644 index 2c879d444..000000000 Binary files a/retroshare-gui/src/gui/WikiPoos/images/wikibook_128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/wikibook_32.png b/retroshare-gui/src/gui/WikiPoos/images/wikibook_32.png deleted file mode 100644 index a443e7021..000000000 Binary files a/retroshare-gui/src/gui/WikiPoos/images/wikibook_32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/wikibook_48.png b/retroshare-gui/src/gui/WikiPoos/images/wikibook_48.png deleted file mode 100644 index 4c7d37d88..000000000 Binary files a/retroshare-gui/src/gui/WikiPoos/images/wikibook_48.png and /dev/null differ diff --git a/retroshare-gui/src/gui/WikiPoos/images/wikibook_64.png b/retroshare-gui/src/gui/WikiPoos/images/wikibook_64.png deleted file mode 100644 index 4a98cada7..000000000 Binary files a/retroshare-gui/src/gui/WikiPoos/images/wikibook_64.png and /dev/null differ diff --git a/retroshare-gui/src/gui/chat/ChatLobbyDialog.cpp b/retroshare-gui/src/gui/chat/ChatLobbyDialog.cpp index d5bbc3d2b..66ebb8e9e 100644 --- a/retroshare-gui/src/gui/chat/ChatLobbyDialog.cpp +++ b/retroshare-gui/src/gui/chat/ChatLobbyDialog.cpp @@ -59,12 +59,14 @@ #define ROLE_SORT Qt::UserRole + 1 const static uint32_t timeToInactivity = 60 * 10; // in seconds +const static uint32_t timeToInactivity2 = 60 * 5; // in seconds /** Default constructor */ ChatLobbyDialog::ChatLobbyDialog(const ChatLobbyId& lid, QWidget *parent, Qt::WindowFlags flags) - : ChatDialog(parent, flags), lobbyId(lid), + : ChatDialog(parent, flags), lobbyId(lid), mWindowedSetted(false), mPCWindow(nullptr), bullet_red_128(":/icons/bullet_red_128.png"), bullet_grey_128(":/icons/bullet_grey_128.png"), - bullet_green_128(":/icons/bullet_green_128.png"), bullet_yellow_128(":/icons/bullet_yellow_128.png") + bullet_green_128(":/icons/bullet_green_128.png"), bullet_yellow_128(":/icons/bullet_yellow_128.png"), + bullet_blue_128(":/icons/bullet_blue_128.png") { /* Invoke Qt Designer generated QObject setup routine */ ui.setupUi(this); @@ -90,11 +92,11 @@ ChatLobbyDialog::ChatLobbyDialog(const ChatLobbyId& lid, QWidget *parent, Qt::Wi QHeaderView_setSectionResizeModeColumn(header, COLUMN_NAME, QHeaderView::Stretch); muteAct = new QAction(QIcon(), tr("Mute participant"), this); - voteNegativeAct = new QAction(QIcon(":/icons/png/thumbs-down.png"), tr("Ban this person (Sets negative opinion)"), this); - voteNeutralAct = new QAction(QIcon(":/icons/png/thumbs-neutral.png"), tr("Give neutral opinion"), this); - votePositiveAct = new QAction(QIcon(":/icons/png/thumbs-up.png"), tr("Give positive opinion"), this); - distantChatAct = new QAction(QIcon(":/images/chat_24.png"), tr("Start private chat"), this); - sendMessageAct = new QAction(QIcon(":/images/mail_new.png"), tr("Send Message"), this); + voteNegativeAct = new QAction(FilesDefs::getIconFromQtResourcePath(":/icons/png/thumbs-down.png"), tr("Ban this person (Sets negative opinion)"), this); + voteNeutralAct = new QAction(FilesDefs::getIconFromQtResourcePath(":/icons/png/thumbs-neutral.png"), tr("Give neutral opinion"), this); + votePositiveAct = new QAction(FilesDefs::getIconFromQtResourcePath(":/icons/png/thumbs-up.png"), tr("Give positive opinion"), this); + distantChatAct = new QAction(FilesDefs::getIconFromQtResourcePath(":/icons/png/chats.png"), tr("Start private chat"), this); + sendMessageAct = new QAction(FilesDefs::getIconFromQtResourcePath(":/icons/mail/write-mail.png"), tr("Send Message"), this); showInPeopleAct = new QAction(QIcon(), tr("Show author in people tab"), this); QActionGroup *sortgrp = new QActionGroup(this); @@ -129,6 +131,15 @@ ChatLobbyDialog::ChatLobbyDialog(const ChatLobbyId& lid, QWidget *parent, Qt::Wi double scaler_factor = S > 25 ? 2.4 : 1.8; QSize icon_size(scaler_factor * S, scaler_factor * S); + // Add a button to undock dialog. + // + undockButton = new QToolButton; + undockButton->setText(QString()); + undockButton->setAutoRaise(true); + connect(undockButton, SIGNAL(clicked()), this , SLOT(toggleWindowed())); + + getChatWidget()->addTitleBarWidget(undockButton) ; + // Add a button to invite friends. // inviteFriendsButton = new QToolButton ; @@ -143,7 +154,7 @@ ChatLobbyDialog::ChatLobbyDialog(const ChatLobbyId& lid, QWidget *parent, Qt::Wi { QIcon icon ; - icon.addPixmap(QPixmap(":/icons/png/invite.png")) ; + icon.addPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/invite.png")) ; inviteFriendsButton->setIcon(icon) ; inviteFriendsButton->setIconSize(icon_size); } @@ -185,7 +196,7 @@ ChatLobbyDialog::ChatLobbyDialog(const ChatLobbyId& lid, QWidget *parent, Qt::Wi { QIcon icon ; - icon.addPixmap(QPixmap(":/icons/png/leave.png")) ; + icon.addPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/leave.png")) ; unsubscribeButton->setIcon(icon) ; unsubscribeButton->setIconSize(icon_size); } @@ -201,8 +212,12 @@ ChatLobbyDialog::ChatLobbyDialog(const ChatLobbyId& lid, QWidget *parent, Qt::Wi void ChatLobbyDialog::leaveLobby() { - emit lobbyLeave(id()) ; + emit dialogClose(this); + + if (mPCWindow) + mPCWindow = nullptr;// Windows deleted by events just before. } + void ChatLobbyDialog::inviteFriends() { std::cerr << "Inviting friends" << std::endl; @@ -404,12 +419,8 @@ void ChatLobbyDialog::init(const ChatId &/*id*/, const QString &/*title*/) lastUpdateListTime = 0; - // add to window - - ChatLobbyWidget *chatLobbyPage = dynamic_cast(MainWindow::getPage(MainWindow::ChatLobby)); - if (chatLobbyPage) { - chatLobbyPage->addChatPage(this) ; - } + // add to stacked lobby list + setWindowed(false); /** List of muted Participants */ mutedParticipants.clear() ; @@ -421,11 +432,15 @@ void ChatLobbyDialog::init(const ChatId &/*id*/, const QString &/*title*/) /** Destructor. */ ChatLobbyDialog::~ChatLobbyDialog() { - // announce leaving of lobby - + if (mPCWindow) + { + mPCWindow->removeDialog(this); + mPCWindow = nullptr; + } // check that the lobby still exists. - if (mChatId.isLobbyId()) - rsMsgs->sendLobbyStatusPeerLeaving(mChatId.toLobbyId()); + // announce leaving of lobby + if (mChatId.isLobbyId()) + rsMsgs->sendLobbyStatusPeerLeaving(mChatId.toLobbyId()); // save settings processSettings(false); @@ -601,10 +616,12 @@ void ChatLobbyDialog::updateParticipantsList() else widgetitem = dynamic_cast(qlFoundParticipants.at(0)); + //TODO (Phenom): Add qproperty for these text colors in stylesheets + // As palette is not updated by stylesheet if (isParticipantMuted(it2->first)) { - widgetitem->setTextColor(COLUMN_NAME,QColor(255,0,0)); + widgetitem->setData(COLUMN_NAME, Qt::ForegroundRole, QColor(255,0,0)); } else { - widgetitem->setTextColor(COLUMN_NAME,ui.participantsList->palette().color(QPalette::Active, QPalette::Text)); + widgetitem->setData(COLUMN_NAME, Qt::ForegroundRole, QVariant()); } time_t tLastAct=widgetitem->text(COLUMN_ACTIVITY).toInt(); @@ -617,13 +634,15 @@ void ChatLobbyDialog::updateParticipantsList() widgetitem->setIcon(COLUMN_ICON, bullet_red_128); else if (tLastAct + timeToInactivity < now) widgetitem->setIcon(COLUMN_ICON, bullet_grey_128); + else if (tLastAct + timeToInactivity2 < now) + widgetitem->setIcon(COLUMN_ICON, bullet_yellow_128); else widgetitem->setIcon(COLUMN_ICON, bullet_green_128); RsGxsId gxs_id; rsMsgs->getIdentityForChatLobby(lobbyId, gxs_id); - if (RsGxsId(participant.toStdString()) == gxs_id) widgetitem->setIcon(COLUMN_ICON, bullet_yellow_128); + if (RsGxsId(participant.toStdString()) == gxs_id) widgetitem->setIcon(COLUMN_ICON, bullet_blue_128); widgetitem->updateBannedState(); @@ -910,11 +929,17 @@ void ChatLobbyDialog::showDialog(uint chatflags) { if (chatflags & RS_CHAT_FOCUS) { - MainWindow::showWindow(MainWindow::ChatLobby); - MainPage *p = MainWindow::getPage(MainWindow::ChatLobby); + if (isWindowed() && mPCWindow) { + mPCWindow->showDialog(this, chatflags); + } + else + { + MainWindow::showWindow(MainWindow::ChatLobby); + MainPage *p = MainWindow::getPage(MainWindow::ChatLobby); - if(p != NULL) - dynamic_cast(p)->setCurrentChatPage(this) ; + if(p) + dynamic_cast(p)->setCurrentChatPage(this) ; + } } } @@ -941,3 +966,54 @@ void ChatLobbyDialog::filterIds() ui.participantsList->filterItems(filterColumn, text); } + +void ChatLobbyDialog::setWindowed(bool windowed) +{ + if (mWindowedSetted && (windowed == isWindowed()) ) + { + RsErr() << __PRETTY_FUNCTION__ << " Attempt to set windowed same as last state." << std::endl; + return; + } + mWindowedSetted = true; + // just empiric values + qreal S = QFontMetricsF(font()).height(); + int size = static_cast(S > 25.0 ? 2.4 * S : 1.8 * S); + QSize icon_size(size, size); + + QIcon icon ; + // chatLobbyPage could be NULL for first autosubscribe lobby as Dialog is created before main widget + ChatLobbyWidget *chatLobbyPage = dynamic_cast(MainWindow::getPage(MainWindow::ChatLobby)); + if (!mPCWindow) + mPCWindow = PopupChatWindow::getWindow(true); + + if (windowed) + { + if (chatLobbyPage) + chatLobbyPage->removeChatPage(this); + if (mPCWindow) + mPCWindow->addDialog(this); + + undockButton->setToolTip(tr("Redock to Main window")); + icon.addPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/dock.png")) ; + undockButton->setIcon(icon) ; + undockButton->setIconSize(icon_size); + } + else + { + if (mPCWindow) + { + mPCWindow->removeDialog(this); + mPCWindow = nullptr; + } + if (chatLobbyPage) + chatLobbyPage->addChatPage(this); + + undockButton->setToolTip(tr("Undock to a new window")); + icon.addPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/undock.png")) ; + undockButton->setIcon(icon) ; + undockButton->setIconSize(icon_size); + } + show(); + if (chatLobbyPage)// If not defined, we are on autosubscribe loop of lobby widget constructor. So don't recall it. + showDialog(RS_CHAT_FOCUS); +} diff --git a/retroshare-gui/src/gui/chat/ChatLobbyDialog.h b/retroshare-gui/src/gui/chat/ChatLobbyDialog.h index 688cfb1c9..429a5bfb4 100644 --- a/retroshare-gui/src/gui/chat/ChatLobbyDialog.h +++ b/retroshare-gui/src/gui/chat/ChatLobbyDialog.h @@ -25,6 +25,7 @@ #include "ui_ChatLobbyDialog.h" #include "gui/common/RSTreeWidgetItem.h" #include "ChatDialog.h" +#include "PopupChatWindow.h" Q_DECLARE_METATYPE(RsGxsId) Q_DECLARE_METATYPE(QList) @@ -51,16 +52,20 @@ public: ChatLobbyId id() const { return lobbyId ;} void sortParcipants(); + inline bool isWindowed() const { return dynamic_cast(this->window()) != nullptr; } + +public slots: + void leaveLobby() ; private slots: void participantsTreeWidgetCustomPopupMenu( QPoint point ); void textBrowserAskContextMenu(QMenu* contextMnu, QString anchorForPosition, const QPoint point); void inviteFriends() ; - void leaveLobby() ; void filterChanged(const QString &text); - void showInPeopleTab(); + void showInPeopleTab(); + void toggleWindowed(){setWindowed(!isWindowed());} + void setWindowed(bool windowed); signals: - void lobbyLeave(ChatLobbyId) ; void typingEventReceived(ChatLobbyId) ; void messageReceived(bool incoming, ChatLobbyId lobby_id, QDateTime time, QString senderName, QString msg) ; void peerJoined(ChatLobbyId) ; @@ -103,9 +108,13 @@ private: RSTreeWidgetItemCompareRole *mParticipantCompareRole ; - QToolButton *inviteFriendsButton ; + QToolButton *undockButton ; + QToolButton *inviteFriendsButton ; QToolButton *unsubscribeButton ; + bool mWindowedSetted; + PopupChatWindow* mPCWindow; + /** Qt Designer generated object */ Ui::ChatLobbyDialog ui; @@ -125,7 +134,7 @@ private: GxsIdChooser *ownIdChooser ; //icons cache - QIcon bullet_red_128, bullet_grey_128, bullet_green_128, bullet_yellow_128; + QIcon bullet_red_128, bullet_grey_128, bullet_green_128, bullet_yellow_128, bullet_blue_128; }; #endif diff --git a/retroshare-gui/src/gui/chat/ChatLobbyUserNotify.cpp b/retroshare-gui/src/gui/chat/ChatLobbyUserNotify.cpp index 81cb47846..64798a011 100644 --- a/retroshare-gui/src/gui/chat/ChatLobbyUserNotify.cpp +++ b/retroshare-gui/src/gui/chat/ChatLobbyUserNotify.cpp @@ -23,6 +23,7 @@ #include #include +#include "gui/common/FilesDefs.h" #include "ChatLobbyUserNotify.h" #include "gui/ChatLobbyWidget.h" @@ -104,12 +105,12 @@ void ChatLobbyUserNotify::setTextCaseSensitive(bool value) QIcon ChatLobbyUserNotify::getIcon() { - return QIcon(":/icons/png/chat-lobbies.png"); + return FilesDefs::getIconFromQtResourcePath(":/icons/png/chat-lobbies.png"); } QIcon ChatLobbyUserNotify::getMainIcon(bool hasNew) { - return hasNew ? QIcon(":/icons/png/chat-lobbies-notify.png") : QIcon(":/icons/png/chat-lobbies.png"); + return hasNew ? FilesDefs::getIconFromQtResourcePath(":/icons/png/chat-lobbies-notify.png") : FilesDefs::getIconFromQtResourcePath(":/icons/png/chat-lobbies.png"); } unsigned int ChatLobbyUserNotify::getNewCount() @@ -164,7 +165,7 @@ void ChatLobbyUserNotify::iconClicked() ChatLobbyInfo clInfo; if (rsMsgs->getChatLobbyInfo(clId,clInfo)) strLobbyName=QString::fromUtf8(clInfo.lobby_name.c_str()) ; - icoLobby=(clInfo.lobby_flags & RS_CHAT_LOBBY_FLAGS_PUBLIC) ? QIcon(":/images/chat_red24.png") : QIcon(":/images/chat_x24.png"); + icoLobby=(clInfo.lobby_flags & RS_CHAT_LOBBY_FLAGS_PUBLIC) ? FilesDefs::getIconFromQtResourcePath(":/images/chat_red24.png") : FilesDefs::getIconFromQtResourcePath(":/images/chat_x24.png"); bFound=true; break; } diff --git a/retroshare-gui/src/gui/chat/ChatLobbyUserNotify.h b/retroshare-gui/src/gui/chat/ChatLobbyUserNotify.h index fd95511b4..620d49fbe 100644 --- a/retroshare-gui/src/gui/chat/ChatLobbyUserNotify.h +++ b/retroshare-gui/src/gui/chat/ChatLobbyUserNotify.h @@ -63,6 +63,7 @@ public: QString textToNotify() { return _textToNotify.join("\n");} void setTextCaseSensitive(bool value); bool isTextCaseSensitive() {return _bTextCaseSensitive;} + virtual QString textInfo() const override { return tr("mention(s)"); } signals: void countChanged(ChatLobbyId id, unsigned int count); diff --git a/retroshare-gui/src/gui/chat/ChatTabWidget.cpp b/retroshare-gui/src/gui/chat/ChatTabWidget.cpp index 7642f3827..03357a3e4 100644 --- a/retroshare-gui/src/gui/chat/ChatTabWidget.cpp +++ b/retroshare-gui/src/gui/chat/ChatTabWidget.cpp @@ -22,13 +22,14 @@ #include +#include "gui/common/FilesDefs.h" #include "ChatTabWidget.h" #include "ui_ChatTabWidget.h" #include "ChatDialog.h" #include "gui/common/StatusDefs.h" #include "rshare.h" -#define IMAGE_TYPING ":/images/typing.png" +#define IMAGE_TYPING ":/icons/png/typing.png" #define IMAGE_CHAT ":/images/chat.png" ChatTabWidget::ChatTabWidget(QWidget *parent) : @@ -102,9 +103,9 @@ void ChatTabWidget::tabInfoChanged(ChatDialog *dialog) if (tab >= 0) { if (dialog->isTyping()) { setBlinking(tab, false); - setTabIcon(tab, QIcon(IMAGE_TYPING)); + setTabIcon(tab, FilesDefs::getIconFromQtResourcePath(IMAGE_TYPING)); } else if (dialog->hasNewMessages()) { - setTabIcon(tab, QIcon(IMAGE_CHAT)); + setTabIcon(tab, FilesDefs::getIconFromQtResourcePath(IMAGE_CHAT)); if (dialog->notifyBlink()) { setBlinking(tab, true); } else { @@ -148,9 +149,9 @@ void ChatTabWidget::getInfo(bool &isTyping, bool &hasNewMessage, QIcon *icon) if (icon) { if (isTyping) { - *icon = QIcon(IMAGE_TYPING); + *icon = FilesDefs::getIconFromQtResourcePath(IMAGE_TYPING); } else if (hasNewMessage) { - *icon = QIcon(IMAGE_CHAT); + *icon = FilesDefs::getIconFromQtResourcePath(IMAGE_CHAT); } else { cd = dynamic_cast(currentWidget()); if (cd && cd->hasPeerStatus()) { diff --git a/retroshare-gui/src/gui/chat/ChatUserNotify.cpp b/retroshare-gui/src/gui/chat/ChatUserNotify.cpp index ecb563938..2e9fb2d1a 100644 --- a/retroshare-gui/src/gui/chat/ChatUserNotify.cpp +++ b/retroshare-gui/src/gui/chat/ChatUserNotify.cpp @@ -20,6 +20,7 @@ * * *******************************************************************************/ +#include "gui/common/FilesDefs.h" #include "ChatUserNotify.h" #include "gui/notifyqt.h" #include "gui/MainWindow.h" @@ -75,12 +76,12 @@ bool ChatUserNotify::hasSetting(QString *name, QString *group) QIcon ChatUserNotify::getIcon() { - return QIcon(":/images/chat.png"); + return FilesDefs::getIconFromQtResourcePath(":/images/chat.png"); } QIcon ChatUserNotify::getMainIcon(bool hasNew) { - return hasNew ? QIcon(":/icons/png/network-notify.png") : QIcon(":/icons/png/network.png"); + return hasNew ? FilesDefs::getIconFromQtResourcePath(":/icons/png/network-notify.png") : FilesDefs::getIconFromQtResourcePath(":/icons/png/network.png"); } unsigned int ChatUserNotify::getNewCount() diff --git a/retroshare-gui/src/gui/chat/ChatUserNotify.h b/retroshare-gui/src/gui/chat/ChatUserNotify.h index f70b37a54..8f6502847 100644 --- a/retroshare-gui/src/gui/chat/ChatUserNotify.h +++ b/retroshare-gui/src/gui/chat/ChatUserNotify.h @@ -41,6 +41,7 @@ public: ~ChatUserNotify(); virtual bool hasSetting(QString *name, QString *group); + virtual QString textInfo() const override { return tr("mention(s)"); } private slots: void chatMessageReceived(ChatMessage msg); diff --git a/retroshare-gui/src/gui/chat/ChatWidget.cpp b/retroshare-gui/src/gui/chat/ChatWidget.cpp index 0922793f6..7b8c532d0 100644 --- a/retroshare-gui/src/gui/chat/ChatWidget.cpp +++ b/retroshare-gui/src/gui/chat/ChatWidget.cpp @@ -115,6 +115,7 @@ ChatWidget::ChatWidget(QWidget *parent) //ui->sendButton->setFixedHeight(iconHeight); ui->sendButton->setIconSize(iconSize); ui->typingLabel->setMaximumHeight(QFontMetricsF(font()).height()*1.2); + ui->fontcolorButton->setIconSize(iconSize); //Initialize search iCharToStartSearch=Settings->getChatSearchCharToStartSearch(); @@ -189,7 +190,7 @@ ChatWidget::ChatWidget(QWidget *parent) ui->hashBox->setDropWidget(this); ui->hashBox->setAutoHide(true); - QMenu *fontmenu = new QMenu(tr("Set text font & color")); + QMenu *fontmenu = new QMenu(); fontmenu->addAction(ui->actionChooseFont); fontmenu->addAction(ui->actionChooseColor); fontmenu->addAction(ui->actionResetFont); @@ -198,14 +199,16 @@ ChatWidget::ChatWidget(QWidget *parent) #ifdef USE_CMARK fontmenu->addAction(ui->actionSend_as_CommonMark); #endif + ui->fontcolorButton->setMenu(fontmenu); QMenu *menu = new QMenu(); + menu->addAction(ui->actionMessageHistory); + menu->addSeparator(); + menu->addAction(ui->actionSaveChatHistory); menu->addAction(ui->actionClearChatHistory); menu->addAction(ui->actionDeleteChatHistory); - menu->addAction(ui->actionSaveChatHistory); - menu->addAction(ui->actionMessageHistory); + ui->pushtoolsButton->setMenu(menu); - menu->addMenu(fontmenu); ui->actionSendAsPlainText->setChecked(Settings->getChatSendAsPlainTextByDef()); ui->chatTextEdit->setOnlyPlainText(ui->actionSendAsPlainText->isChecked()); @@ -242,7 +245,7 @@ ChatWidget::ChatWidget(QWidget *parent) //#ifdef ENABLE_DISTANT_CHAT_AND_MSGS // contextMnu->addSeparator(); -// QAction *action = new QAction(QIcon(":/images/pasterslink.png"), tr("Paste/Create private chat or Message link..."), this); +// QAction *action = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/pasterslink.png"), tr("Paste/Create private chat or Message link..."), this); // connect(action, SIGNAL(triggered()), this, SLOT(pasteCreateMsgLink())); // ui->chatTextEdit->addContextMenuAction(action); //#endif @@ -1064,7 +1067,7 @@ void ChatWidget::addChatMsg(bool incoming, const QString &name, const RsGxsId gx rsIdentity->getIdDetails(gxsId, details); bool isUnsigned = !(details.mFlags & RS_IDENTITY_FLAGS_PGP_LINKED); if(isUnsigned && ui->textBrowser->getShowImages()) { - QIcon icon = QIcon(":/icons/anonymous_blue_128.png"); + QIcon icon = FilesDefs::getIconFromQtResourcePath(":/icons/anonymous_blue_128.png"); int height = ui->textBrowser->fontMetrics().height()*0.8; QImage image(icon.pixmap(height,height).toImage()); QByteArray byteArray; @@ -1860,7 +1863,7 @@ void ChatWidget::updatePeersCustomStateString(const QString& /*peer_id*/, const void ChatWidget::updateStatusString(const QString &statusMask, const QString &statusString, bool permanent) { ui->typingLabel->setText(QString(statusMask).arg(trUtf8(statusString.toUtf8()))); // displays info for 5 secs. - ui->typingPixmapLabel->setPixmap(QPixmap(":images/typing.png") ); + ui->typingPixmapLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":icons/png/typing.png") ); if (statusString == "is typing...") { typing = true; @@ -1906,11 +1909,8 @@ void ChatWidget::updateCMPreview() void ChatWidget::quote() { - QString text = ui->textBrowser->textCursor().selection().toPlainText(); - QStringList sl = text.split(QRegExp("[\r\n]"), QString::SkipEmptyParts); - text = sl.join("\n> "); - text.replace(QChar(-4), " "); // Char used when image on text. - emit ui->chatTextEdit->append(QString("> ") + text); + QString text = RsHtml::makeQuotedText(ui->textBrowser); + emit ui->chatTextEdit->append(text); } void ChatWidget::dropPlacemark() diff --git a/retroshare-gui/src/gui/chat/ChatWidget.ui b/retroshare-gui/src/gui/chat/ChatWidget.ui index d8a4125e0..f4d070c11 100644 --- a/retroshare-gui/src/gui/chat/ChatWidget.ui +++ b/retroshare-gui/src/gui/chat/ChatWidget.ui @@ -241,6 +241,9 @@ border-image: url(:/images/closepressed.png) + + 2 + @@ -258,6 +261,9 @@ border-image: url(:/images/closepressed.png) T + + true + Qt::AlignCenter @@ -324,12 +330,12 @@ border-image: url(:/images/closepressed.png) - + - QFrame::Box + QFrame::NoFrame - QFrame::Sunken + QFrame::Plain @@ -412,7 +418,7 @@ border-image: url(:/images/closepressed.png) - :/icons/png/attach-image.png:/icons/png/attach-image.png + :/icons/png/add-image.png:/icons/png/add-image.png @@ -435,7 +441,7 @@ border-image: url(:/images/closepressed.png) - :/icons/png/attach.png:/icons/png/attach.png + :/icons/png/attachements.png:/icons/png/attachements.png @@ -449,13 +455,13 @@ border-image: url(:/images/closepressed.png) - - - Qt::NoFocus + + + Set font & color - :/icons/png/settings.png:/icons/png/settings.png + :/icons/png/font.png:/icons/png/font.png @@ -471,142 +477,6 @@ border-image: url(:/images/closepressed.png) - - - - - 0 - 0 - - - - Qt::NoFocus - - - Qt::CustomContextMenu - - - - :/icons/png/search.png:/icons/png/search.png - - - - 28 - 28 - - - - true - - - true - - - - - - - - - - - 0 - 0 - - - - Qt::NoFocus - - - - :/icons/png/highlight.png:/icons/png/highlight.png - - - - 28 - 28 - - - - true - - - true - - - - - - - - 14 - 28 - - - - - 14 - 28 - - - - Qt::NoFocus - - - - :/images/arrow-left.png:/images/arrow-left.png - - - true - - - - - - - - 14 - 28 - - - - - 14 - 28 - - - - Qt::NoFocus - - - - :/images/arrow-right.png:/images/arrow-right.png - - - true - - - - - - - Qt::NoFocus - - - - :/icons/png/chat-bubble-notify.png:/icons/png/chat-bubble-notify.png - - - - 28 - 28 - - - - true - - - @@ -711,10 +581,10 @@ border-image: url(:/images/closepressed.png) - QFrame::Box + QFrame::NoFrame - QFrame::Sunken + QFrame::Plain @@ -798,6 +668,156 @@ border-image: url(:/images/closepressed.png) + + + + + 0 + 0 + + + + Qt::NoFocus + + + Qt::CustomContextMenu + + + + :/icons/png/search.png:/icons/png/search.png + + + + 28 + 28 + + + + true + + + true + + + + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + + :/icons/png/highlight.png:/icons/png/highlight.png + + + + 28 + 28 + + + + true + + + true + + + + + + + Qt::NoFocus + + + + :/icons/png/arrow-left.png:/icons/png/arrow-left.png + + + + 28 + 28 + + + + true + + + + + + + Qt::NoFocus + + + + :/icons/png/arrow-right.png:/icons/png/arrow-right.png + + + + 28 + 28 + + + + true + + + + + + + Qt::NoFocus + + + + :/icons/png/chat-bubble-notify.png:/icons/png/chat-bubble-notify.png + + + + 28 + 28 + + + + true + + + + + + + Qt::NoFocus + + + <html><head/><body><p>Chat menu</p></body></html> + + + + :/icons/png/menu.png:/icons/png/menu.png + + + + 28 + 28 + + + + QToolButton::InstantPopup + + + true + + + @@ -983,7 +1003,7 @@ border-image: url(:/images/closepressed.png) - + :/icons/png/addstickers.png:/icons/png/addstickers.png diff --git a/retroshare-gui/src/gui/chat/CreateLobbyDialog.cpp b/retroshare-gui/src/gui/chat/CreateLobbyDialog.cpp index 1aeb96366..41cb8d3bf 100644 --- a/retroshare-gui/src/gui/chat/CreateLobbyDialog.cpp +++ b/retroshare-gui/src/gui/chat/CreateLobbyDialog.cpp @@ -33,6 +33,7 @@ #include "gui/common/PeerDefs.h" #include "ChatDialog.h" #include "gui/ChatLobbyWidget.h" +#include "gui/common/FilesDefs.h" CreateLobbyDialog::CreateLobbyDialog(const std::set& peer_list, int privacyLevel, QWidget *parent) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint) @@ -40,7 +41,7 @@ CreateLobbyDialog::CreateLobbyDialog(const std::set& peer_list, int pr ui = new Ui::CreateLobbyDialog() ; ui->setupUi(this); - ui->headerFrame->setHeaderImage(QPixmap(":/icons/png/chat-lobbies.png")); + ui->headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/chat-lobbies.png")); ui->headerFrame->setHeaderText(tr("Create Chat Room")); RsGxsId default_identity ; diff --git a/retroshare-gui/src/gui/chat/PopupChatDialog.cpp b/retroshare-gui/src/gui/chat/PopupChatDialog.cpp index 1591669f4..9cb422a8f 100644 --- a/retroshare-gui/src/gui/chat/PopupChatDialog.cpp +++ b/retroshare-gui/src/gui/chat/PopupChatDialog.cpp @@ -23,6 +23,7 @@ #include "PopupChatDialog.h" #include "PopupChatWindow.h" +#include "gui/common/FilesDefs.h" #include "gui/settings/rsharesettings.h" #include "gui/settings/RsharePeerSettings.h" #include "gui/notifyqt.h" @@ -149,10 +150,10 @@ void PopupChatDialog::showAvatarFrame(bool show) if (show) { ui.avatarFrameButton->setToolTip(tr("Hide Avatar")); - ui.avatarFrameButton->setIcon(QIcon(":images/hide_toolbox_frame.png")); + ui.avatarFrameButton->setIcon(FilesDefs::getIconFromQtResourcePath(":images/hide_toolbox_frame.png")); } else { ui.avatarFrameButton->setToolTip(tr("Show Avatar")); - ui.avatarFrameButton->setIcon(QIcon(":images/show_toolbox_frame.png")); + ui.avatarFrameButton->setIcon(FilesDefs::getIconFromQtResourcePath(":images/show_toolbox_frame.png")); } PeerSettings->setShowAvatarFrame(mChatId, show); diff --git a/retroshare-gui/src/gui/chat/PopupChatWindow.cpp b/retroshare-gui/src/gui/chat/PopupChatWindow.cpp index 17c2c38c3..6c1f9128d 100644 --- a/retroshare-gui/src/gui/chat/PopupChatWindow.cpp +++ b/retroshare-gui/src/gui/chat/PopupChatWindow.cpp @@ -24,6 +24,7 @@ #include #include +#include "gui/common/FilesDefs.h" #include "PopupChatWindow.h" #include "ChatDialog.h" #include "gui/settings/rsharesettings.h" @@ -64,15 +65,13 @@ static PopupChatWindow *instance = NULL; } /** Default constructor */ -PopupChatWindow::PopupChatWindow(bool tabbed, QWidget *parent, Qt::WindowFlags flags) : QMainWindow(parent, flags) +PopupChatWindow::PopupChatWindow(bool tabbed, QWidget *parent, Qt::WindowFlags flags) + : QMainWindow(parent, flags),tabbedWindow(tabbed),firstShow(true) + , chatDialog(nullptr),mEmptyIcon(nullptr) { /* Invoke Qt Designer generated QObject setup routine */ ui.setupUi(this); - - tabbedWindow = tabbed; - firstShow = true; - chatDialog = NULL; - mEmptyIcon = NULL; + setAttribute(Qt::WA_DeleteOnClose); ui.tabWidget->setVisible(tabbedWindow); @@ -115,14 +114,14 @@ PopupChatWindow::PopupChatWindow(bool tabbed, QWidget *parent, Qt::WindowFlags f void PopupChatWindow::showContextMenu(QPoint) { QMenu contextMnu(this); - contextMnu.addAction(QIcon(":/images/highlight.png"),tr("Choose window color..."),this,SLOT(setStyle())); + contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/highlight.png"),tr("Choose window color..."),this,SLOT(setStyle())); if (Settings->getChatFlags() & RS_CHAT_TABBED_WINDOW) { if(tabbedWindow) - contextMnu.addAction(QIcon(":/images/tab-dock.png"),tr("Dock window"),this,SLOT(docTab())); + contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/tab-dock.png"),tr("Dock window"),this,SLOT(docTab())); - contextMnu.addAction(QIcon(":/images/tab-undock.png"),tr("Dock window"),this,SLOT(undockTab())); + contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/tab-undock.png"),tr("Dock window"),this,SLOT(undockTab())); } contextMnu.exec(QCursor::pos()); } @@ -204,15 +203,16 @@ void PopupChatWindow::addDialog(ChatDialog *dialog) if (tabbedWindow) { ui.tabWidget->addDialog(dialog); } else { - ui.horizontalLayout->addWidget(dialog); + ui.chatcentralLayout->addWidget(dialog); dialog->addToParent(this); - ui.horizontalLayout->setContentsMargins(0, 0, 0, 0); - chatId = dialog->getChatId(); + ui.chatcentralLayout->setContentsMargins(0, 0, 0, 0); + chatId = dialog->getChatId(); chatDialog = dialog; calculateStyle(dialog); + calculateTitle(dialog); /* signal toggled is called */ - ui.actionSetOnTop->setChecked(PeerSettings->getPrivateChatOnTop(chatId)); + ui.actionSetOnTop->setChecked(PeerSettings->getPrivateChatOnTop(chatId)); QObject::connect(dialog, SIGNAL(dialogClose(ChatDialog*)), this, SLOT(dialogClose(ChatDialog*))); } @@ -233,14 +233,15 @@ void PopupChatWindow::removeDialog(ChatDialog *dialog) deleteLater(); } } else { - QObject::disconnect(dialog, SIGNAL(dialogClose(ChatDialog*)), this, SLOT(dialogClose(ChatDialog*))); if (chatDialog == dialog) { + QObject::disconnect(dialog, SIGNAL(dialogClose(ChatDialog*)), this, SLOT(dialogClose(ChatDialog*))); saveSettings(); dialog->removeFromParent(this); - ui.horizontalLayout->removeWidget(dialog); - chatDialog = NULL; - chatId = ChatId(); + ui.chatcentralLayout->removeWidget(dialog); + chatDialog = nullptr; + chatId = ChatId(); + close(); deleteLater(); } } @@ -295,9 +296,9 @@ void PopupChatWindow::calculateTitle(ChatDialog *dialog) QIcon icon; if (isTyping) { mBlinkIcon = QIcon(); - icon = QIcon(IMAGE_TYPING); + icon = FilesDefs::getIconFromQtResourcePath(IMAGE_TYPING); } else if (hasNewMessages) { - icon = QIcon(IMAGE_CHAT); + icon = FilesDefs::getIconFromQtResourcePath(IMAGE_CHAT); if (Settings->getChatFlags() & RS_CHAT_BLINK) { mBlinkIcon = icon; } else { diff --git a/retroshare-gui/src/gui/chat/PopupChatWindow.ui b/retroshare-gui/src/gui/chat/PopupChatWindow.ui index f85a570b6..ea6ea79af 100644 --- a/retroshare-gui/src/gui/chat/PopupChatWindow.ui +++ b/retroshare-gui/src/gui/chat/PopupChatWindow.ui @@ -17,7 +17,7 @@ MainWindow - + 5 diff --git a/retroshare-gui/src/gui/chat/PopupDistantChatDialog.cpp b/retroshare-gui/src/gui/chat/PopupDistantChatDialog.cpp index b93c2d438..f78b5cc30 100644 --- a/retroshare-gui/src/gui/chat/PopupDistantChatDialog.cpp +++ b/retroshare-gui/src/gui/chat/PopupDistantChatDialog.cpp @@ -26,11 +26,13 @@ #include +#include "gui/common/FilesDefs.h" #include #include #include -#include "RsAutoUpdatePage.h" +#include + #include "PopupDistantChatDialog.h" #define IMAGE_RED_LED ":/icons/bullet_red_128.png" @@ -112,7 +114,7 @@ void PopupDistantChatDialog::updateDisplay() { case RS_DISTANT_CHAT_STATUS_UNKNOWN: - _status_label->setIcon(QIcon(IMAGE_GRY_LED)); + _status_label->setIcon(FilesDefs::getIconFromQtResourcePath(IMAGE_GRY_LED)); msg = tr("Remote status unknown."); _status_label->setToolTip(msg); getChatWidget()->updateStatusString("%1", msg, true); @@ -123,7 +125,7 @@ void PopupDistantChatDialog::updateDisplay() break ; case RS_DISTANT_CHAT_STATUS_REMOTELY_CLOSED: std::cerr << "Chat remotely closed. " << std::endl; - _status_label->setIcon(QIcon(IMAGE_RED_LED)); + _status_label->setIcon(FilesDefs::getIconFromQtResourcePath(IMAGE_RED_LED)); _status_label->setToolTip( QObject::tr("Distant peer has closed the chat") ); getChatWidget()->updateStatusString("%1", tr( "Your partner closed the conversation." ), true ); @@ -134,7 +136,7 @@ void PopupDistantChatDialog::updateDisplay() case RS_DISTANT_CHAT_STATUS_TUNNEL_DN: - _status_label->setIcon(QIcon(IMAGE_YEL_LED)); + _status_label->setIcon(FilesDefs::getIconFromQtResourcePath(IMAGE_YEL_LED)); msg = QObject::tr( "Tunnel is pending"); if(tinfo.pending_items > 0) @@ -147,7 +149,7 @@ void PopupDistantChatDialog::updateDisplay() break; case RS_DISTANT_CHAT_STATUS_CAN_TALK: - _status_label->setIcon(QIcon(IMAGE_GRN_LED)); + _status_label->setIcon(FilesDefs::getIconFromQtResourcePath(IMAGE_GRN_LED)); msg = QObject::tr( "End-to-end encrypted conversation established"); _status_label->setToolTip(msg); getChatWidget()->unblockSending(); diff --git a/retroshare-gui/src/gui/common/AvatarDefs.cpp b/retroshare-gui/src/gui/common/AvatarDefs.cpp index 0332f50d0..ccca08707 100644 --- a/retroshare-gui/src/gui/common/AvatarDefs.cpp +++ b/retroshare-gui/src/gui/common/AvatarDefs.cpp @@ -26,6 +26,7 @@ #include #include "AvatarDefs.h" +#include "gui/common/FilesDefs.h" void AvatarDefs::getOwnAvatar(QPixmap &avatar, const QString& defaultImage) { @@ -36,7 +37,7 @@ void AvatarDefs::getOwnAvatar(QPixmap &avatar, const QString& defaultImage) rsMsgs->getOwnAvatarData(data, size); if (size == 0) { - avatar = QPixmap(defaultImage); + avatar = FilesDefs::getPixmapFromQtResourcePath(defaultImage); return; } @@ -53,7 +54,7 @@ bool AvatarDefs::getAvatarFromSslId(const RsPeerId& sslId, QPixmap &avatar, cons /* get avatar */ rsMsgs->getAvatarData(RsPeerId(sslId), data, size); if (size == 0) { - avatar = QPixmap(defaultImage); + avatar = FilesDefs::getPixmapFromQtResourcePath(defaultImage); return false; } @@ -72,7 +73,7 @@ bool AvatarDefs::getAvatarFromGxsId(const RsGxsId& gxsId, QPixmap &avatar, const if(!rsIdentity->getIdDetails(gxsId, details)) { - avatar = QPixmap(defaultImage); + avatar = FilesDefs::getPixmapFromQtResourcePath(defaultImage); return false; } @@ -107,7 +108,7 @@ bool AvatarDefs::getAvatarFromGpgId(const RsPgpId& gpgId, QPixmap &avatar, const } if (size == 0) { - avatar = QPixmap(defaultImage); + avatar = FilesDefs::getPixmapFromQtResourcePath(defaultImage); return false; } diff --git a/retroshare-gui/src/gui/common/AvatarDialog.cpp b/retroshare-gui/src/gui/common/AvatarDialog.cpp index c6a9f9c0b..2a5d92cac 100644 --- a/retroshare-gui/src/gui/common/AvatarDialog.cpp +++ b/retroshare-gui/src/gui/common/AvatarDialog.cpp @@ -24,6 +24,7 @@ #include "ui_AvatarDialog.h" #include "AvatarDefs.h" #include "util/misc.h" +#include "gui/common/FilesDefs.h" /** Constructor */ AvatarDialog::AvatarDialog(QWidget *parent) : @@ -33,7 +34,7 @@ AvatarDialog::AvatarDialog(QWidget *parent) : /* Invoke Qt Designer generated QObject setup routine */ ui->setupUi(this); - ui->headerFrame->setHeaderImage(QPixmap(":/images/no_avatar_70.png")); + ui->headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/images/no_avatar_70.png")); ui->headerFrame->setHeaderText(tr("Set your Avatar picture")); connect(ui->avatarButton, SIGNAL(clicked(bool)), this, SLOT(changeAvatar())); @@ -45,8 +46,8 @@ AvatarDialog::AvatarDialog(QWidget *parent) : updateInterface(); } -const int AvatarDialog::RS_AVATAR_DEFAULT_IMAGE_W = 64; -const int AvatarDialog::RS_AVATAR_DEFAULT_IMAGE_H = 64; +const int AvatarDialog::RS_AVATAR_DEFAULT_IMAGE_W = 96; +const int AvatarDialog::RS_AVATAR_DEFAULT_IMAGE_H = 96; AvatarDialog::~AvatarDialog() { diff --git a/retroshare-gui/src/gui/common/ElidedLabel.cpp b/retroshare-gui/src/gui/common/ElidedLabel.cpp index 02c3881ff..796852ad3 100644 --- a/retroshare-gui/src/gui/common/ElidedLabel.cpp +++ b/retroshare-gui/src/gui/common/ElidedLabel.cpp @@ -74,19 +74,9 @@ void ElidedLabel::clear() void ElidedLabel::paintEvent(QPaintEvent *event) { QLabel::paintEvent(event); - QList > lLines; - QString elidedLastLine = ""; + QPainter painter(this); - QFontMetrics fontMetrics = painter.fontMetrics(); - QRect cr = contentsRect(); - cr.adjust(margin(), margin(), -margin(), -margin()); - - bool didElide = false; - QChar ellipsisChar(0x2026);//= "…" - int lineSpacing = fontMetrics.lineSpacing(); - int y = 0; - - QString plainText = ""; + QString plainText = ""; if (mOnlyPlainText) { plainText = mContent; @@ -95,15 +85,48 @@ void ElidedLabel::paintEvent(QPaintEvent *event) td.setHtml(mContent); plainText = td.toPlainText(); } + QRect cr(contentsRect()); + cr.adjust(margin(), margin(), -margin(), -margin()); + + bool didElide = paintElidedLine(&painter,plainText,cr,font(),alignment(),wordWrap(),true,&mRectElision); + + //Send signal if changed + + if (didElide != mElided) + { + mElided = didElide; + emit elisionChanged(didElide); + } +} + +bool ElidedLabel::paintElidedLine( QPainter* painter, QString plainText + , const QRect& cr, QFont useFont + , Qt::Alignment alignment, bool wordWrap + , bool drawRoundedRect, QRect* rectElision/*=nullptr*/) +{ + if (rectElision) *rectElision = QRect(); + if (plainText.isEmpty()) + return false; + + QList > lLines; + QString elidedLastLine = ""; + QFontMetrics fontMetrics = QFontMetrics(useFont); + + bool didElide = false; + QChar ellipsisChar(0x2026);//= "…" + int lineSpacing = fontMetrics.lineSpacing(); + int y = 0; + plainText = plainText.replace("\n",QChar(QChar::LineSeparator)); plainText = plainText.replace("\r",QChar(QChar::LineSeparator)); - QTextLayout textLayout(plainText, painter.font()); + QTextLayout textLayout(plainText, useFont); QTextOption to = textLayout.textOption(); - to.setAlignment(alignment()); - to.setWrapMode(wordWrap() ? QTextOption::WrapAtWordBoundaryOrAnywhere : QTextOption::NoWrap); + to.setAlignment(alignment); + to.setWrapMode(wordWrap ? QTextOption::WrapAtWordBoundaryOrAnywhere : QTextOption::NoWrap); textLayout.setTextOption(to); + if (painter) painter->save(); textLayout.beginLayout(); forever { //Get new line for text. @@ -115,7 +138,7 @@ void ElidedLabel::paintEvent(QPaintEvent *event) line.setLineWidth(cr.width()); int nextLineY = y + lineSpacing; - if ((cr.height() >= nextLineY + lineSpacing) && wordWrap()) { + if ((cr.height() >= nextLineY + lineSpacing) && wordWrap) { //Line written normaly, next line will too lLines.append(QPair(line, QPoint(0, y))); y = nextLineY; @@ -123,8 +146,13 @@ void ElidedLabel::paintEvent(QPaintEvent *event) //The next line can't be written. QString lastLine = plainText.mid(line.textStart()).split(QChar(QChar::LineSeparator)).at(0); QTextLine lineEnd = textLayout.createLine(); - if (!lineEnd.isValid() && (wordWrap() - || (fontMetrics.width(lastLine) < cr.width()))) { + if (!lineEnd.isValid() && (wordWrap +#if QT_VERSION < QT_VERSION_CHECK(5,11,0) + || (fontMetrics.width(lastLine) < cr.width()) )) +#else + || (fontMetrics.horizontalAdvance(lastLine) < cr.width()) )) +#endif + { //No more text for next line so this one is OK lLines.append(QPair(line, QPoint(0, y))); elidedLastLine=""; @@ -150,11 +178,11 @@ void ElidedLabel::paintEvent(QPaintEvent *event) if (didElide) iHeight += lineSpacing; //Compute lines translation with alignment - if (alignment() & Qt::AlignTop) + if (alignment & Qt::AlignTop) iTransY = 0; - if (alignment() & Qt::AlignBottom) + if (alignment & Qt::AlignBottom) iTransY = cr.height() - iHeight; - if (alignment() & Qt::AlignVCenter) + if (alignment & Qt::AlignVCenter) iTransY = (cr.height() - iHeight) / 2; QPair pair; @@ -162,12 +190,17 @@ void ElidedLabel::paintEvent(QPaintEvent *event) //Now we know how many lines to redraw at good position foreach (pair, lLines){ lastPos = pair.second + QPoint(0+ cr.left(), iTransY + cr.top()); - pair.first.draw(&painter, lastPos); + if (painter) pair.first.draw(painter, lastPos); } //Print last elided line - if (didElide) { + if (didElide) + { +#if QT_VERSION < QT_VERSION_CHECK(5,11,0) int width = fontMetrics.width(elidedLastLine); +#else + int width = fontMetrics.horizontalAdvance(elidedLastLine); +#endif if (lastPos.y() == -1){ y = iTransY;// Only one line } else { @@ -175,32 +208,45 @@ void ElidedLabel::paintEvent(QPaintEvent *event) } if (width < cr.width()){ //Text don't taking all line (with line break), so align it - if (alignment() & Qt::AlignLeft) + if (alignment & Qt::AlignLeft) iTransX = 0; - if (alignment() & Qt::AlignRight) + if (alignment & Qt::AlignRight) iTransX = cr.width() - width; - if (alignment() & Qt::AlignHCenter) + if (alignment & Qt::AlignHCenter) iTransX = (cr.width() - width) / 2; - if (alignment() & Qt::AlignJustify) + if (alignment & Qt::AlignJustify) iTransX = 0; } - painter.drawText(QPoint(iTransX + cr.left(), y + fontMetrics.ascent() + cr.top()), elidedLastLine); + if(width+iTransX+cr.left() <= cr.right()) + if (painter) + { + painter->setFont(useFont); + painter->drawText(QPoint(iTransX + cr.left(), y + fontMetrics.ascent() + cr.top()), elidedLastLine); + } + //Draw button to get ToolTip - mRectElision = QRect(iTransX + width - fontMetrics.width(ellipsisChar) + cr.left() - , y + cr.top() - , fontMetrics.width(ellipsisChar) - , fontMetrics.height() - 1); - painter.drawRoundRect(mRectElision); - } else { - mRectElision = QRect(); +#if QT_VERSION < QT_VERSION_CHECK(5,11,0) + int fontWidth = fontMetrics.width(ellipsisChar); +#else + int fontWidth = fontMetrics.horizontalAdvance(ellipsisChar); +#endif + QRect mRectElision = QRect( iTransX + width - fontWidth + cr.left() + , y + cr.top() + , fontWidth + , fontMetrics.height() - 1); + if (rectElision) + *rectElision = mRectElision; + + if(drawRoundedRect) + if (painter) { + painter->setBrush(QBrush(Qt::transparent)); + painter->drawRoundedRect(mRectElision, 2 , 2); + } } - //Send signal if changed - if (didElide != mElided) { - mElided = didElide; - emit elisionChanged(didElide); - } + if (painter) painter->restore(); + return didElide; } void ElidedLabel::mousePressEvent(QMouseEvent *ev) diff --git a/retroshare-gui/src/gui/common/ElidedLabel.h b/retroshare-gui/src/gui/common/ElidedLabel.h index 3b935e444..af9ccfc40 100644 --- a/retroshare-gui/src/gui/common/ElidedLabel.h +++ b/retroshare-gui/src/gui/common/ElidedLabel.h @@ -48,6 +48,24 @@ public: QColor textColor() const { return mTextColor; } void setTextColor(const QColor &color); + /** + * @brief paintElidedLine: Draw elided(…) line to a painter. + * @param painter: wher to paint line. If null, only calculate rectElision. + * painter pen have to be setted before. This doesn't paint background. + * @param plainText: Plain text to paint. + * @param cr: Area where to paint. If too close, text is elided. + * @param font: Font to use to pain text. + * @param alignment: Which alignement to use for text. + * @param wordWrap: If it elide by char or by word. + * @param drawRoundRect: If rounded rect around … is need. To notice user he can click on it. + * @param rectElision: Where elision occurs. To manage click. Can be omitted. + * @return If text need to be elided. + */ + static bool paintElidedLine( QPainter* painter, QString plainText + , const QRect& cr, QFont font + , Qt::Alignment alignment,bool wordWrap + , bool drawRoundedRect,QRect* rectElision = nullptr); + public slots: void setText(const QString &text); void setOnlyPlainText(const bool &value); diff --git a/retroshare-gui/src/gui/common/Emoticons.cpp b/retroshare-gui/src/gui/common/Emoticons.cpp index 347d88b6e..d6bb17e4a 100644 --- a/retroshare-gui/src/gui/common/Emoticons.cpp +++ b/retroshare-gui/src/gui/common/Emoticons.cpp @@ -37,6 +37,7 @@ #include "Emoticons.h" #include "util/HandleRichText.h" #include "retroshare/rsinit.h" +#include "gui/common/FilesDefs.h" #define ICONNAME "groupicon.png" @@ -210,7 +211,7 @@ void Emoticons::showSmileyWidget(QWidget *parent, QWidget *button, const char *s smTab->setStyleSheet("QTabBar::tab { height: 44px; width: 44px; }"); if (groupName.right(4).toLower() == ".png") - smTab->addTab( tabGrpWidget, QIcon(groupName), ""); + smTab->addTab( tabGrpWidget, FilesDefs::getIconFromQtResourcePath(groupName), ""); else smTab->addTab( tabGrpWidget, groupName); } else { @@ -237,7 +238,7 @@ void Emoticons::showSmileyWidget(QWidget *parent, QWidget *button, const char *s QPushButton *button = new QPushButton("", tabGrpWidget); button->setIconSize(QSize(buttonWidth, buttonHeight)); button->setFixedSize(QSize(buttonWidth, buttonHeight)); - button->setIcon(QPixmap(group.value(key))); + button->setIcon(FilesDefs::getIconFromQtResourcePath(group.value(key))); button->setToolTip(key); button->setStyleSheet("QPushButton:hover {border: 3px solid #0099cc; border-radius: 3px;}"); button->setFlat(true); @@ -371,9 +372,9 @@ void Emoticons::showStickerWidget(QWidget *parent, QWidget *button, const char * int index; if (groupDir.exists(ICONNAME)) //use groupicon.png if exists, else the first png as a group icon - index = smTab->addTab( tabGrpWidget, QIcon(groupDir.absoluteFilePath(ICONNAME)), ""); + index = smTab->addTab( tabGrpWidget, FilesDefs::getIconFromQtResourcePath(groupDir.absoluteFilePath(ICONNAME)), ""); else - index = smTab->addTab( tabGrpWidget, QIcon(groupDir.entryInfoList(QDir::Files)[0].canonicalFilePath()), ""); + index = smTab->addTab( tabGrpWidget, FilesDefs::getIconFromQtResourcePath(groupDir.entryInfoList(QDir::Files)[0].canonicalFilePath()), ""); smTab->setTabToolTip(index, groupName); } else { tabGrpWidget = smWidget; @@ -401,7 +402,7 @@ void Emoticons::showStickerWidget(QWidget *parent, QWidget *button, const char * button->setFixedSize(QSize(buttonWidth, buttonHeight)); if(!iconcache.contains(fi.absoluteFilePath())) { - iconcache.insert(fi.absoluteFilePath(), QPixmap(fi.absoluteFilePath()).scaled(buttonWidth, buttonHeight, Qt::KeepAspectRatio)); + iconcache.insert(fi.absoluteFilePath(), FilesDefs::getPixmapFromQtResourcePath(fi.absoluteFilePath()).scaled(buttonWidth, buttonHeight, Qt::KeepAspectRatio)); } button->setIcon(iconcache[fi.absoluteFilePath()]); button->setToolTip(fi.fileName()); diff --git a/retroshare-gui/src/gui/common/FilesDefs.cpp b/retroshare-gui/src/gui/common/FilesDefs.cpp index 83474a752..b7f0192a4 100644 --- a/retroshare-gui/src/gui/common/FilesDefs.cpp +++ b/retroshare-gui/src/gui/common/FilesDefs.cpp @@ -27,6 +27,8 @@ #include +//#define DEBUG_FILESDEFS 1 + static QString getInfoFromFilename(const QString& filename, bool anyForUnknown, bool image) { QString ext = QFileInfo(filename).suffix().toLower(); @@ -85,23 +87,111 @@ QString FilesDefs::getImageFromFilename(const QString& filename, bool anyForUnkn return getInfoFromFilename(filename, anyForUnknown, true); } -QIcon FilesDefs::getIconFromFilename(const QString& filename) +QPixmap FilesDefs::getPixmapFromQtResourcePath(const QString& resource_path) { - QString sImage = getInfoFromFilename(filename, true, true); - static std::map mIconCache; - QIcon icon; - auto item = mIconCache.find(sImage); - if (item == mIconCache.end()) + static std::map mPixmapCache; + QPixmap pixmap; +#ifdef DEBUG_FILESDEFS + std::cerr << "Creating Pixmap from resource path " << resource_path.toStdString() ; +#endif + + auto item = mPixmapCache.find(resource_path); + + if (item == mPixmapCache.end()) { - icon = QIcon(sImage); - mIconCache[sImage] = icon; +#ifdef DEBUG_FILESDEFS + std::cerr << " Not in cache. Creating new one." << std::endl; +#endif + pixmap = QPixmap(resource_path); + mPixmapCache[resource_path] = pixmap; } else + { +#ifdef DEBUG_FILESDEFS + std::cerr << " In cache. " << std::endl; +#endif + pixmap = item->second; + } + + return pixmap; +} + +QIcon FilesDefs::getIconFromQtResourcePath(const QString& resource_path) +{ + static std::map mIconCache; + QIcon icon; +#ifdef DEBUG_FILESDEFS + std::cerr << "Creating Icon from resource path " << resource_path.toStdString() ; +#endif + + auto item = mIconCache.find(resource_path); + + if (item == mIconCache.end()) + { +#ifdef DEBUG_FILESDEFS + std::cerr << " Not in cache. Creating new one." << std::endl; +#endif + icon = QIcon(resource_path); + mIconCache[resource_path] = icon; + } + else + { +#ifdef DEBUG_FILESDEFS + std::cerr << " In cache. " << std::endl; +#endif icon = item->second; + } return icon; } +QIcon FilesDefs::getIconFromGxsIdCache(const RsGxsId& id,const QIcon& setIcon, bool& exist) +{ + static std::map mIconCache; + exist = false; + QIcon icon; +#ifdef DEBUG_FILESDEFS + std::cerr << "Creating Icon from id " << id.toStdString() ; +#endif + if (setIcon.isNull()) + { + if (id.isNull()) + return getIconFromQtResourcePath(":/icons/notification.png"); + + auto item = mIconCache.find(id); + + if (item == mIconCache.end()) + { +#ifdef DEBUG_FILESDEFS + std::cerr << " Not in cache. And not setted." << std::endl; +#endif + icon = getIconFromQtResourcePath(":/icons/png/anonymous.png"); + } + else + { +#ifdef DEBUG_FILESDEFS + std::cerr << " In cache. " << std::endl; +#endif + icon = item->second; + exist = true; + } + } + else + { +#ifdef DEBUG_FILESDEFS + std::cerr << " Have to set it." << std::endl; +#endif + icon = setIcon; + mIconCache[id] = icon; + } + return icon; +} + +QIcon FilesDefs::getIconFromFileType(const QString& filename) +{ + return getIconFromQtResourcePath(getInfoFromFilename(filename,true,true)); +} + QString FilesDefs::getNameFromFilename(const QString &filename) { return getInfoFromFilename(filename, false, false); diff --git a/retroshare-gui/src/gui/common/FilesDefs.h b/retroshare-gui/src/gui/common/FilesDefs.h index 50a9e19ab..1163fb004 100644 --- a/retroshare-gui/src/gui/common/FilesDefs.h +++ b/retroshare-gui/src/gui/common/FilesDefs.h @@ -24,11 +24,25 @@ #include #include +#include "retroshare/rsids.h" + class FilesDefs { public: static QString getImageFromFilename(const QString& filename, bool anyForUnknown); - static QIcon getIconFromFilename(const QString& filename); + + // Theses methods is here to fix a Qt design flow that makes QIcon loaded from filename (e.g. :/images/icon.png) to not use the cache. + // As a result, icons created by Qt in this way (mostly from GUI) do not use data sharing. + // The method below has its own cache. + + static QIcon getIconFromQtResourcePath(const QString& resource_path); + static QPixmap getPixmapFromQtResourcePath(const QString& resource_path); + static QIcon getIconFromGxsIdCache(const RsGxsId& id, const QIcon& setIcon, bool& exist); + + // This method returns a QIcon that is suitable to represent a file of a particular type (image, movie, etc.) + + static QIcon getIconFromFileType(const QString& filename); + static QString getNameFromFilename(const QString& filename); }; diff --git a/retroshare-gui/src/gui/common/FriendList.cpp b/retroshare-gui/src/gui/common/FriendList.cpp index 49bd7ab56..531a87f63 100644 --- a/retroshare-gui/src/gui/common/FriendList.cpp +++ b/retroshare-gui/src/gui/common/FriendList.cpp @@ -65,12 +65,12 @@ #define IMAGE_EXPORTFRIEND ":/images/user/friend_suggestion16.png" #define IMAGE_ADDFRIEND ":/images/user/add_user16.png" #define IMAGE_FRIENDINFO ":/images/info16.png" -#define IMAGE_CHAT ":/images/chat_24.png" -#define IMAGE_MSG ":/images/mail_new.png" +#define IMAGE_CHAT ":/icons/png/chats.png" +#define IMAGE_MSG ":/icons/mail/write-mail.png" #define IMAGE_CONNECT ":/images/connect_friend.png" #define IMAGE_COPYLINK ":/images/copyrslink.png" #define IMAGE_GROUP16 ":/images/user/group16.png" -#define IMAGE_EDIT ":/images/edit_16.png" +#define IMAGE_EDIT ":/icons/png/pencil-edit-button.png" #define IMAGE_REMOVE ":/images/delete.png" #define IMAGE_EXPAND ":/images/edit_add24.png" #define IMAGE_COLLAPSE ":/images/edit_remove24.png" @@ -306,7 +306,7 @@ void FriendList::peerTreeWidgetCustomPopupMenu() hbox->setSpacing(6); QLabel *iconLabel = new QLabel(widget); - QPixmap pix = QPixmap(":/images/user/friends24.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5); + QPixmap pix = FilesDefs::getPixmapFromQtResourcePath(":/images/user/friends24.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5); iconLabel->setPixmap(pix); iconLabel->setMaximumSize(iconLabel->frameSize().height() + pix.height(), pix.width()); hbox->addWidget(iconLabel); @@ -352,25 +352,25 @@ void FriendList::peerTreeWidgetCustomPopupMenu() { bool standard = c->data(COLUMN_DATA, ROLE_STANDARD).toBool(); #ifdef RS_DIRECT_CHAT - contextMenu->addAction(QIcon(IMAGE_MSG), tr("Send message to whole group"), this, SLOT(msgfriend())); + contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MSG), tr("Send message to whole group"), this, SLOT(msgfriend())); contextMenu->addSeparator(); #endif // RS_DIRECT_CHAT - contextMenu->addAction(QIcon(IMAGE_EDIT), tr("Edit Group"), this, SLOT(editGroup())); + contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_EDIT), tr("Edit Group"), this, SLOT(editGroup())); - QAction *action = contextMenu->addAction(QIcon(IMAGE_REMOVE), tr("Remove Group"), this, SLOT(removeGroup())); + QAction *action = contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_REMOVE), tr("Remove Group"), this, SLOT(removeGroup())); action->setDisabled(standard); } break; case TYPE_GPG: { #ifdef RS_DIRECT_CHAT - contextMenu->addAction(QIcon(IMAGE_CHAT), tr("Chat"), this, SLOT(chatfriendproxy())); - contextMenu->addAction(QIcon(IMAGE_MSG), tr("Send message"), this, SLOT(msgfriend())); + contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_CHAT), tr("Chat"), this, SLOT(chatfriendproxy())); + contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MSG), tr("Send message"), this, SLOT(msgfriend())); contextMenu->addSeparator(); #endif // RS_DIRECT_CHAT - contextMenu->addAction(QIcon(IMAGE_FRIENDINFO), tr("Profile details"), this, SLOT(configurefriend())); - contextMenu->addAction(QIcon(IMAGE_DENYFRIEND), tr("Deny connections"), this, SLOT(removefriend())); + contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_FRIENDINFO), tr("Profile details"), this, SLOT(configurefriend())); + contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_DENYFRIEND), tr("Deny connections"), this, SLOT(removefriend())); if(mShowGroups) { @@ -412,8 +412,8 @@ void FriendList::peerTreeWidgetCustomPopupMenu() } } - QMenu *groupsMenu = contextMenu->addMenu(QIcon(IMAGE_GROUP16), tr("Groups")); - groupsMenu->addAction(QIcon(IMAGE_EXPAND), tr("Create new group"), this, SLOT(createNewGroup())); + QMenu *groupsMenu = contextMenu->addMenu(FilesDefs::getIconFromQtResourcePath(IMAGE_GROUP16), tr("Groups")); + groupsMenu->addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_EXPAND), tr("Create new group"), this, SLOT(createNewGroup())); if (addToGroupMenu || moveToGroupMenu || foundGroup) { if (addToGroupMenu) { @@ -445,24 +445,24 @@ void FriendList::peerTreeWidgetCustomPopupMenu() case TYPE_SSL: { #ifdef RS_DIRECT_CHAT - contextMenu->addAction(QIcon(IMAGE_CHAT), tr("Chat"), this, SLOT(chatfriendproxy())); - contextMenu->addAction(QIcon(IMAGE_MSG), tr("Send message to this node"), this, SLOT(msgfriend())); + contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_CHAT), tr("Chat"), this, SLOT(chatfriendproxy())); + contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MSG), tr("Send message to this node"), this, SLOT(msgfriend())); contextMenu->addSeparator(); #endif // RS_DIRECT_CHAT - contextMenu->addAction(QIcon(IMAGE_FRIENDINFO), tr("Node details"), this, SLOT(configurefriend())); + contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_FRIENDINFO), tr("Node details"), this, SLOT(configurefriend())); if (type == TYPE_GPG || type == TYPE_SSL) { - contextMenu->addAction(QIcon(IMAGE_EXPORTFRIEND), tr("Recommend this node to..."), this, SLOT(recommendfriend())); + contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_EXPORTFRIEND), tr("Recommend this node to..."), this, SLOT(recommendfriend())); } if(!rsPeers->isHiddenNode(rsPeers->getOwnId()) || rsPeers->isHiddenNode( RsPeerId(getRsId(c)) )) - contextMenu->addAction(QIcon(IMAGE_CONNECT), tr("Attempt to connect"), this, SLOT(connectfriend())); + contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_CONNECT), tr("Attempt to connect"), this, SLOT(connectfriend())); - contextMenu->addAction(QIcon(IMAGE_COPYLINK), tr("Copy certificate link"), this, SLOT(copyFullCertificate())); + contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COPYLINK), tr("Copy certificate link"), this, SLOT(copyFullCertificate())); //this is a SSL key - contextMenu->addAction(QIcon(IMAGE_REMOVEFRIEND), tr("Remove Friend Node"), this, SLOT(removefriend())); + contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_REMOVEFRIEND), tr("Remove Friend Node"), this, SLOT(removefriend())); } } @@ -471,12 +471,12 @@ void FriendList::peerTreeWidgetCustomPopupMenu() contextMenu->addSeparator(); - QAction *action = contextMenu->addAction(QIcon(IMAGE_PASTELINK), tr("Paste certificate link"), this, SLOT(pastePerson())); + QAction *action = contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_PASTELINK), tr("Paste certificate link"), this, SLOT(pastePerson())); if (RSLinkClipboard::empty(RetroShareLink::TYPE_CERTIFICATE)) action->setDisabled(true); - contextMenu->addAction(QIcon(IMAGE_EXPAND), tr("Expand all"), ui->peerTreeWidget, SLOT(expandAll())); - contextMenu->addAction(QIcon(IMAGE_COLLAPSE), tr("Collapse all"), ui->peerTreeWidget, SLOT(collapseAll())); + contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_EXPAND), tr("Expand all"), ui->peerTreeWidget, SLOT(expandAll())); + contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COLLAPSE), tr("Collapse all"), ui->peerTreeWidget, SLOT(collapseAll())); contextMenu = ui->peerTreeWidget->createStandardContextMenu(contextMenu); @@ -716,8 +716,8 @@ void FriendList::insertPeers() groupItem->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless); groupItem->setTextAlignment(COLUMN_NAME, Qt::AlignLeft | Qt::AlignVCenter); - groupItem->setIcon(COLUMN_NAME, QIcon(IMAGE_GROUP24)); - groupItem->setForeground(COLUMN_NAME, QBrush(textColorGroup())); + groupItem->setIcon(COLUMN_NAME, FilesDefs::getIconFromQtResourcePath(IMAGE_GROUP24)); + groupItem->setData(COLUMN_NAME, Qt::ForegroundRole, textColorGroup()); /* used to find back the item */ QString strID = QString::fromStdString(groupInfo->id.toStdString()); @@ -1006,7 +1006,7 @@ void FriendList::insertPeers() sslItem->setHidden(false); gpg_connected = true; - sslOverlayIcon = QPixmap(StatusDefs::imageStatus(bestRSState)); + sslOverlayIcon = FilesDefs::getPixmapFromQtResourcePath(StatusDefs::imageStatus(bestRSState)); connectStateString = StatusDefs::name(rsState); @@ -1024,9 +1024,9 @@ void FriendList::insertPeers() peerState = PEER_STATE_AVAILABLE; if (sslDetail.connectState) { - sslOverlayIcon = QPixmap(":/images/connect_creating.png"); + sslOverlayIcon = FilesDefs::getPixmapFromQtResourcePath(":/images/connect_creating.png"); } else { - sslOverlayIcon = QPixmap(StatusDefs::imageStatus(RS_STATUS_ONLINE)); + sslOverlayIcon = FilesDefs::getPixmapFromQtResourcePath(StatusDefs::imageStatus(RS_STATUS_ONLINE)); } connectStateString = StatusDefs::name(RS_STATUS_ONLINE); @@ -1037,9 +1037,9 @@ void FriendList::insertPeers() peerState = PEER_STATE_OFFLINE; sslItem->setHidden(mHideUnconnected); if (sslDetail.connectState) { - sslOverlayIcon = QPixmap(":/images/connect_creating.png"); + sslOverlayIcon = FilesDefs::getPixmapFromQtResourcePath(":/images/connect_creating.png"); } else { - sslOverlayIcon = QPixmap(StatusDefs::imageStatus(RS_STATUS_OFFLINE)); + sslOverlayIcon = FilesDefs::getPixmapFromQtResourcePath(StatusDefs::imageStatus(RS_STATUS_OFFLINE)); } connectStateString = StatusDefs::connectStateWithoutTransportTypeString(sslDetail); @@ -1099,7 +1099,7 @@ void FriendList::insertPeers() if (std::find(privateChatIds.begin(), privateChatIds.end(), sslDetail.id) != privateChatIds.end()) { // private chat is available - sslOverlayIcon = QPixmap(":/images/chat.png"); + sslOverlayIcon = FilesDefs::getPixmapFromQtResourcePath(":/images/chat.png"); gpg_hasPrivateChat = true; } sslItem->setIcon(COLUMN_NAME, createAvatar(sslAvatar, sslOverlayIcon)); @@ -1110,8 +1110,8 @@ void FriendList::insertPeers() for (int i = 0; i < columnCount; ++i) { sslItem->setData(i, ROLE_SORT_STATE, peerState); - sslItem->setTextColor(i, sslColor); - sslItem->setFont(i, sslFont); + sslItem->setData(i, Qt::ForegroundRole, sslColor); + sslItem->setData(i, Qt::FontRole, sslFont); } } @@ -1138,7 +1138,7 @@ void FriendList::insertPeers() gpgFont = StatusDefs::font(bestRSState); if (showInfoAtGpgItem) { - gpgOverlayIcon = QPixmap(StatusDefs::imageStatus(bestRSState)); + gpgOverlayIcon = FilesDefs::getPixmapFromQtResourcePath(StatusDefs::imageStatus(bestRSState)); if (mShowState) { gpgText = StatusDefs::name(bestRSState); @@ -1164,7 +1164,7 @@ void FriendList::insertPeers() gpgText += tr("Available"); } - gpgOverlayIcon = QPixmap(IMAGE_AVAILABLE); + gpgOverlayIcon = FilesDefs::getPixmapFromQtResourcePath(IMAGE_AVAILABLE); } } else { bestPeerState = PEER_STATE_OFFLINE; @@ -1178,15 +1178,15 @@ void FriendList::insertPeers() gpgText += StatusDefs::name(RS_STATUS_OFFLINE); } - gpgOverlayIcon = QPixmap(StatusDefs::imageStatus(RS_STATUS_OFFLINE)); + gpgOverlayIcon = FilesDefs::getPixmapFromQtResourcePath(StatusDefs::imageStatus(RS_STATUS_OFFLINE)); } } if (gpg_hasPrivateChat) { - gpgOverlayIcon = QPixmap(":/images/chat.png"); + gpgOverlayIcon = FilesDefs::getPixmapFromQtResourcePath(":/images/chat.png"); } - gpgItem->setIcon(COLUMN_NAME, createAvatar(bestAvatar.isNull() ? QPixmap(AVATAR_DEFAULT_IMAGE) : bestAvatar, gpgOverlayIcon)); + gpgItem->setIcon(COLUMN_NAME, createAvatar(bestAvatar.isNull() ? FilesDefs::getPixmapFromQtResourcePath(AVATAR_DEFAULT_IMAGE) : bestAvatar, gpgOverlayIcon)); /* Create or get gpg label */ ElidedLabel *gpgNameLabel = NULL; @@ -1222,8 +1222,8 @@ void FriendList::insertPeers() for (int i = 0; i < columnCount; ++i) { gpgItem->setData(i, ROLE_SORT_STATE, bestPeerState); - gpgItem->setTextColor(i, gpgColor); - gpgItem->setFont(i, gpgFont); + gpgItem->setData(i, Qt::ForegroundRole, gpgColor); + gpgItem->setData(i, Qt::FontRole, gpgFont); } if (openPeers.find(gpgId) != openPeers.end()) { diff --git a/retroshare-gui/src/gui/common/FriendListModel.cpp b/retroshare-gui/src/gui/common/FriendListModel.cpp index 3da609aa4..39d4bbded 100644 --- a/retroshare-gui/src/gui/common/FriendListModel.cpp +++ b/retroshare-gui/src/gui/common/FriendListModel.cpp @@ -27,6 +27,7 @@ #include #include "gui/common/StatusDefs.h" +#include "gui/common/FilesDefs.h" #include "gui/common/AvatarDefs.h" #include "util/HandleRichText.h" #include "util/DateTime.h" @@ -151,15 +152,13 @@ void RsFriendListModel::setDisplayGroups(bool b) } void RsFriendListModel::preMods() { - emit layoutAboutToBeChanged(); - beginResetModel(); } void RsFriendListModel::postMods() { endResetModel(); - emit layoutChanged(); - emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mTopLevel.size()-1,COLUMN_THREAD_NB_COLUMNS-1,(void*)NULL)); + + emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(rowCount()-1,columnCount()-1,(void*)NULL)); } int RsFriendListModel::rowCount(const QModelIndex& parent) const @@ -616,100 +615,104 @@ public: QVariant RsFriendListModel::displayRole(const EntryIndex& e, int col) const { #ifdef DEBUG_MODEL_INDEX - std::cerr << " Display role " << e.type << ", (" << (int)e.group_index << ","<< (int)e.profile_index << ","<< (int)e.node_index << ") col="<< col<<": "; - AutoEndel x; + std::cerr << " Display role " << e.type << ", (" << (int)e.group_index << ","<< (int)e.profile_index << ","<< (int)e.node_index << ") col="<< col<<": "; + AutoEndel x; #endif - switch(e.type) + switch(e.type) { - case ENTRY_TYPE_GROUP: - { - const HierarchicalGroupInformation *group = getGroupInfo(e); + case ENTRY_TYPE_GROUP: + { + const HierarchicalGroupInformation *group = getGroupInfo(e); - if(!group) - return QVariant(); + if(!group) + return QVariant(); - uint32_t nb_online = 0; + uint32_t nb_online = 0; - for(uint32_t i=0;ichild_profile_indices.size();++i) - for(uint32_t j=0;jchild_profile_indices[i]].child_node_indices.size();++j) - if(mLocations[mProfiles[group->child_profile_indices[i]].child_node_indices[j]].node_info.state & RS_PEER_STATE_CONNECTED) - { - nb_online++; - break;// only breaks the inner loop, on purpose. - } + for(uint32_t i=0;ichild_profile_indices.size();++i) + for(uint32_t j=0;jchild_profile_indices[i]].child_node_indices.size();++j) + if(mLocations[mProfiles[group->child_profile_indices[i]].child_node_indices[j]].node_info.state & RS_PEER_STATE_CONNECTED) + { + nb_online++; + break;// only breaks the inner loop, on purpose. + } switch(col) { - case COLUMN_THREAD_NAME: + case COLUMN_THREAD_NAME: #ifdef DEBUG_MODEL_INDEX - std::cerr << group->group_info.name.c_str() ; + std::cerr << group->group_info.name.c_str() ; #endif - if(!group->child_profile_indices.empty()) - return QVariant(QString::fromUtf8(group->group_info.name.c_str())+" (" + QString::number(nb_online) + "/" + QString::number(group->child_profile_indices.size()) + ")"); - else - return QVariant(QString::fromUtf8(group->group_info.name.c_str())); + if(!group->child_profile_indices.empty()) + return QVariant(QString::fromUtf8(group->group_info.name.c_str())+" (" + QString::number(nb_online) + "/" + QString::number(group->child_profile_indices.size()) + ")"); + else + return QVariant(QString::fromUtf8(group->group_info.name.c_str())); - case COLUMN_THREAD_ID: return QVariant(QString::fromStdString(group->group_info.id.toStdString())); - default: + case COLUMN_THREAD_ID: return QVariant(QString::fromStdString(group->group_info.id.toStdString())); + default: return QVariant(); } - } - break; + } + break; - case ENTRY_TYPE_PROFILE: + case ENTRY_TYPE_PROFILE: { - const HierarchicalProfileInformation *profile = getProfileInfo(e); + const HierarchicalProfileInformation *profile = getProfileInfo(e); - if(!profile) - return QVariant(); + if(!profile) + return QVariant(); #ifdef DEBUG_MODEL_INDEX - std::cerr << profile->profile_info.name.c_str() ; + std::cerr << profile->profile_info.name.c_str() ; #endif switch(col) { - case COLUMN_THREAD_NAME: return QVariant(QString::fromUtf8(profile->profile_info.name.c_str())); - case COLUMN_THREAD_ID: return QVariant(QString::fromStdString(profile->profile_info.gpg_id.toStdString()) ); + case COLUMN_THREAD_NAME: return QVariant(QString::fromUtf8(profile->profile_info.name.c_str())); + case COLUMN_THREAD_ID: return QVariant(QString::fromStdString(profile->profile_info.gpg_id.toStdString()) ); - default: + default: return QVariant(); } - } - break; + } + break; - case ENTRY_TYPE_NODE: - const HierarchicalNodeInformation *node = getNodeInfo(e); + case ENTRY_TYPE_NODE: + { + const HierarchicalNodeInformation *node = getNodeInfo(e); - if(!node) - return QVariant(); + if(!node) + return QVariant(); #ifdef DEBUG_MODEL_INDEX - std::cerr << node->node_info.location.c_str() ; + std::cerr << node->node_info.location.c_str() ; #endif - switch(col) - { - case COLUMN_THREAD_NAME: if(node->node_info.location.empty()) - return QVariant(QString::fromStdString(node->node_info.id.toStdString())); + switch(col) + { + case COLUMN_THREAD_NAME: if(node->node_info.location.empty()) + return QVariant(QString::fromStdString(node->node_info.id.toStdString())); - { - std::string css = rsMsgs->getCustomStateString(node->node_info.id); + { + std::string css = rsMsgs->getCustomStateString(node->node_info.id); - if(!css.empty() && mDisplayStatusString) - return QVariant(QString::fromUtf8(node->node_info.location.c_str())+"\n"+QString::fromUtf8(css.c_str())); - else - return QVariant(QString::fromUtf8(node->node_info.location.c_str())); - } + if(!css.empty() && mDisplayStatusString) + return QVariant(QString::fromUtf8(node->node_info.location.c_str())+"\n"+QString::fromUtf8(css.c_str())); + else + return QVariant(QString::fromUtf8(node->node_info.location.c_str())); + } - case COLUMN_THREAD_LAST_CONTACT: return QVariant(QDateTime::fromTime_t(node->node_info.lastConnect).toString()); - case COLUMN_THREAD_IP: return QVariant( (node->node_info.state & RS_PEER_STATE_CONNECTED) ? StatusDefs::connectStateIpString(node->node_info) : QString("---")); - case COLUMN_THREAD_ID: return QVariant( QString::fromStdString(node->node_info.id.toStdString()) ); + case COLUMN_THREAD_LAST_CONTACT: return QVariant(QDateTime::fromTime_t(node->node_info.lastConnect).toString()); + case COLUMN_THREAD_IP: return QVariant( (node->node_info.state & RS_PEER_STATE_CONNECTED) ? StatusDefs::connectStateIpString(node->node_info) : QString("---")); + case COLUMN_THREAD_ID: return QVariant( QString::fromStdString(node->node_info.id.toStdString()) ); - default: - return QVariant(); - } break; + default: + return QVariant(); + } break; + } + break; + default: //ENTRY_TYPE return QVariant(); } } @@ -815,13 +818,13 @@ QVariant RsFriendListModel::decorationRole(const EntryIndex& entry,int col) cons switch(entry.type) { - case ENTRY_TYPE_GROUP: return QVariant(QIcon(IMAGE_GROUP24)); + case ENTRY_TYPE_GROUP: return QVariant(FilesDefs::getIconFromQtResourcePath(IMAGE_GROUP24)); case ENTRY_TYPE_PROFILE: { if(!isProfileExpanded(entry)) { - QPixmap sslAvatar(AVATAR_DEFAULT_IMAGE); + QPixmap sslAvatar = FilesDefs::getPixmapFromQtResourcePath(AVATAR_DEFAULT_IMAGE); const HierarchicalProfileInformation *hn = getProfileInfo(entry); @@ -1055,14 +1058,16 @@ void RsFriendListModel::updateInternalData() preMods(); beginRemoveRows(QModelIndex(),0,mTopLevel.size()-1); - endRemoveRows(); mGroups.clear(); mProfiles.clear(); mLocations.clear(); - mTopLevel.clear(); + endRemoveRows(); + + auto TL = mTopLevel ; // This allows to fill TL without touching mTopLevel outside of [begin/end]InsertRows(). + // create a map of profiles and groups std::map pgp_indices; @@ -1150,7 +1155,6 @@ void RsFriendListModel::updateInternalData() RsDbg() << "Creating top level list" << std::endl; #endif - mTopLevel.clear(); std::set already_in_a_group; if(mDisplayGroups) // in this case, we list all groups at the top level followed by the profiles without parent group @@ -1165,7 +1169,7 @@ void RsFriendListModel::updateInternalData() e.type = ENTRY_TYPE_GROUP; e.group_index = i; - mTopLevel.push_back(e); + TL.push_back(e); for(uint32_t j=0;jprofile_info.gpg_id.toStdString(); if(!s.empty()) - mExpandedProfiles.insert(s); + mExpandedProfiles.insert(s); // apparently we cannot be subtle here. - emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mTopLevel.size()-1,COLUMN_THREAD_NB_COLUMNS-1,(void*)NULL)); + emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mTopLevel.size()-1,columnCount()-1,(void*)NULL)); } bool RsFriendListModel::isProfileExpanded(const EntryIndex& e) const diff --git a/retroshare-gui/src/gui/common/FriendListModel.h b/retroshare-gui/src/gui/common/FriendListModel.h index 5e8d84930..871dc14b9 100644 --- a/retroshare-gui/src/gui/common/FriendListModel.h +++ b/retroshare-gui/src/gui/common/FriendListModel.h @@ -39,8 +39,16 @@ public: explicit RsFriendListModel(QObject *parent = NULL); ~RsFriendListModel(){} - class RsNodeDetails: public RsPeerDetails {};// in the near future, there will be a specific class for Profile/Node details in replacement of RsPeerDetails - class RsProfileDetails: public RsPeerDetails {}; + class RsNodeDetails: public RsPeerDetails + { + public: + virtual ~RsNodeDetails() {} + };// in the near future, there will be a specific class for Profile/Node details in replacement of RsPeerDetails + class RsProfileDetails: public RsPeerDetails + { + public: + virtual ~RsProfileDetails() {} + }; struct HierarchicalGroupInformation { @@ -55,6 +63,7 @@ public: struct HierarchicalNodeInformation { HierarchicalNodeInformation() : last_update_ts(0) {} + virtual ~HierarchicalNodeInformation() {} rstime_t last_update_ts; RsNodeDetails node_info; diff --git a/retroshare-gui/src/gui/common/FriendSelectionDialog.cpp b/retroshare-gui/src/gui/common/FriendSelectionDialog.cpp index 9201a0458..9631054a0 100644 --- a/retroshare-gui/src/gui/common/FriendSelectionDialog.cpp +++ b/retroshare-gui/src/gui/common/FriendSelectionDialog.cpp @@ -100,7 +100,7 @@ FriendSelectionDialog::FriendSelectionDialog(QWidget *parent,const QString& head friends_widget->setModus(modus) ; friends_widget->setShowType(show_type) ; friends_widget->start() ; - friends_widget->setSelectedIds(pre_selected_id_type, pre_selected_ids, false); + friends_widget->setSelectedIdsFromString(pre_selected_id_type, pre_selected_ids, false); QLayout *l = new QVBoxLayout ; setLayout(l) ; diff --git a/retroshare-gui/src/gui/common/FriendSelectionWidget.cpp b/retroshare-gui/src/gui/common/FriendSelectionWidget.cpp index 2770622fe..d253691bf 100644 --- a/retroshare-gui/src/gui/common/FriendSelectionWidget.cpp +++ b/retroshare-gui/src/gui/common/FriendSelectionWidget.cpp @@ -18,6 +18,7 @@ * * *******************************************************************************/ +#include "gui/common/FilesDefs.h" #include #include #include "FriendSelectionWidget.h" @@ -27,6 +28,7 @@ #include "gui/notifyqt.h" #include "gui/common/RSTreeWidgetItem.h" #include "gui/common/StatusDefs.h" +#include "util/qtthreadsutils.h" #include "gui/common/PeerDefs.h" #include "gui/common/GroupDefs.h" #include "rshare.h" @@ -35,6 +37,7 @@ #include #include +#include #define COLUMN_NAME 0 #define COLUMN_CHECK 0 @@ -80,7 +83,7 @@ static void setSelected(FriendSelectionWidget::Modus modus, QTreeWidgetItem *ite } FriendSelectionWidget::FriendSelectionWidget(QWidget *parent) - : RsGxsUpdateBroadcastPage(rsIdentity,parent), ui(new Ui::FriendSelectionWidget) + : QWidget(parent), ui(new Ui::FriendSelectionWidget) { ui->setupUi(this); @@ -92,8 +95,6 @@ FriendSelectionWidget::FriendSelectionWidget(QWidget *parent) mInSslItemChanged = false; mInFillList = false; - mIdQueue = new TokenQueue(rsIdentity->getTokenService(), this); - connect(ui->friendList, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequested(QPoint))); connect(ui->friendList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(itemDoubleClicked(QTreeWidgetItem*,int))); connect(ui->friendList, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(itemChanged(QTreeWidgetItem*,int))); @@ -131,7 +132,6 @@ FriendSelectionWidget::FriendSelectionWidget(QWidget *parent) FriendSelectionWidget::~FriendSelectionWidget() { - delete(mIdQueue); delete ui; } @@ -185,6 +185,11 @@ int FriendSelectionWidget::addColumn(const QString &title) return column; } +void FriendSelectionWidget::showEvent(QShowEvent *e) +{ + if(gxsIds.empty()) + loadIdentities(); +} void FriendSelectionWidget::start() { mStarted = true; @@ -212,10 +217,10 @@ static void initSslItem(QTreeWidgetItem *item, const RsPeerDetails &detail, cons } if (state != (int) RS_STATUS_OFFLINE) { - item->setTextColor(COLUMN_NAME, textColorOnline); + item->setData(COLUMN_NAME, Qt::ForegroundRole, textColorOnline); } - item->setIcon(COLUMN_NAME, QIcon(StatusDefs::imageUser(state))); + item->setIcon(COLUMN_NAME, FilesDefs::getIconFromQtResourcePath(StatusDefs::imageUser(state))); item->setData(COLUMN_DATA, ROLE_ID, QString::fromStdString(detail.id.toStdString())); item->setData(COLUMN_NAME, ROLE_SORT_GROUP, 1); @@ -237,32 +242,33 @@ void FriendSelectionWidget::fillList() secured_fillList() ; } -void FriendSelectionWidget::loadRequest(const TokenQueue */*queue*/, const TokenRequest &req) +void FriendSelectionWidget::loadIdentities() { // store all IDs locally, and call fillList() ; - uint32_t token = req.mToken ; - - RsGxsIdGroup data; - std::vector datavector; - std::vector::iterator vit; - - if (!rsIdentity->getGroupData(token, datavector)) + RsThread::async([this]() { - std::cerr << "FriendSelectionWidget::loadRequest() ERROR. Cannot load data from rsIdentity." << std::endl; - return ; - } + std::list ids_meta; - gxsIds.clear() ; + if(!rsIdentity->getIdentitiesSummaries(ids_meta)) + { + RS_ERR("failed to retrieve identities group info for all identities"); + return; + } - for(uint32_t i=0;i sslIdsSelected; - if (mShowTypes & SHOW_SSL) { + if (mShowTypes & SHOW_SSL) + { + if(!ui->friendList->topLevelItemCount()) // if not loaded yet, use the existing list. + for(auto& s:mPreSelectedIds) + sslIdsSelected.insert(RsPeerId(s)); + selectedIds(sslIdsSelected,true); } std::set groupIdsSelected; - if (mShowTypes & SHOW_GROUP) { + + if (mShowTypes & SHOW_GROUP) + { selectedIds(groupIdsSelected,true); + + if(!ui->friendList->topLevelItemCount()) // if not loaded yet, use the existing list. + for(auto& s:mPreSelectedIds) + groupIdsSelected.insert(RsNodeGroupId(s)); } std::set gpgIdsSelected; - if (mShowTypes & (SHOW_GPG | SHOW_NON_FRIEND_GPG)) { + + if (mShowTypes & (SHOW_GPG | SHOW_NON_FRIEND_GPG)) + { selectedIds(gpgIdsSelected,true); + + if(!ui->friendList->topLevelItemCount()) // if not loaded yet, use the existing list. + for(auto& s:mPreSelectedIds) + gpgIdsSelected.insert(RsPgpId(s)); } std::set gxsIdsSelected; @@ -292,7 +315,8 @@ void FriendSelectionWidget::secured_fillList() selectedIds(gxsIdsSelected,true); if(!ui->friendList->topLevelItemCount()) // if not loaded yet, use the existing list. - gxsIdsSelected = mPreSelectedGxsIds; + for(auto& s:mPreSelectedIds) + gxsIdsSelected.insert(RsGxsId(s)); } std::set gxsIdsSelected2; @@ -358,7 +382,7 @@ void FriendSelectionWidget::secured_fillList() groupItem->setFlags(Qt::ItemIsUserCheckable | groupItem->flags()); groupItem->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless); groupItem->setTextAlignment(COLUMN_NAME, Qt::AlignLeft | Qt::AlignVCenter); - groupItem->setIcon(COLUMN_NAME, QIcon(IMAGE_GROUP16)); + groupItem->setIcon(COLUMN_NAME, FilesDefs::getIconFromQtResourcePath(IMAGE_GROUP16)); groupItem->setData(COLUMN_DATA, ROLE_ID, QString::fromStdString(groupInfo->id.toStdString())); @@ -427,11 +451,11 @@ void FriendSelectionWidget::secured_fillList() } if (state != (int) RS_STATUS_OFFLINE) { - gpgItem->setTextColor(COLUMN_NAME, textColorOnline()); + gpgItem->setData(COLUMN_NAME, Qt::ForegroundRole, textColorOnline()); } gpgItem->setFlags(Qt::ItemIsUserCheckable | gpgItem->flags()); - gpgItem->setIcon(COLUMN_NAME, QIcon(StatusDefs::imageUser(state))); + gpgItem->setIcon(COLUMN_NAME, FilesDefs::getIconFromQtResourcePath(StatusDefs::imageUser(state))); gpgItem->setData(COLUMN_DATA, ROLE_ID, QString::fromStdString(detail.gpg_id.toStdString())); gpgItem->setData(COLUMN_NAME, ROLE_SORT_GROUP, 1); @@ -564,7 +588,7 @@ void FriendSelectionWidget::secured_fillList() QString name = QString::fromUtf8(detail.mNickname.c_str()); gxsItem->setText(COLUMN_NAME, name + " ("+QString::fromStdString( (*gxsIt).toStdString() )+")"); - //gxsItem->setTextColor(COLUMN_NAME, textColorOnline()); + //gxsItem->setData(COLUMN_NAME, Qt::ForegroundRole, textColorOnline()); gxsItem->setFlags(Qt::ItemIsUserCheckable | gxsItem->flags()); gxsItem->setIcon(COLUMN_NAME, identicon); gxsItem->setData(COLUMN_DATA, ROLE_ID, QString::fromStdString(detail.mId.toStdString())); @@ -618,7 +642,7 @@ void FriendSelectionWidget::secured_fillList() QString name = QString::fromUtf8(detail.mNickname.c_str()); gxsItem->setText(COLUMN_NAME, name + " ("+QString::fromStdString( (*gxsIt).toStdString() )+")"); - //gxsItem->setTextColor(COLUMN_NAME, textColorOnline()); + //gxsItem->setData(COLUMN_NAME, Qt::ForegroundRole, textColorOnline()); gxsItem->setFlags(Qt::ItemIsUserCheckable | gxsItem->flags()); gxsItem->setIcon(COLUMN_NAME, identicon); gxsItem->setData(COLUMN_DATA, ROLE_ID, QString::fromStdString(detail.mId.toStdString())); @@ -666,32 +690,18 @@ void FriendSelectionWidget::secured_fillList() } void FriendSelectionWidget::updateDisplay(bool) { - requestGXSIdList() ; + loadIdentities() ; } -void FriendSelectionWidget::requestGXSIdList() -{ - if (!mIdQueue) - return; - - //mStateHelper->setLoading(IDDIALOG_IDLIST, true); - //mStateHelper->setLoading(IDDIALOG_IDDETAILS, true); - //mStateHelper->setLoading(IDDIALOG_REPLIST, true); - - mIdQueue->cancelActiveRequestTokens(IDDIALOG_IDLIST); - - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; - - uint32_t token; - - mIdQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, IDDIALOG_IDLIST); -} - // This call is inlined so that there's no linking conflict with MinGW on Windows template<> inline void FriendSelectionWidget::setSelectedIds(const std::set& ids, bool add) { - mPreSelectedGxsIds = ids ; - requestGXSIdList(); + if(!add) + mPreSelectedIds.clear(); + + for(auto& gxsId:ids) + mPreSelectedIds.insert(gxsId.toStdString()); + + loadIdentities(); } void FriendSelectionWidget::groupsChanged(int /*type*/) @@ -758,15 +768,13 @@ void FriendSelectionWidget::peerStatusChanged(const QString& peerId, int status) case IDTYPE_GPG: { if (item->data(COLUMN_DATA, ROLE_ID).toString() == gpgId) { - QColor color; if (status != (int) RS_STATUS_OFFLINE) { - color = textColorOnline(); + item->setData(COLUMN_NAME, Qt::ForegroundRole, textColorOnline()); } else { - color = ui->friendList->palette().color(QPalette::Text); + item->setData(COLUMN_NAME, Qt::ForegroundRole, QVariant()); } - item->setTextColor(COLUMN_NAME, color); - item->setIcon(COLUMN_NAME, QIcon(StatusDefs::imageUser(gpgStatus))); + item->setIcon(COLUMN_NAME, FilesDefs::getIconFromQtResourcePath(StatusDefs::imageUser(gpgStatus))); item->setData(COLUMN_NAME, ROLE_SORT_STATE, gpgStatus); @@ -777,15 +785,13 @@ void FriendSelectionWidget::peerStatusChanged(const QString& peerId, int status) case IDTYPE_SSL: { if (item->data(COLUMN_DATA, ROLE_ID).toString() == peerId) { - QColor color; if (status != (int) RS_STATUS_OFFLINE) { - color = textColorOnline(); + item->setData(COLUMN_NAME, Qt::ForegroundRole, textColorOnline()); } else { - color = ui->friendList->palette().color(QPalette::Text); + item->setData(COLUMN_NAME, Qt::ForegroundRole, QVariant()); } - item->setTextColor(COLUMN_NAME, color); - item->setIcon(COLUMN_NAME, QIcon(StatusDefs::imageUser(status))); + item->setIcon(COLUMN_NAME, FilesDefs::getIconFromQtResourcePath(StatusDefs::imageUser(status))); item->setData(COLUMN_NAME, ROLE_SORT_STATE, status); @@ -993,7 +999,7 @@ std::string FriendSelectionWidget::selectedId(IdType &idType) return idFromItem(item); } -void FriendSelectionWidget::selectedIds(IdType idType, std::set &ids, bool onlyDirectSelected) +void FriendSelectionWidget::selectedIds_internal(IdType idType, std::set &ids, bool onlyDirectSelected) { QTreeWidgetItemIterator itemIterator(ui->friendList); QTreeWidgetItem *item; @@ -1067,11 +1073,21 @@ void FriendSelectionWidget::selectAll() setSelected(mListModus, *itemIterator, true); } -void FriendSelectionWidget::setSelectedIds(IdType idType, const std::set &ids, bool add) +void FriendSelectionWidget::setSelectedIdsFromString(IdType type, const std::set& ids, bool add) { + setSelectedIds_internal(type,ids,add); +} + +void FriendSelectionWidget::setSelectedIds_internal(IdType idType, const std::set &ids, bool add) +{ + mPreSelectedIds = ids; + + // if items are already loaded, check them + QTreeWidgetItemIterator itemIterator(ui->friendList); QTreeWidgetItem *item; - while ((item = *itemIterator) != NULL) { + while ((item = *itemIterator) != NULL) + { ++itemIterator; std::string id = idFromItem(item); diff --git a/retroshare-gui/src/gui/common/FriendSelectionWidget.h b/retroshare-gui/src/gui/common/FriendSelectionWidget.h index 3b7a3db6c..7a2fb718e 100644 --- a/retroshare-gui/src/gui/common/FriendSelectionWidget.h +++ b/retroshare-gui/src/gui/common/FriendSelectionWidget.h @@ -25,7 +25,6 @@ #include #include -#include "util/TokenQueue.h" namespace Ui { class FriendSelectionWidget; @@ -34,7 +33,7 @@ class FriendSelectionWidget; class QTreeWidgetItem; class RSTreeWidgetItemCompareRole; -class FriendSelectionWidget : public RsGxsUpdateBroadcastPage, public TokenResponse +class FriendSelectionWidget : public QWidget { Q_OBJECT @@ -82,13 +81,16 @@ public: bool isSortByState(); bool isFilterConnected(); + void loadIdentities(); int selectedItemCount(); std::string selectedId(IdType &idType); + void setSelectedIdsFromString(IdType type,const std::set& ids,bool add); + template void selectedIds(std::set& ids, bool onlyDirectSelected) { std::set tmpids ; - selectedIds(TYPE, tmpids, onlyDirectSelected); + selectedIds_internal(TYPE, tmpids, onlyDirectSelected); ids.clear() ; for(std::set::const_iterator it(tmpids.begin());it!=tmpids.end();++it) ids.insert(ID_CLASS(*it)) ; @@ -98,7 +100,7 @@ public: std::set tmpids ; for(typename std::set::const_iterator it(ids.begin());it!=ids.end();++it) tmpids.insert((*it).toStdString()) ; - setSelectedIds(TYPE, tmpids, add); + setSelectedIds_internal(TYPE, tmpids, add); } void itemsFromId(IdType idType, const std::string &id, QList &items); @@ -115,9 +117,9 @@ public: void addContextMenuAction(QAction *action); protected: + void showEvent(QShowEvent *e) override; void changeEvent(QEvent *e); - virtual void loadRequest(const TokenQueue *queue,const TokenRequest& req); virtual void updateDisplay(bool complete); signals: @@ -145,10 +147,8 @@ private: void fillList(); void secured_fillList(); - void selectedIds(IdType idType, std::set &ids, bool onlyDirectSelected); - void setSelectedIds(IdType idType, const std::set &ids, bool add); - - void requestGXSIdList() ; + void selectedIds_internal(IdType idType, std::set &ids, bool onlyDirectSelected); + void setSelectedIds_internal(IdType idType, const std::set &ids, bool add); private: bool mStarted; @@ -170,10 +170,9 @@ private: friend class FriendSelectionDialog ; std::vector gxsIds ; - TokenQueue *mIdQueue ; QList mContextMenuActions; - std::set mPreSelectedGxsIds; // because loading of GxsIds is asynchroneous we keep selected Ids from the client in a list here and use it to initialize after loading them. + std::set mPreSelectedIds; // because loading of GxsIds is asynchroneous we keep selected Ids from the client in a list here and use it to initialize after loading them. }; Q_DECLARE_OPERATORS_FOR_FLAGS(FriendSelectionWidget::ShowTypes) diff --git a/retroshare-gui/src/gui/common/GroupFlagsWidget.cpp b/retroshare-gui/src/gui/common/GroupFlagsWidget.cpp index d4142f886..9b40df3d0 100644 --- a/retroshare-gui/src/gui/common/GroupFlagsWidget.cpp +++ b/retroshare-gui/src/gui/common/GroupFlagsWidget.cpp @@ -18,6 +18,7 @@ * * *******************************************************************************/ +#include "gui/common/FilesDefs.h" #include #include #include "GroupFlagsWidget.h" @@ -54,12 +55,12 @@ GroupFlagsWidget::GroupFlagsWidget(QWidget *parent,FileStorageFlags flags) setMaximumSize(128 * QFontMetricsF(font()).height()/14.0,32 * QFontMetricsF(font()).height()/14.0) ; setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed); - _icons[2*INDEX_BROWSABLE+0] = new QIcon(FLAGS_BROWSABLE_OFF) ; - _icons[2*INDEX_BROWSABLE+1] = new QIcon(FLAGS_BROWSABLE_ON) ; - _icons[2*INDEX_ANON_SEARCH+0] = new QIcon(FLAGS_ANONYMOUS_SEARCH_OFF) ; - _icons[2*INDEX_ANON_SEARCH+1] = new QIcon(FLAGS_ANONYMOUS_SEARCH_ON) ; - _icons[2*INDEX_ANON_DL+0] = new QIcon(FLAGS_ANONYMOUS_DL_OFF) ; - _icons[2*INDEX_ANON_DL+1] = new QIcon(FLAGS_ANONYMOUS_DL_ON) ; + _icons[2*INDEX_BROWSABLE+0] = FilesDefs::getIconFromQtResourcePath(FLAGS_BROWSABLE_OFF) ; + _icons[2*INDEX_BROWSABLE+1] = FilesDefs::getIconFromQtResourcePath(FLAGS_BROWSABLE_ON) ; + _icons[2*INDEX_ANON_SEARCH+0] = FilesDefs::getIconFromQtResourcePath(FLAGS_ANONYMOUS_SEARCH_OFF) ; + _icons[2*INDEX_ANON_SEARCH+1] = FilesDefs::getIconFromQtResourcePath(FLAGS_ANONYMOUS_SEARCH_ON) ; + _icons[2*INDEX_ANON_DL+0] = FilesDefs::getIconFromQtResourcePath(FLAGS_ANONYMOUS_DL_OFF) ; + _icons[2*INDEX_ANON_DL+1] = FilesDefs::getIconFromQtResourcePath(FLAGS_ANONYMOUS_DL_ON) ; setLayout(_layout) ; @@ -136,7 +137,7 @@ void GroupFlagsWidget::update_button_state(bool b,int button_id) tip_on = ""; tip_off = ""; } - _buttons[button_id]->setIcon(*_icons[2*button_id+(int)b]) ; + _buttons[button_id]->setIcon(_icons[2*button_id+(int)b]) ; _buttons[button_id]->setToolTip(b?tip_on:tip_off) ; } @@ -183,10 +184,6 @@ void GroupFlagsWidget::update_BR_button(bool b) { update_button_state(b,INDEX_BR GroupFlagsWidget::~GroupFlagsWidget() { for(int i=0;i<3;++i) - { delete _buttons[i] ; - delete _icons[2*i+0] ; - delete _icons[2*i+1] ; - } } diff --git a/retroshare-gui/src/gui/common/GroupFlagsWidget.h b/retroshare-gui/src/gui/common/GroupFlagsWidget.h index 4703d124d..d4ef95295 100644 --- a/retroshare-gui/src/gui/common/GroupFlagsWidget.h +++ b/retroshare-gui/src/gui/common/GroupFlagsWidget.h @@ -55,7 +55,7 @@ class GroupFlagsWidget: public QWidget QPushButton *_buttons[4] ; QLayout *_layout ; - QIcon *_icons[6] ; + QIcon _icons[6] ; FileStorageFlags _flags[4] ; static QString _tooltips_on[4] ; diff --git a/retroshare-gui/src/gui/common/GroupTreeWidget.cpp b/retroshare-gui/src/gui/common/GroupTreeWidget.cpp index 8a5516f6f..543fa9759 100644 --- a/retroshare-gui/src/gui/common/GroupTreeWidget.cpp +++ b/retroshare-gui/src/gui/common/GroupTreeWidget.cpp @@ -18,28 +18,21 @@ * * *******************************************************************************/ -#include - #include "GroupTreeWidget.h" #include "ui_GroupTreeWidget.h" -#include -#include -#include -#include -#include -#include - +#include "gui/common/FilesDefs.h" #include "retroshare/rsgxsflags.h" #include "PopularityDefs.h" #include "RSElidedItemDelegate.h" #include "RSTreeWidgetItem.h" -#include "gui/common/ElidedLabel.h" #include "gui/settings/rsharesettings.h" #include "util/QtVersion.h" #include "util/DateTime.h" +#include + #include #define COLUMN_NAME 0 @@ -66,9 +59,6 @@ #define FILTER_NAME_INDEX 0 #define FILTER_DESC_INDEX 1 -Q_DECLARE_METATYPE(ElidedLabel*) -Q_DECLARE_METATYPE(QLabel*) - GroupTreeWidget::GroupTreeWidget(QWidget *parent) : QWidget(parent), ui(new Ui::GroupTreeWidget) { @@ -143,7 +133,7 @@ GroupTreeWidget::GroupTreeWidget(QWidget *parent) : /* Initialize display button */ initDisplayMenu(ui->displayButton); - ui->treeWidget->setIconSize(QSize(S*1.8,S*1.8)); + ui->treeWidget->setIconSize(QSize(S*1.8,S*1.8)); } GroupTreeWidget::~GroupTreeWidget() @@ -151,38 +141,6 @@ GroupTreeWidget::~GroupTreeWidget() delete ui; } -static void getNameWidget(QTreeWidget *treeWidget, QTreeWidgetItem *item, ElidedLabel *&nameLabel, QLabel *&waitLabel) -{ - QWidget *widget = treeWidget->itemWidget(item, COLUMN_NAME); - - if (!widget) { - widget = new QWidget; - widget->setAttribute(Qt::WA_TranslucentBackground); - nameLabel = new ElidedLabel(widget); - waitLabel = new QLabel(widget); - QMovie *movie = new QMovie(":/images/loader/circleball-16.gif"); - waitLabel->setMovie(movie); - waitLabel->setHidden(true); - - widget->setProperty("nameLabel", qVariantFromValue(nameLabel)); - widget->setProperty("waitLabel", qVariantFromValue(waitLabel)); - - QHBoxLayout *layout = new QHBoxLayout; - layout->setSpacing(0); - layout->setContentsMargins(0, 0, 0, 0); - - layout->addWidget(nameLabel); - layout->addWidget(waitLabel); - - widget->setLayout(layout); - - treeWidget->setItemWidget(item, COLUMN_NAME, widget); - } else { - nameLabel = widget->property("nameLabel").value(); - waitLabel = widget->property("waitLabel").value(); - } -} - void GroupTreeWidget::changeEvent(QEvent *e) { QWidget::changeEvent(e); @@ -293,11 +251,11 @@ void GroupTreeWidget::initDisplayMenu(QToolButton *toolButton) displayMenu = new QMenu(); QActionGroup *actionGroupAsc = new QActionGroup(displayMenu); - actionSortDescending = displayMenu->addAction(QIcon(":/images/sort_decrease.png"), tr("Sort Descending Order"), this, SLOT(sort())); + actionSortDescending = displayMenu->addAction(FilesDefs::getIconFromQtResourcePath(":/images/sort_decrease.png"), tr("Sort Descending Order"), this, SLOT(sort())); actionSortDescending->setCheckable(true); actionSortDescending->setActionGroup(actionGroupAsc); - actionSortAscending = displayMenu->addAction(QIcon(":/images/sort_incr.png"), tr("Sort Ascending Order"), this, SLOT(sort())); + actionSortAscending = displayMenu->addAction(FilesDefs::getIconFromQtResourcePath(":/images/sort_incr.png"), tr("Sort Ascending Order"), this, SLOT(sort())); actionSortAscending->setCheckable(true); actionSortAscending->setActionGroup(actionGroupAsc); @@ -330,9 +288,6 @@ void GroupTreeWidget::initDisplayMenu(QToolButton *toolButton) void GroupTreeWidget::updateColors() { - QBrush brush; - QBrush standardBrush = ui->treeWidget->palette().color(QPalette::Text); - QTreeWidgetItemIterator itemIterator(ui->treeWidget); QTreeWidgetItem *item; while ((item = *itemIterator) != NULL) { @@ -340,17 +295,11 @@ void GroupTreeWidget::updateColors() int color = item->data(COLUMN_DATA, ROLE_COLOR).toInt(); if (color >= 0) { - brush = QBrush(mTextColor[color]); + item->setData(COLUMN_NAME, Qt::TextColorRole, mTextColor[color]); } else { - brush = standardBrush; + item->setData(COLUMN_NAME, Qt::TextColorRole, QVariant()); } - item->setForeground(COLUMN_NAME, brush); - - ElidedLabel *nameLabel = NULL; - QLabel *waitLabel = NULL; - getNameWidget(ui->treeWidget, item, nameLabel, waitLabel); - nameLabel->setTextColor(brush.color()); } } @@ -390,24 +339,21 @@ QTreeWidgetItem *GroupTreeWidget::addCategoryItem(const QString &name, const QIc QFont font; QTreeWidgetItem *item = new QTreeWidgetItem(); ui->treeWidget->addTopLevelItem(item); + // To get StyleSheet for Items + ui->treeWidget->style()->unpolish(ui->treeWidget); + ui->treeWidget->style()->polish(ui->treeWidget); - ElidedLabel *nameLabel = NULL; - QLabel *waitLabel = NULL; - getNameWidget(ui->treeWidget, item, nameLabel, waitLabel); - - nameLabel->setText(name); + item->setText(COLUMN_NAME, name); item->setData(COLUMN_DATA, ROLE_NAME, name); font = item->font(COLUMN_NAME); font.setBold(true); item->setFont(COLUMN_NAME, font); - nameLabel->setFont(font); item->setIcon(COLUMN_NAME, icon); int S = QFontMetricsF(font).height(); item->setSizeHint(COLUMN_NAME, QSize(S*1.9, S*1.9)); - item->setForeground(COLUMN_NAME, QBrush(textColorCategory())); - nameLabel->setTextColor(textColorCategory()); + item->setData(COLUMN_NAME, Qt::TextColorRole, textColorCategory()); item->setData(COLUMN_DATA, ROLE_COLOR, GROUPTREEWIDGET_COLOR_CATEGORY); item->setExpanded(expand); @@ -437,19 +383,32 @@ void GroupTreeWidget::setDistSearchVisible(bool visible) bool GroupTreeWidget::isSearchRequestResult(QPoint &point,QString& group_id,uint32_t& search_req_id) { - QTreeWidgetItem *item = ui->treeWidget->itemAt(point); + QTreeWidgetItem *item = ui->treeWidget->itemAt(point); if (item == NULL) return false; - QTreeWidgetItem *parent = item->parent(); + QTreeWidgetItem *parent = item->parent(); - if(parent == NULL) - return false ; + if(parent == NULL) + return false ; search_req_id = parent->data(COLUMN_DATA, ROLE_REQUEST_ID).toUInt(); - group_id = itemId(item) ; + group_id = itemId(item) ; - return search_req_id > 0; + return search_req_id > 0; +} + +bool GroupTreeWidget::isSearchRequestResultItem(QTreeWidgetItem *item,QString& group_id,uint32_t& search_req_id) +{ + QTreeWidgetItem *parent = item->parent(); + + if(parent == NULL) + return false ; + + search_req_id = parent->data(COLUMN_DATA, ROLE_REQUEST_ID).toUInt(); + group_id = itemId(item) ; + + return search_req_id > 0; } bool GroupTreeWidget::isSearchRequestItem(QPoint &point,uint32_t& search_req_id) @@ -514,14 +473,21 @@ void GroupTreeWidget::fillGroupItems(QTreeWidgetItem *categoryItem, const QList< categoryItem->addChild(item); } - ElidedLabel *nameLabel = NULL; - QLabel *waitLabel = NULL; - getNameWidget(ui->treeWidget, item, nameLabel, waitLabel); - - nameLabel->setText(itemInfo.name); + item->setText(COLUMN_NAME, itemInfo.name); item->setData(COLUMN_DATA, ROLE_NAME, itemInfo.name); item->setData(COLUMN_DATA, ROLE_DESCRIPTION, itemInfo.description); + // Add children for context strings. This happens in the search. + while(nullptr != item->takeChild(0)); + + for(auto str:itemInfo.context_strings) + if(!str.empty()) + { + QTreeWidgetItem *it = new QTreeWidgetItem(QStringList(QString::fromUtf8(str.c_str()))); + it->setData(COLUMN_DATA,ROLE_ID,itemInfo.id); + item->addChild(it); + } + /* Set last post */ qlonglong lastPost = itemInfo.lastpost.toTime_t(); item->setData(COLUMN_DATA, ROLE_LASTPOST, -lastPost); // negative for correct sorting @@ -535,12 +501,7 @@ void GroupTreeWidget::fillGroupItems(QTreeWidgetItem *categoryItem, const QList< item->setData(COLUMN_DATA, ROLE_POSTS, -itemInfo.max_visible_posts);// negative for correct sorting /* Set icon */ - if (waitLabel->isVisible()) { - /* Item is waiting, save icon in role */ - item->setData(COLUMN_DATA, ROLE_SAVED_ICON, itemInfo.icon); - } else { - item->setIcon(COLUMN_NAME, itemInfo.icon); - } + item->setIcon(COLUMN_NAME, itemInfo.icon); /* Set popularity */ QString tooltip = PopularityDefs::tooltip(itemInfo.popularity); @@ -566,6 +527,8 @@ void GroupTreeWidget::fillGroupItems(QTreeWidgetItem *categoryItem, const QList< tooltip += "\n" + tr("Description") + ": " + itemInfo.description; + tooltip += "\n" + tr("Id") + ": " + itemInfo.id; + item->setToolTip(COLUMN_NAME, tooltip); item->setToolTip(COLUMN_UNREAD, tooltip); item->setToolTip(COLUMN_POPULARITY, tooltip); @@ -573,16 +536,14 @@ void GroupTreeWidget::fillGroupItems(QTreeWidgetItem *categoryItem, const QList< item->setData(COLUMN_DATA, ROLE_SUBSCRIBE_FLAGS, itemInfo.subscribeFlags); /* Set color */ - QBrush brush; if (itemInfo.publishKey) { - brush = QBrush(textColorPrivateKey()); item->setData(COLUMN_DATA, ROLE_COLOR, GROUPTREEWIDGET_COLOR_PRIVATEKEY); + item->setData(COLUMN_NAME, Qt::ForegroundRole, QBrush(textColorPrivateKey())); } else { - brush = ui->treeWidget->palette().color(QPalette::Text); + // Let StyleSheet color item->setData(COLUMN_DATA, ROLE_COLOR, GROUPTREEWIDGET_COLOR_STANDARD); + item->setData(COLUMN_NAME, Qt::BackgroundRole, QVariant()); } - item->setForeground(COLUMN_NAME, brush); - nameLabel->setTextColor(brush.color()); /* Calculate score */ calculateScore(item, filterText); @@ -616,11 +577,8 @@ void GroupTreeWidget::setUnreadCount(QTreeWidgetItem *item, int unreadCount) if (item == NULL) { return; } - ElidedLabel *nameLabel = NULL; - QLabel *waitLabel = NULL; - getNameWidget(ui->treeWidget, item, nameLabel, waitLabel); - QFont font = nameLabel->font(); + QFont font = item->font(COLUMN_NAME); if (unreadCount) { item->setData(COLUMN_DATA, ROLE_UNREAD, unreadCount); @@ -631,7 +589,7 @@ void GroupTreeWidget::setUnreadCount(QTreeWidgetItem *item, int unreadCount) font.setBold(false); } - nameLabel->setFont(font); + item->setFont(COLUMN_NAME, font); } QTreeWidgetItem *GroupTreeWidget::getItemFromId(const QString &id) @@ -677,40 +635,7 @@ bool GroupTreeWidget::setWaiting(const QString &id, bool wait) return false; } - ElidedLabel *nameLabel = NULL; - QLabel *waitLabel = NULL; - getNameWidget(ui->treeWidget, item, nameLabel, waitLabel); - if (wait) { - if (waitLabel->isVisible()) { - /* Allready waiting */ - } else { - /* Save icon in role */ - QIcon icon = item->icon(COLUMN_NAME); - item->setData(COLUMN_DATA, ROLE_SAVED_ICON, icon); - - /* Create empty icon of the same size */ - QPixmap pixmap(ui->treeWidget->iconSize()); - pixmap.fill(Qt::transparent); - item->setIcon(COLUMN_NAME, QIcon(pixmap)); - - /* Show waitLabel and hide nameLabel */ - nameLabel->setHidden(true); - waitLabel->setVisible(true); - waitLabel->movie()->start(); - } - } else { - if (waitLabel->isVisible()) { - /* Show nameLabel and hide waitLabel */ - waitLabel->movie()->stop(); - waitLabel->setHidden(true); - nameLabel->setVisible(true); - - /* Set icon saved in role */ - item->setIcon(COLUMN_NAME, item->data(COLUMN_DATA, ROLE_SAVED_ICON).value()); - item->setData(COLUMN_DATA, ROLE_SAVED_ICON, QIcon()); - } - } - + item->setData(COLUMN_NAME, Qt::StatusTipRole, wait ? "waiting" : ""); return true; } diff --git a/retroshare-gui/src/gui/common/GroupTreeWidget.h b/retroshare-gui/src/gui/common/GroupTreeWidget.h index cc414c6db..5b40a9487 100644 --- a/retroshare-gui/src/gui/common/GroupTreeWidget.h +++ b/retroshare-gui/src/gui/common/GroupTreeWidget.h @@ -21,8 +21,8 @@ #ifndef GROUPTREEWIDGET_H #define GROUPTREEWIDGET_H -#include -#include +#include + #include #include @@ -49,16 +49,17 @@ public: {} public: - QString id; - QString name; - QString description; - int popularity; - QDateTime lastpost; - QIcon icon; - bool publishKey; - bool adminKey; - quint32 subscribeFlags; - quint32 max_visible_posts ; + QString id; + QString name; + QString description; + int popularity; + QDateTime lastpost; + QIcon icon; + bool publishKey; + bool adminKey; + quint32 subscribeFlags; + quint32 max_visible_posts ; + std::set context_strings; }; //cppcheck-suppress noConstructor @@ -96,6 +97,7 @@ public: bool isSearchRequestItem(QPoint &point,uint32_t& search_req_id); bool isSearchRequestResult(QPoint &point, QString &group_id, uint32_t& search_req_id); + bool isSearchRequestResultItem(QTreeWidgetItem *item,QString& group_id,uint32_t& search_req_id); QTreeWidgetItem *getItemFromId(const QString &id); QTreeWidgetItem *activateId(const QString &id, bool focus); @@ -109,7 +111,8 @@ public: void setTextColorCategory(QColor color) { mTextColor[GROUPTREEWIDGET_COLOR_CATEGORY] = color; } void setTextColorPrivateKey(QColor color) { mTextColor[GROUPTREEWIDGET_COLOR_PRIVATEKEY] = color; } - bool getGroupName(QString id, QString& name); + + bool getGroupName(QString id, QString& name); int subscribeFlags(const QString &id); diff --git a/retroshare-gui/src/gui/common/GroupTreeWidget.ui b/retroshare-gui/src/gui/common/GroupTreeWidget.ui index 4f0eb977d..1f0a09426 100644 --- a/retroshare-gui/src/gui/common/GroupTreeWidget.ui +++ b/retroshare-gui/src/gui/common/GroupTreeWidget.ui @@ -65,8 +65,8 @@ Display - - :/images/looknfeel.png:/images/looknfeel.png + + :/icons/svg/design.svg:/icons/svg/design.svg @@ -147,6 +147,7 @@ + diff --git a/retroshare-gui/src/gui/common/LineEditClear.cpp b/retroshare-gui/src/gui/common/LineEditClear.cpp index 05bdca90b..b439aaa07 100644 --- a/retroshare-gui/src/gui/common/LineEditClear.cpp +++ b/retroshare-gui/src/gui/common/LineEditClear.cpp @@ -18,6 +18,7 @@ * * *******************************************************************************/ +#include "gui/common/FilesDefs.h" #include "LineEditClear.h" #include @@ -124,9 +125,8 @@ void LineEditClear::showFilterIcon() mFilterButton = new QToolButton(this); mFilterButton->setFixedSize(16, 16); - QPixmap filterPixmap(IMAGE_FILTER); - mFilterButton->setIcon(QIcon(filterPixmap)); - mFilterButton->setIconSize(filterPixmap.size()); + mFilterButton->setIcon(FilesDefs::getIconFromQtResourcePath(IMAGE_FILTER)); + //mFilterButton->setIconSize(filterPixmap.size()); mFilterButton->setCursor(Qt::ArrowCursor); mFilterButton->setStyleSheet("QToolButton { border: none; padding: 0px; }" "QToolButton[popupMode=\"2\"] { padding-right: 10px; }" @@ -224,7 +224,7 @@ void LineEditClear::activateAction(QAction *action) QIcon icon = action->icon(); if (icon.isNull()) { - icon = QIcon(IMAGE_FILTER); + icon = FilesDefs::getIconFromQtResourcePath(IMAGE_FILTER); } mFilterButton->setIcon(icon); diff --git a/retroshare-gui/src/gui/common/MimeTextEdit.cpp b/retroshare-gui/src/gui/common/MimeTextEdit.cpp index cac02dc3e..b24a65879 100644 --- a/retroshare-gui/src/gui/common/MimeTextEdit.cpp +++ b/retroshare-gui/src/gui/common/MimeTextEdit.cpp @@ -18,6 +18,7 @@ * * *******************************************************************************/ +#include "gui/common/FilesDefs.h" #include #include #include @@ -250,8 +251,8 @@ void MimeTextEdit::contextMenuEvent(QContextMenuEvent *e) QAction *spoilerAction = contextMenu->addAction(tr("Spoiler"), this, SLOT(spoiler())); spoilerAction->setToolTip(tr("Select text to hide, then push this button")); contextMenu->addSeparator(); - QAction *pasteLinkAction = contextMenu->addAction(QIcon(":/images/pasterslink.png"), tr("Paste RetroShare Link"), this, SLOT(pasteLink())); - contextMenu->addAction(QIcon(":/images/pasterslink.png"), tr("Paste my certificate link"), this, SLOT(pasteOwnCertificateLink())); + QAction *pasteLinkAction = contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(":/images/pasterslink.png"), tr("Paste RetroShare Link"), this, SLOT(pasteLink())); + contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(":/images/pasterslink.png"), tr("Paste my certificate link"), this, SLOT(pasteOwnCertificateLink())); if (RSLinkClipboard::empty()) { pasteLinkAction->setDisabled(true); diff --git a/retroshare-gui/src/gui/common/MimeTextEdit.h b/retroshare-gui/src/gui/common/MimeTextEdit.h index fc449fa64..9c47cac23 100644 --- a/retroshare-gui/src/gui/common/MimeTextEdit.h +++ b/retroshare-gui/src/gui/common/MimeTextEdit.h @@ -31,6 +31,7 @@ class MimeTextEdit : public RSTextEdit Q_OBJECT Q_PROPERTY(QColor textColorQuote READ textColorQuote WRITE setTextColorQuote) + Q_PROPERTY(QVariant textColorQuotes READ textColorQuotes WRITE setTextColorQuotes) public: MimeTextEdit(QWidget *parent = 0); @@ -48,12 +49,14 @@ public: void addContextMenuAction(QAction *action); QColor textColorQuote() const { return highliter->textColorQuote();} + QVariant textColorQuotes() const { return highliter->textColorQuotes();} bool onlyPlainText() const {return mOnlyPlainText;} void setMaxBytes(int limit) {mMaxBytes = limit;} public slots: void setTextColorQuote(QColor textColorQuote) { highliter->setTextColorQuote(textColorQuote);} + void setTextColorQuotes(QVariant textColorQuotes) { highliter->setTextColorQuotes(textColorQuotes);} void setOnlyPlainText(bool bOnlyPlainText) {mOnlyPlainText = bOnlyPlainText;} signals: diff --git a/retroshare-gui/src/gui/common/NewFriendList.cpp b/retroshare-gui/src/gui/common/NewFriendList.cpp index 302ce9310..897b10d20 100644 --- a/retroshare-gui/src/gui/common/NewFriendList.cpp +++ b/retroshare-gui/src/gui/common/NewFriendList.cpp @@ -32,6 +32,7 @@ #include "GroupDefs.h" #include "gui/chat/ChatDialog.h" #include "gui/common/AvatarDefs.h" +#include "gui/common/FilesDefs.h" #include "gui/connect/ConfCertDialog.h" #include "gui/connect/PGPKeyDialog.h" @@ -63,12 +64,12 @@ #define IMAGE_EXPORTFRIEND ":/images/user/friend_suggestion16.png" #define IMAGE_ADDFRIEND ":/images/user/add_user16.png" #define IMAGE_FRIENDINFO ":/images/info16.png" -#define IMAGE_CHAT ":/images/chat_24.png" -#define IMAGE_MSG ":/images/mail_new.png" +#define IMAGE_CHAT ":/icons/png/chats.png" +#define IMAGE_MSG ":/icons/mail/write-mail.png" #define IMAGE_CONNECT ":/images/connect_friend.png" #define IMAGE_COPYLINK ":/images/copyrslink.png" #define IMAGE_GROUP16 ":/images/user/group16.png" -#define IMAGE_EDIT ":/images/edit_16.png" +#define IMAGE_EDIT ":/icons/png/pencil-edit-button.png" #define IMAGE_REMOVE ":/images/delete.png" #define IMAGE_EXPAND ":/images/edit_add24.png" #define IMAGE_COLLAPSE ":/images/edit_remove24.png" @@ -177,8 +178,10 @@ NewFriendList::NewFriendList(QWidget *parent) : /* RsAutoUpdatePage(5000,parent) ui->filterLineEdit->setPlaceholderText(tr("Search")) ; ui->filterLineEdit->showFilterIcon(); - mEventHandlerId=0; // forces initialization - rsEvents->registerEventsHandler( RsEventType::PEER_CONNECTION, [this](std::shared_ptr e) { handleEvent(e); }, mEventHandlerId ); + mEventHandlerId=0; // forces initialization + rsEvents->registerEventsHandler( + [this](std::shared_ptr e) { handleEvent(e); }, + mEventHandlerId, RsEventType::PEER_CONNECTION ); mModel = new RsFriendListModel(); mProxyModel = new FriendListSortFilterProxyModel(ui->peerTreeWidget->header(),this); @@ -210,7 +213,7 @@ NewFriendList::NewFriendList(QWidget *parent) : /* RsAutoUpdatePage(5000,parent) // workaround for Qt bug, should be solved in next Qt release 4.7.0 // http://bugreports.qt.nokia.com/browse/QTBUG-8270 QShortcut *Shortcut = new QShortcut(QKeySequence(Qt::Key_Delete), ui->peerTreeWidget, 0, 0, Qt::WidgetShortcut); - connect(Shortcut, SIGNAL(activated()), this, SLOT(removefriend()),Qt::QueuedConnection); + connect(Shortcut, SIGNAL(activated()), this, SLOT(removeItem()),Qt::QueuedConnection); QFontMetricsF fontMetrics(ui->peerTreeWidget->font()); @@ -304,7 +307,7 @@ void NewFriendList::headerContextMenuRequested(QPoint p) hbox->setSpacing(6); QLabel *iconLabel = new QLabel(widget); - QPixmap pix = QPixmap(":/images/user/friends24.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5); + QPixmap pix = FilesDefs::getPixmapFromQtResourcePath(":/images/user/friends24.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5); iconLabel->setPixmap(pix); iconLabel->setMaximumSize(iconLabel->frameSize().height() + pix.height(), pix.width()); hbox->addWidget(iconLabel); @@ -451,6 +454,7 @@ void NewFriendList::processSettings(bool load) if (load) // load settings { + std::cerr <<"Re-loading settings..." << std::endl; // states setShowUnconnected(!Settings->value("hideUnconnected", !mProxyModel->showOfflineNodes()).toBool()); setShowState(Settings->value("showState", mModel->getDisplayStatusString()).toBool()); @@ -530,7 +534,7 @@ void NewFriendList::peerTreeWidgetCustomPopupMenu() hbox->setSpacing(6); QLabel *iconLabel = new QLabel(widget); - QPixmap pix = QPixmap(":/images/user/friends24.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5); + QPixmap pix = FilesDefs::getPixmapFromQtResourcePath(":/images/user/friends24.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5); iconLabel->setPixmap(pix); iconLabel->setMaximumSize(iconLabel->frameSize().height() + pix.height(), pix.width()); hbox->addWidget(iconLabel); @@ -575,20 +579,20 @@ void NewFriendList::peerTreeWidgetCustomPopupMenu() bool standard = group_info.flag & RS_GROUP_FLAG_STANDARD; #ifdef RS_DIRECT_CHAT - contextMenu.addAction(QIcon(IMAGE_MSG), tr("Send message to whole group"), this, SLOT(msgGroup())); + contextMenu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MSG), tr("Send message to whole group"), this, SLOT(msgGroup())); contextMenu.addSeparator(); #endif // RS_DIRECT_CHAT - contextMenu.addAction(QIcon(IMAGE_EDIT), tr("Edit Group"), this, SLOT(editGroup())); + contextMenu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_EDIT), tr("Edit Group"), this, SLOT(editGroup())); - QAction *action = contextMenu.addAction(QIcon(IMAGE_REMOVE), tr("Remove Group"), this, SLOT(removeGroup())); + QAction *action = contextMenu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_REMOVE), tr("Remove Group"), this, SLOT(removeGroup())); action->setDisabled(standard); } break; case RsFriendListModel::ENTRY_TYPE_PROFILE: { - contextMenu.addAction(QIcon(IMAGE_FRIENDINFO), tr("Profile details"), this, SLOT(configureProfile())); - contextMenu.addAction(QIcon(IMAGE_DENYFRIEND), tr("Deny connections"), this, SLOT(removeProfile())); + contextMenu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_FRIENDINFO), tr("Profile details"), this, SLOT(configureProfile())); + contextMenu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_DENYFRIEND), tr("Deny connections"), this, SLOT(removeProfile())); RsFriendListModel::RsProfileDetails details; mModel->getProfileData(index,details); @@ -634,8 +638,8 @@ void NewFriendList::peerTreeWidgetCustomPopupMenu() } } - QMenu *groupsMenu = contextMenu.addMenu(QIcon(IMAGE_GROUP16), tr("Groups")); - groupsMenu->addAction(QIcon(IMAGE_EXPAND), tr("Create new group"), this, SLOT(createNewGroup())); + QMenu *groupsMenu = contextMenu.addMenu(FilesDefs::getIconFromQtResourcePath(IMAGE_GROUP16), tr("Groups")); + groupsMenu->addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_EXPAND), tr("Create new group"), this, SLOT(createNewGroup())); if (addToGroupMenu || moveToGroupMenu || foundGroup) { if (addToGroupMenu) { @@ -672,26 +676,26 @@ void NewFriendList::peerTreeWidgetCustomPopupMenu() case RsFriendListModel::ENTRY_TYPE_NODE: { #ifdef RS_DIRECT_CHAT - contextMenu.addAction(QIcon(IMAGE_CHAT), tr("Chat"), this, SLOT(chatNode())); - contextMenu.addAction(QIcon(IMAGE_MSG), tr("Send message to this node"), this, SLOT(msgNode())); + contextMenu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_CHAT), tr("Chat"), this, SLOT(chatNode())); + contextMenu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MSG), tr("Send message to this node"), this, SLOT(msgNode())); contextMenu.addSeparator(); #endif // RS_DIRECT_CHAT - contextMenu.addAction(QIcon(IMAGE_FRIENDINFO), tr("Node details"), this, SLOT(configureNode())); + contextMenu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_FRIENDINFO), tr("Node details"), this, SLOT(configureNode())); if (type == RsFriendListModel::ENTRY_TYPE_PROFILE || type == RsFriendListModel::ENTRY_TYPE_NODE) - contextMenu.addAction(QIcon(IMAGE_EXPORTFRIEND), tr("Recommend this node to..."), this, SLOT(recommendNode())); + contextMenu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_EXPORTFRIEND), tr("Recommend this node to..."), this, SLOT(recommendNode())); RsFriendListModel::RsNodeDetails details; mModel->getNodeData(index,details); if(!rsPeers->isHiddenNode(rsPeers->getOwnId()) || rsPeers->isHiddenNode( details.id )) - contextMenu.addAction(QIcon(IMAGE_CONNECT), tr("Attempt to connect"), this, SLOT(connectNode())); + contextMenu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_CONNECT), tr("Attempt to connect"), this, SLOT(connectNode())); - contextMenu.addAction(QIcon(IMAGE_COPYLINK), tr("Copy certificate link"), this, SLOT(copyFullCertificate())); + contextMenu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COPYLINK), tr("Copy certificate link"), this, SLOT(copyFullCertificate())); //this is a SSL key - contextMenu.addAction(QIcon(IMAGE_REMOVEFRIEND), tr("Remove Friend Node"), this, SLOT(removeNode())); + contextMenu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_REMOVEFRIEND), tr("Remove Friend Node"), this, SLOT(removeNode())); } } @@ -700,12 +704,12 @@ void NewFriendList::peerTreeWidgetCustomPopupMenu() contextMenu.addSeparator(); - QAction *action = contextMenu.addAction(QIcon(IMAGE_PASTELINK), tr("Paste certificate link"), this, SLOT(pastePerson())); + QAction *action = contextMenu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_PASTELINK), tr("Paste certificate link"), this, SLOT(pastePerson())); if (RSLinkClipboard::empty(RetroShareLink::TYPE_CERTIFICATE)) action->setDisabled(true); - contextMenu.addAction(QIcon(IMAGE_EXPAND), tr("Expand all"), ui->peerTreeWidget, SLOT(expandAll())); - contextMenu.addAction(QIcon(IMAGE_COLLAPSE), tr("Collapse all"), ui->peerTreeWidget, SLOT(collapseAll())); + contextMenu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_EXPAND), tr("Expand all"), ui->peerTreeWidget, SLOT(expandAll())); + contextMenu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COLLAPSE), tr("Collapse all"), ui->peerTreeWidget, SLOT(collapseAll())); contextMenu.addSeparator(); @@ -913,6 +917,25 @@ void FriendsDialog::viewprofile() * * All of these rely on the finding of the current Id. */ + +void NewFriendList::removeItem() +{ + QModelIndex index = getCurrentSourceIndex(); + RsFriendListModel::EntryType type = mModel->getType(index); + if(index.isValid()) + { + switch (type) { + case RsFriendListModel::ENTRY_TYPE_GROUP: removeGroup(); + break; + case RsFriendListModel::ENTRY_TYPE_PROFILE: removeProfile(); + break; + case RsFriendListModel::ENTRY_TYPE_NODE: removeNode(); + break; + case RsFriendListModel::ENTRY_TYPE_UNKNOWN: RsErr()<<__PRETTY_FUNCTION__<<" Get Item of type unknow."< predicate) { std::set expanded_indexes; - std::set selected_indexes; + std::set selected_indexes; - saveExpandedPathsAndSelection(expanded_indexes, selected_indexes); + saveExpandedPathsAndSelection(expanded_indexes, selected_indexes); - mModel->checkInternalData(force); + // This is a hack to avoid crashes on windows while calling endInsertRows(). I'm not sure wether these crashes are + // due to a Qt bug, or a misuse of the proxy model on my side. Anyway, this solves them for good. + // As a side effect we need to save/restore hidden columns because setSourceModel() resets this setting. - restoreExpandedPathsAndSelection(expanded_indexes, selected_indexes); + // save hidden columns and sizes + std::vector col_visible(RsFriendListModel::COLUMN_THREAD_NB_COLUMNS); + std::vector col_sizes(RsFriendListModel::COLUMN_THREAD_NB_COLUMNS); + + for(int i=0;ipeerTreeWidget->isColumnHidden(i); + col_sizes[i] = ui->peerTreeWidget->columnWidth(i); + } + + + mProxyModel->setSourceModel(nullptr); + + predicate(); + + mProxyModel->setSourceModel(mModel); + restoreExpandedPathsAndSelection(expanded_indexes, selected_indexes); + + // restore hidden columns + for(uint32_t i=0;ipeerTreeWidget->setColumnHidden(i,!col_visible[i]); + ui->peerTreeWidget->setColumnWidth(i,col_sizes[i]); + } +} + +void NewFriendList::checkInternalData(bool force) +{ + applyWhileKeepingTree([force,this]() { mModel->checkInternalData(force) ; }); } void NewFriendList::exportFriendlistClicked() @@ -1463,6 +1516,7 @@ bool NewFriendList::isColumnVisible(int col) const } void NewFriendList::setColumnVisible(int col,bool visible) { + std::cerr << "Setting column " << col << " to be visible: " << visible << std::endl; ui->peerTreeWidget->setColumnHidden(col, !visible); } void NewFriendList::toggleColumnVisible() @@ -1480,12 +1534,12 @@ void NewFriendList::toggleColumnVisible() void NewFriendList::setShowState(bool show) { - mModel->setDisplayStatusString(show); + applyWhileKeepingTree([show,this]() { mModel->setDisplayStatusString(show) ; }); } void NewFriendList::setShowGroups(bool show) { - mModel->setDisplayGroups(show); + applyWhileKeepingTree([show,this]() { mModel->setDisplayGroups(show) ; }); } /** diff --git a/retroshare-gui/src/gui/common/NewFriendList.h b/retroshare-gui/src/gui/common/NewFriendList.h index 6e5f617f9..7387a477e 100644 --- a/retroshare-gui/src/gui/common/NewFriendList.h +++ b/retroshare-gui/src/gui/common/NewFriendList.h @@ -25,8 +25,9 @@ #include #include +#include + #include "FriendListModel.h" -#include "RsAutoUpdatePage.h" #include "retroshare/rsstatus.h" namespace Ui { @@ -102,7 +103,9 @@ private: RsFriendListModel *mModel; QAction *mActionSortByState; - void expandGroup(const RsNodeGroupId& gid); + void applyWhileKeepingTree(std::function predicate); + + void expandGroup(const RsNodeGroupId& gid); void recursRestoreExpandedItems(const QModelIndex& index, const QString& parent_path, const std::set& exp, const std::set &sel); void recursSaveExpandedItems(const QModelIndex& index,const QString& parent_path,std::set& exp, std::set& sel); void saveExpandedPathsAndSelection(std::set& expanded_indexes, std::set& selected_indexes); @@ -144,6 +147,7 @@ private slots: void msgGroup(); void msgProfile(); void recommendNode(); + void removeItem(); void removeNode(); void removeProfile(); void createNewGroup() ; diff --git a/retroshare-gui/src/gui/common/PopularityDefs.cpp b/retroshare-gui/src/gui/common/PopularityDefs.cpp index 096cdc1cf..d027c4248 100644 --- a/retroshare-gui/src/gui/common/PopularityDefs.cpp +++ b/retroshare-gui/src/gui/common/PopularityDefs.cpp @@ -20,22 +20,23 @@ #include +#include "FilesDefs.h" #include "PopularityDefs.h" QIcon PopularityDefs::icon(int popularity) { if (popularity <= 1) - return QIcon(":/images/hot_0.png"); + return FilesDefs::getIconFromQtResourcePath(":/images/hot_0.png"); else if (popularity <= 2) /* 1-1 */ - return QIcon(":/images/hot_1.png"); + return FilesDefs::getIconFromQtResourcePath(":/images/hot_1.png"); else if (popularity <= 5) /* 2-2 */ - return QIcon(":/images/hot_2.png"); + return FilesDefs::getIconFromQtResourcePath(":/images/hot_2.png"); else if (popularity <= 10) /* 3-5 */ - return QIcon(":/images/hot_3.png"); + return FilesDefs::getIconFromQtResourcePath(":/images/hot_3.png"); else if (popularity <= 20) /* 6-10 */ - return QIcon(":/images/hot_4.png"); + return FilesDefs::getIconFromQtResourcePath(":/images/hot_4.png"); else /* >10 */ - return QIcon(":/images/hot_5.png"); + return FilesDefs::getIconFromQtResourcePath(":/images/hot_5.png"); } QString PopularityDefs::tooltip(int popularity) diff --git a/retroshare-gui/src/gui/common/RSElidedItemDelegate.cpp b/retroshare-gui/src/gui/common/RSElidedItemDelegate.cpp index de9f476b7..3fa95f898 100644 --- a/retroshare-gui/src/gui/common/RSElidedItemDelegate.cpp +++ b/retroshare-gui/src/gui/common/RSElidedItemDelegate.cpp @@ -20,170 +20,371 @@ #include "RSElidedItemDelegate.h" +#include "gui/common/StyledElidedLabel.h" +#include "util/rsdebug.h" + +#include +#include #include #include #include +#include #include -RSElidedItemDelegate::RSElidedItemDelegate(QObject *parent) - : RSItemDelegate(parent) - , mElided(false) - , mOnlyPlainText(false) - , mContent("") +#include + +#include +#include + +//#define DEBUG_EID_PAINT 1 + +/* To test it you can make an empty.qss file with: +QTreeView::item, QTreeWidget::item, QListWidget::item{ + color: #AB0000; + background-color: #00DC00; +} +QTreeView::item:selected, QTreeWidget::item:selected, QListWidget::item:selected{ + color: #00CD00; + background-color: #0000BA; +} +QTreeView::item:hover, QTreeWidget::item:hover, QListWidget::item:hover{ + color: #0000EF; + background-color: #FEDCBA; +} +QQTreeView::item:selected:hover, TreeWidget::item:selected:hover, QListWidget::item:selected:hover{ + color: #ABCDEF; + background-color: #FE0000; +} + +ForumsDialog, GxsForumThreadWidget { - mRectElision = QRect(); + qproperty-textColorRead: darkgray; + qproperty-textColorUnread: white; + qproperty-textColorUnreadChildren: red; + qproperty-textColorNotSubscribed: white; + qproperty-textColorMissing: darkred; +} +*/ + +RSElidedItemDelegate::RSElidedItemDelegate(QObject *parent) + : RSStyledItemDelegate(parent) + , mOnlyPlainText(false), mPaintRoundedRect(true) +{ +} + +QSize RSElidedItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyleOptionViewItem ownOption (option); + initStyleOption(&ownOption, index); + + const QWidget* widget = option.widget; + QStyle* ownStyle = widget ? widget->style() : QApplication::style(); + + //Only need "…" for text + ownOption.text = "…"; + QRect checkRect = ownStyle->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &ownOption, widget); + QRect iconRect = ownStyle->subElementRect(QStyle::SE_ItemViewItemDecoration, &ownOption, widget); + QRect textRect = ownStyle->subElementRect(QStyle::SE_ItemViewItemText, &ownOption, widget); + + QSize contSize = ownStyle->sizeFromContents( QStyle::CT_ItemViewItem,&ownOption + ,QSize( checkRect.width()+iconRect.width()+textRect.width() + ,qMax(checkRect.height(),qMax(iconRect.height(),textRect.height()))),widget); + + return contSize; +} + +inline QColor getImagePixelColor(QImage img, int x, int y) +{ +#if QT_VERSION >= QT_VERSION_CHECK(5,6,0) +#ifdef DEBUG_EID_PAINT +// RsDbg() << " RSEID: Found Color " << img.pixelColor(x,y).name(QColor::HexArgb).toStdString() << " at " << x << "," << y << std::endl; +#endif + return img.pixelColor(x,y); +#else + return img.pixel(x,y); +#endif } void RSElidedItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { - RSItemDelegate::paint(painter, option, index); -} - -void RSElidedItemDelegate::drawDisplay(QPainter *painter, const QStyleOptionViewItem &option, - const QRect &rect, const QString &text) const -{ - if (!text.isEmpty()) + if(!index.isValid()) { - mContent = text; - QList > lLines; - QString elidedLastLine = ""; - QFontMetrics fontMetrics = option.fontMetrics; - QRect cr = rect; - cr.adjust(spacing().width(), spacing().height(), -spacing().width(), -spacing().height()); - - bool didElide = false; - QChar ellipsisChar(0x2026);//= "…" - int lineSpacing = fontMetrics.lineSpacing(); - int y = 0; - - QString plainText = ""; - if (mOnlyPlainText) - { - plainText = mContent; - } else { - QTextDocument td; - td.setHtml(mContent); - plainText = td.toPlainText(); - } - plainText = plainText.replace("\n",QChar(QChar::LineSeparator)); - plainText = plainText.replace("\r",QChar(QChar::LineSeparator)); - - if (painter) { - painter->setFont(option.font); - QPalette::ColorGroup cg = option.state & QStyle::State_Enabled - ? QPalette::Normal : QPalette::Disabled; - if (cg == QPalette::Normal && !(option.state & QStyle::State_Active)) - cg = QPalette::Inactive; - QColor textColor = option.palette.color(cg, QPalette::Text); - painter->setPen(textColor); - } - - QTextLayout textLayout(plainText, option.font); - QTextOption to = textLayout.textOption(); - to.setAlignment(option.displayAlignment); -#if QT_VERSION >= 0x050000 - bool wordWrap = option.features.testFlag(QStyleOptionViewItem::WrapText); -#else - bool wordWrap = false; -#endif - to.setWrapMode(wordWrap ? QTextOption::WrapAtWordBoundaryOrAnywhere : QTextOption::NoWrap); - textLayout.setTextOption(to); - - textLayout.beginLayout(); - forever { - //Get new line for text. - QTextLine line = textLayout.createLine(); - - if (!line.isValid()) - break;// No more line to write - - line.setLineWidth(cr.width()); - int nextLineY = y + lineSpacing; - - if ((cr.height() >= nextLineY + lineSpacing) && wordWrap) { - //Line written normaly, next line will too - lLines.append(QPair(line, QPoint(0, y))); - y = nextLineY; - } else { - //The next line can't be written. - QString lastLine = plainText.mid(line.textStart()).split(QChar(QChar::LineSeparator)).at(0); - QTextLine lineEnd = textLayout.createLine(); - if (!lineEnd.isValid() && (wordWrap - || (fontMetrics.width(lastLine) < cr.width()))) { - //No more text for next line so this one is OK - lLines.append(QPair(line, QPoint(0, y))); - elidedLastLine=""; - didElide = false; - } else { - //Text is left, so get elided text - if (lastLine == "") { - elidedLastLine = ellipsisChar; - } else { - elidedLastLine = fontMetrics.elidedText(lastLine, Qt::ElideRight, cr.width()-1); - if (elidedLastLine.right(1) != ellipsisChar) - elidedLastLine.append(ellipsisChar);//New line at end - } - didElide = true; - break; - } - } - } - textLayout.endLayout(); - - int iTransX, iTransY = iTransX = 0; - int iHeight = lLines.count() * lineSpacing; - if (didElide) iHeight += lineSpacing; - - //Compute lines translation with alignment - if (option.displayAlignment & Qt::AlignTop) - iTransY = 0; - if (option.displayAlignment & Qt::AlignBottom) - iTransY = cr.height() - iHeight; - if (option.displayAlignment & Qt::AlignVCenter) - iTransY = (cr.height() - iHeight) / 2; - - QPair pair; - QPoint lastPos(-1,-1); - //Now we know how many lines to redraw at good position - foreach (pair, lLines){ - lastPos = pair.second + QPoint(0 + cr.left(), iTransY + cr.top()); - if (painter) pair.first.draw(painter, lastPos); - } - - //Print last elided line - if (didElide) { - int width = fontMetrics.width(elidedLastLine); - if (lastPos.y() == -1){ - y = iTransY;// Only one line - } else { - y = lastPos.y() + lineSpacing; - } - if (width < cr.width()){ - //Text don't taking all line (with line break), so align it - if (option.displayAlignment & Qt::AlignLeft) - iTransX = 0; - if (option.displayAlignment & Qt::AlignRight) - iTransX = cr.width() - width; - if (option.displayAlignment & Qt::AlignHCenter) - iTransX = (cr.width() - width) / 2; - if (option.displayAlignment & Qt::AlignJustify) - iTransX = 0; - } - - if (painter) painter->drawText(QPoint(iTransX + cr.left(), y + fontMetrics.ascent() + cr.top()), elidedLastLine); - //Draw button to get ToolTip - mRectElision = QRect(iTransX + width - fontMetrics.width(ellipsisChar) + cr.left() - , y + cr.top() - , fontMetrics.width(ellipsisChar) - , fontMetrics.height() - 1); - if (painter) painter->drawRoundRect(mRectElision); - - } else { - mRectElision = QRect(); - } - - mElided = didElide; + RsErr() << __PRETTY_FUNCTION__ << " attempt to draw an invalid index." << std::endl; + return ; } + painter->save(); + QStyleOptionViewItem ownOption (option); + initStyleOption(&ownOption, index); + //Prefer use icon from option + if (!option.icon.isNull()) + ownOption.icon = option.icon; + +#ifdef DEBUG_EID_PAINT + RsDbg() << __PRETTY_FUNCTION__ << std::endl << " RSEID: Enter for item with text:" << ownOption.text.toStdString() << std::endl; +#endif + + const QWidget* widget = option.widget; + QStyle* ownStyle = widget ? widget->style() : QApplication::style(); + + if (!mOnlyPlainText) + { + QTextDocument td; + td.setHtml(ownOption.text); + ownOption.text = td.toPlainText(); + } + // Get Font as option.font is not accurate + if (index.data(Qt::FontRole).type() == QVariant::Font) { + QFont font = index.data(Qt::FontRole).value(); + ownOption.font = font; + ownOption.fontMetrics = QFontMetrics(font); +#ifdef DEBUG_EID_PAINT + QFontInfo info(font); + RsDbg() << " RSEID: Found font in model:" << info.family().toStdString() << std::endl; +#endif + } + // Get Text color from model if one exists + QColor textColor; + if (index.data(Qt::TextColorRole).isValid()) { + //textColor = QColor(index.data(Qt::TextColorRole).toString());//Needs to pass from string else loose RBG format. + textColor = index.data(Qt::TextColorRole).value(); +#ifdef DEBUG_EID_PAINT + RsDbg() << " RSEID: Found text color in model:" << textColor.name().toStdString() << std::endl; +#endif + } + // Get Brush from model if one exists + QBrush bgBrush; + bgBrush.setColor(QColor());// To get color().spec()==QColor::Invalid) + if (index.data(Qt::BackgroundRole).isValid()) { + bgBrush = index.data(Qt::BackgroundRole).value(); +#ifdef DEBUG_EID_PAINT + RsDbg() << " RSEID: Found bg brush in model:" << bgBrush.color().name().toStdString() << std::endl; +#endif + } + + // If we get text and bg color from model, no need to retrieve it from base + if ( (bgBrush.color().spec()==QColor::Invalid) || (textColor.spec()!=QColor::Invalid) ) + { +#ifdef DEBUG_EID_PAINT + RsDbg() << " RSEID:" + << ((bgBrush.color().spec()==QColor::Invalid) ? " Brush not defined" : "") + << ((textColor.spec()==QColor::Invalid) ? " Text Color not defined" : "") + << " so get it from base image." << std::endl; +#endif + // QPalette is not updated by StyleSheet all occurs in internal class. (QRenderRule) + // https://code.woboq.org/qt5/qtbase/src/widgets/styles/qstylesheetstyle.cpp.html#4138 + // void QStyleSheetStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter *p, const QWidget *w) const + // case CE_ItemViewItem: + // So we have to print it in Image to get colors by pixel + QSize moSize=sizeHint(option,index); + if (moSize.width() <= 20) + moSize.setWidth(20); +#ifdef DEBUG_EID_PAINT + RsDbg() << " RSEID: for item size = " << moSize.width() << "x" << moSize.height() << std::endl; +#endif + + QImage moImg(moSize,QImage::Format_ARGB32); + QPainter moPnt; + moPnt.begin(&moImg); + moPnt.setCompositionMode (QPainter::CompositionMode_Source); + moPnt.fillRect(moImg.rect(), Qt::transparent); + moPnt.setCompositionMode (QPainter::CompositionMode_SourceOver); + + QStyleOptionViewItem moOption (option); + // Define option to get only what we want + { + moOption.rect = QRect(QPoint(0,0),moSize); + moOption.state = ownOption.state; + moOption.text = " ████████████████";//Add a blank char to get BackGround Color at top left + // Remove unwanted info. Yes it can draw without that all public data ... + moOption.backgroundBrush = QBrush(); + moOption.checkState = Qt::Unchecked; + moOption.decorationAlignment = Qt::AlignLeft; + moOption.decorationPosition = QStyleOptionViewItem::Left; + moOption.decorationSize = QSize(); + moOption.displayAlignment = Qt::AlignLeft | Qt::AlignTop; + moOption.features=0; + moOption.font = QFont(); + moOption.icon = QIcon(); + moOption.index = QModelIndex(); + moOption.locale = QLocale(); + moOption.showDecorationSelected = false; + moOption.textElideMode = Qt::ElideNone; + moOption.viewItemPosition = QStyleOptionViewItem::Middle; + //moOption.widget = nullptr; //Needed. + + moOption.direction = Qt::LayoutDirectionAuto; + moOption.fontMetrics = QFontMetrics(QFont()); + moOption.palette = QPalette(); + moOption.styleObject = nullptr; + } + QStyledItemDelegate::paint(&moPnt, moOption, QModelIndex()); + + //// But these lines doesn't works. + { + //QStyleOptionViewItem moOptionsState; + //moOptionsState.initFrom(moOption.widget); + //moOptionsState.rect = QRect(QPoint(0,0),moSize); + //moOptionsState.state = QStyle::State_MouseOver | QStyle::State_Enabled | QStyle::State_Sibling; + //moOptionsState.text = "████████"; + //moOptionsState.widget = option.widget; + //QStyledItemDelegate::paint(&moPnt, moOptionsState, QModelIndex()); + } + + moPnt.end(); +#ifdef DEBUG_EID_PAINT + // To save what it paint in application path + moImg.save("image.png"); +#endif + // Get Color in this rect. + { + QColor moColor; + QColor moBGColor=getImagePixelColor(moImg,1,1); // BackGround may be paint. + QColor moColorBorder;// To avoid Border pixel + int moWidth = moImg.size().width(), moHeight = moImg.size().height(); + for (int x = 0; (xsetPen(textColor); + painter->setBrush(bgBrush); + ownOption.backgroundBrush = bgBrush; + + // Code from: https://code.woboq.org/qt5/qtbase/src/widgets/styles/qcommonstyle.cpp.html#2271 + QRect checkRect = ownStyle->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &ownOption, widget); + QRect iconRect = ownStyle->subElementRect(QStyle::SE_ItemViewItemDecoration, &ownOption, widget); + QRect textRect = ownStyle->subElementRect(QStyle::SE_ItemViewItemText, &ownOption, widget); + + // Draw the background + if (bgBrush.color().alpha() == 0) + // No BackGround Color found, use default delegate to draw it. + ownStyle->proxy()->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, widget);// This prefer draw StyleSheet bg than item one. + else + painter->fillRect(ownOption.rect,bgBrush); + +#ifdef DEBUG_EID_PAINT + { + QStyleOptionViewItem tstOption = option; + // Reduce rect to get this item bg color external and base internal + tstOption.rect.adjust(3,3,-6,-6); + // To draw with base for debug purpose + QStyledItemDelegate::paint(painter, tstOption, index); + } +#endif + + // draw the check mark + if (ownOption.features & QStyleOptionViewItem::HasCheckIndicator) { + QStyleOptionViewItem option(*&ownOption); + option.rect = checkRect; + option.state = option.state & ~QStyle::State_HasFocus; + switch (ownOption.checkState) { + case Qt::Unchecked: + option.state |= QStyle::State_Off; + break; + case Qt::PartiallyChecked: + option.state |= QStyle::State_NoChange; + break; + case Qt::Checked: + option.state |= QStyle::State_On; + break; + } + ownStyle->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &option, painter, widget); + } + // draw the icon + { + if (!ownOption.icon.isNull()) + { + QString status; + if (index.data(Qt::StatusTipRole).canConvert(QMetaType::QString)) + status = index.data(Qt::StatusTipRole).toString(); + + // Draw item Icon + { + QIcon::Mode mode = QIcon::Normal; + if (!(ownOption.state & QStyle::State_Enabled)) + mode = QIcon::Disabled; + else if (ownOption.state & QStyle::State_Selected) + mode = QIcon::Selected; + QIcon::State state = ownOption.state & QStyle::State_Open ? QIcon::On : QIcon::Off; + ownOption.icon.paint(painter, iconRect, ownOption.decorationAlignment, mode, state); + } + // Then overlay with waiting + if (status.toLower() == "waiting") + { + const QAbstractItemView* aiv = dynamic_cast(option.widget); + if (aiv) + QTimer::singleShot(200, aiv->viewport(), SLOT(update())); + + QSize waitSize=iconRect.size(); + qreal diag = qMin(waitSize.height(),waitSize.height())*std::sqrt(2); + auto now = std::chrono::system_clock::now().time_since_epoch(); + auto s = std::chrono::duration_cast(now).count(); + auto ms = std::chrono::duration_cast(now - std::chrono::duration_cast(now)).count(); + int duration = 3;// Time (s) to make a revolution. + auto time = (s%duration)*1000 + ms; + qreal angle = 360.0*(time/(duration*1000.0)); + qreal add = 120*(time/(duration*1000.0))*std::abs(sin(qDegreesToRadians(angle/2))); + painter->setPen(QPen(QBrush(ownOption.palette.color(QPalette::Normal, QPalette::WindowText)),diag/10,Qt::DotLine,Qt::RoundCap)); + painter->drawEllipse( iconRect.x()+iconRect.width() /2 + (diag/4)*cos(qDegreesToRadians(angle )) + , iconRect.y()+iconRect.height()/2 + (diag/4)*sin(qDegreesToRadians(angle )), 1, 1); + painter->setPen(QPen(QBrush(ownOption.palette.color(QPalette::Normal, QPalette::Midlight)),diag/10,Qt::DotLine,Qt::RoundCap)); + painter->drawEllipse( iconRect.x()+iconRect.width() /2 + (diag/4)*cos(qDegreesToRadians(angle- add)) + , iconRect.y()+iconRect.height()/2 + (diag/4)*sin(qDegreesToRadians(angle- add)), 1, 1); + painter->setPen(QPen(QBrush(ownOption.palette.color(QPalette::Normal, QPalette::Window)),diag/10,Qt::DotLine,Qt::RoundCap)); + painter->drawEllipse( iconRect.x()+iconRect.width() /2 + (diag/4)*cos(qDegreesToRadians(angle-2*add)) + , iconRect.y()+iconRect.height()/2 + (diag/4)*sin(qDegreesToRadians(angle-2*add)), 1, 1); + } + } + } + // draw the text + if (!ownOption.text.isEmpty()) { +#ifdef DEBUG_EID_PAINT + // To draw text near base one. + ownOption.text = ownOption.text.prepend("__"); +#endif + + QTextLayout textLayout(ownOption.text, painter->font()); + QTextOption to = textLayout.textOption(); + StyledElidedLabel::paintElidedLine(painter,ownOption.text,textRect,ownOption.font,ownOption.displayAlignment,to.wrapMode()&QTextOption::WordWrap,mPaintRoundedRect); + } + painter->restore(); +#ifdef DEBUG_EID_PAINT + RsDbg() << " RSEID: Finished" << std::endl; +#endif } bool RSElidedItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) @@ -192,38 +393,39 @@ bool RSElidedItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, QMouseEvent *ev = static_cast(event); if (ev) { if (ev->buttons()==Qt::LeftButton) { + QVariant var = index.data(); if (index.data().type() == QVariant::String) { QString text = index.data().toString(); if (!text.isEmpty()) { - QRect rect = option.rect; - //Get decoration Rect to get rect == to rect sent in drawDisplay - if (index.data(Qt::DecorationRole).type() == QVariant::Pixmap) { - QPixmap pixmap = index.data(Qt::DecorationRole).value(); - if (!pixmap.isNull()) rect.adjust(pixmap.width() + 6,0,0,0);//Don't know where come from these 6 pixels... - } - if (index.data(Qt::DecorationRole).type() == QVariant::Image) { - QImage image = index.data(Qt::DecorationRole).value(); - if (!image.isNull()) rect.adjust(image.width() + 6,0,0,0);//Don't know where come from these 6 pixels... - } - if (index.data(Qt::DecorationRole).type() == QVariant::Icon) { - QIcon icon = index.data(Qt::DecorationRole).value(); - if (!icon.isNull()) { - QSize size = icon.actualSize(rect.size()); - rect.adjust(size.width() + 6,0,0,0);//Don't know where come from these 6 pixels... - } + + QStyleOptionViewItem ownOption (option); + initStyleOption(&ownOption, index); + + const QWidget* widget = option.widget; + QStyle* ownStyle = widget ? widget->style() : QApplication::style(); + + if (!mOnlyPlainText) + { + QTextDocument td; + td.setHtml(ownOption.text); + ownOption.text = td.toPlainText(); } //Get Font as option.font is not accurate - QStyleOptionViewItem newOption = option; if (index.data(Qt::FontRole).type() == QVariant::Font) { QFont font = index.data(Qt::FontRole).value(); - newOption.font = font; - newOption.fontMetrics = QFontMetrics(font); + ownOption.font = font; + ownOption.fontMetrics = QFontMetrics(font); } + QRect textRect = ownStyle->subElementRect(QStyle::SE_ItemViewItemText, &ownOption, widget); + + QTextLayout textLayout(text, ownOption.font); + QTextOption to = textLayout.textOption(); //Update RSElidedItemDelegate as only one delegate for all items - drawDisplay(NULL, newOption, rect, text); - if (mElided && (mRectElision.contains(ev->pos()))){ - QToolTip::showText(ev->globalPos(),QString("") + mContent + QString("")); + QRect rectElision; + bool elided = StyledElidedLabel::paintElidedLine(nullptr,text,textRect,ownOption.font,ownOption.displayAlignment,to.wrapMode()&QTextOption::WordWrap,true,&rectElision); + if (elided && (rectElision.contains(ev->pos()))){ + QToolTip::showText(ev->globalPos(),QString("") + text + QString("")); return true; // eat event } } @@ -231,5 +433,5 @@ bool RSElidedItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, } } } - return RSItemDelegate::editorEvent(event, model, option, index); + return RSStyledItemDelegate::editorEvent(event, model, option, index); } diff --git a/retroshare-gui/src/gui/common/RSElidedItemDelegate.h b/retroshare-gui/src/gui/common/RSElidedItemDelegate.h index f2d6e30cb..3d8f67a8d 100644 --- a/retroshare-gui/src/gui/common/RSElidedItemDelegate.h +++ b/retroshare-gui/src/gui/common/RSElidedItemDelegate.h @@ -23,37 +23,35 @@ #include -class RSElidedItemDelegate : public RSItemDelegate +class RSElidedItemDelegate : public RSStyledItemDelegate { Q_OBJECT - Q_PROPERTY(bool isElided READ isElided) + + // For now, these properties cannot be changed by StyleSheet + // If needed, you can add properties to owner widget then copy them in this delegate. Q_PROPERTY(bool isOnlyPlainText READ isOnlyPlainText WRITE setOnlyPlainText) - Q_PROPERTY(QRect rectElision READ rectElision) + Q_PROPERTY(bool paintRoundedRect READ paintRoundedRect WRITE setPaintRoundedRect) public: RSElidedItemDelegate(QObject *parent = 0); - void paint (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; + void paint (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; - bool isElided() const { return mElided; } bool isOnlyPlainText() const { return mOnlyPlainText; } void setOnlyPlainText(const bool &value) { mOnlyPlainText = value; } - QRect rectElision() { return mRectElision; } + bool paintRoundedRect() const { return mPaintRoundedRect; } + void setPaintRoundedRect(const bool &value) { mPaintRoundedRect = value; } protected: - void drawDisplay(QPainter *painter, const QStyleOptionViewItem &option, - const QRect &rect, const QString &text) const; bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, - const QModelIndex &index); + const QModelIndex &index) override; private: - mutable bool mElided; bool mOnlyPlainText; - mutable QString mContent; - mutable QRect mRectElision; - + bool mPaintRoundedRect; }; #endif // RSELIDEDITEMDELEGATE_H diff --git a/retroshare-gui/src/gui/common/RSItemDelegate.cpp b/retroshare-gui/src/gui/common/RSItemDelegate.cpp index d94b24a0e..8dc2fcea3 100644 --- a/retroshare-gui/src/gui/common/RSItemDelegate.cpp +++ b/retroshare-gui/src/gui/common/RSItemDelegate.cpp @@ -19,6 +19,7 @@ *******************************************************************************/ #include "RSItemDelegate.h" +#include "util/rsdebug.h" RSItemDelegate::RSItemDelegate(QObject *parent) : QItemDelegate(parent) { @@ -61,3 +62,48 @@ void RSItemDelegate::setSpacing(const QSize &spacing) { m_spacing = spacing; } + + +/** RSStyledItemDelegate **/ + +RSStyledItemDelegate::RSStyledItemDelegate(QObject *parent) : QStyledItemDelegate(parent) +{ +} + +void RSStyledItemDelegate::paint (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyleOptionViewItem ownOption (option); + + if (m_noFocusRect.indexOf(index.column()) >= 0) { + ownOption.state &= ~QStyle::State_HasFocus; // don't show text and focus rectangle + } + + QStyledItemDelegate::paint (painter, ownOption, index); +} + +QSize RSStyledItemDelegate::sizeHint (const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyleOptionViewItem ownOption (option); + + if (m_noFocusRect.indexOf(index.column()) >= 0) { + ownOption.state &= ~QStyle::State_HasFocus; // don't show text and focus rectangle + } + + QSize size = QStyledItemDelegate::sizeHint(ownOption, index); + + size += m_spacing; + + return size; +} + +void RSStyledItemDelegate::removeFocusRect(int column) +{ + if (m_noFocusRect.indexOf(column) == -1) { + m_noFocusRect.push_back(column); + } +} + +void RSStyledItemDelegate::setSpacing(const QSize &spacing) +{ + m_spacing = spacing; +} diff --git a/retroshare-gui/src/gui/common/RSItemDelegate.h b/retroshare-gui/src/gui/common/RSItemDelegate.h index 3eb056b21..73599e9f5 100644 --- a/retroshare-gui/src/gui/common/RSItemDelegate.h +++ b/retroshare-gui/src/gui/common/RSItemDelegate.h @@ -22,14 +22,32 @@ #define _RSITEMDELEGATE_H #include +#include class RSItemDelegate : public QItemDelegate { public: - RSItemDelegate(QObject *parent = 0); + RSItemDelegate(QObject *parent = nullptr); - void paint (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; - QSize sizeHint (const QStyleOptionViewItem &option, const QModelIndex &index) const; + void paint (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + QSize sizeHint (const QStyleOptionViewItem &option, const QModelIndex &index) const override; + + void removeFocusRect(int column); + void setSpacing(const QSize &spacing); + QSize spacing() const { return m_spacing; } +private: + QList m_noFocusRect; + QSize m_spacing; +}; + + +class RSStyledItemDelegate : public QStyledItemDelegate +{ +public: + RSStyledItemDelegate(QObject *parent = nullptr); + + void paint (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + QSize sizeHint (const QStyleOptionViewItem &option, const QModelIndex &index) const override; void removeFocusRect(int column); void setSpacing(const QSize &spacing); diff --git a/retroshare-gui/src/gui/common/RSTextBrowser.cpp b/retroshare-gui/src/gui/common/RSTextBrowser.cpp index 46053bb66..552369a77 100644 --- a/retroshare-gui/src/gui/common/RSTextBrowser.cpp +++ b/retroshare-gui/src/gui/common/RSTextBrowser.cpp @@ -27,6 +27,7 @@ #include "RSTextBrowser.h" #include "RSImageBlockWidget.h" +#include "gui/common/FilesDefs.h" #include //To get RsAccounts @@ -149,7 +150,7 @@ QVariant RSTextBrowser::loadResource(int type, const QUrl &name) QPixmap RSTextBrowser::getBlockedImage() { - return QPixmap(":/images/imageblocked_24.png"); + return FilesDefs::getPixmapFromQtResourcePath(":/images/imageblocked_24.png"); } void RSTextBrowser::setImageBlockWidget(RSImageBlockWidget *widget) diff --git a/retroshare-gui/src/gui/common/RSTextBrowser.h b/retroshare-gui/src/gui/common/RSTextBrowser.h index 1434bbb72..1f4cafd52 100644 --- a/retroshare-gui/src/gui/common/RSTextBrowser.h +++ b/retroshare-gui/src/gui/common/RSTextBrowser.h @@ -34,6 +34,7 @@ class RSTextBrowser : public QTextBrowser Q_OBJECT Q_PROPERTY(QColor textColorQuote READ textColorQuote WRITE setTextColorQuote) + Q_PROPERTY(QVariant textColorQuotes READ textColorQuotes WRITE setTextColorQuotes) public: explicit RSTextBrowser(QWidget *parent = 0); @@ -52,11 +53,13 @@ public: virtual QVariant loadResource(int type, const QUrl &name); QColor textColorQuote() const { return highlighter->textColorQuote();} + QVariant textColorQuotes() const { return highlighter->textColorQuotes();} bool getShowImages() const { return mShowImages; } public slots: void showImages(); void setTextColorQuote(QColor textColorQuote) { highlighter->setTextColorQuote(textColorQuote);} + void setTextColorQuotes(QVariant textColorQuotes) { highlighter->setTextColorQuotes(textColorQuotes);} private slots: void linkClicked(const QUrl &url); diff --git a/retroshare-gui/src/gui/common/RSTreeView.cpp b/retroshare-gui/src/gui/common/RSTreeView.cpp index 918fbc558..72b191fe2 100644 --- a/retroshare-gui/src/gui/common/RSTreeView.cpp +++ b/retroshare-gui/src/gui/common/RSTreeView.cpp @@ -19,10 +19,46 @@ *******************************************************************************/ #include +#include #include "RSTreeView.h" RSTreeView::RSTreeView(QWidget *parent) : QTreeView(parent) { + setMouseTracking(false); // normally the default, but who knows if it's not goign to change in the future. +} + +void RSTreeView::wheelEvent(QWheelEvent *e) +{ + if(e->modifiers() == Qt::ControlModifier) + { + emit zoomRequested(e->delta() > 0); + return; + } + else + QTreeView::wheelEvent(e); +} + +void RSTreeView::mouseMoveEvent(QMouseEvent *e) +{ + QModelIndex idx = indexAt(e->pos()); + + if(idx != selectionModel()->currentIndex()) + selectionModel()->setCurrentIndex(idx,QItemSelectionModel::ClearAndSelect); + + QTreeView::mouseMoveEvent(e); +} + +void RSTreeView::setAutoSelect(bool b) +{ + if(b) + setMouseTracking(true); + else + setMouseTracking(false); +} + +void RSTreeView::resizeEvent(QResizeEvent *e) +{ + emit sizeChanged(e->size()); } void RSTreeView::setPlaceholderText(const QString &text) diff --git a/retroshare-gui/src/gui/common/RSTreeView.h b/retroshare-gui/src/gui/common/RSTreeView.h index 022a643db..116ccac31 100644 --- a/retroshare-gui/src/gui/common/RSTreeView.h +++ b/retroshare-gui/src/gui/common/RSTreeView.h @@ -33,8 +33,20 @@ public: void setPlaceholderText(const QString &text); + // Use this to make selection automatic based on mouse position. This is useful to trigger selection and therefore editing mode + // in trees that show editing widgets using a QStyledItemDelegate + + void setAutoSelect(bool b); + +signals: + void sizeChanged(QSize); + void zoomRequested(bool zoom_or_unzoom); + protected: - void paintEvent(QPaintEvent *event); + virtual void mouseMoveEvent(QMouseEvent *e) override; // overriding so as to manage auto-selection + virtual void wheelEvent(QWheelEvent *e) override; // overriding so as to manage zoom + virtual void resizeEvent(QResizeEvent *e) override; + virtual void paintEvent(QPaintEvent *event) override; QString placeholderText; }; diff --git a/retroshare-gui/src/gui/common/RSTreeWidget.cpp b/retroshare-gui/src/gui/common/RSTreeWidget.cpp index 9322535fe..bcd8eef53 100644 --- a/retroshare-gui/src/gui/common/RSTreeWidget.cpp +++ b/retroshare-gui/src/gui/common/RSTreeWidget.cpp @@ -29,6 +29,7 @@ #include #include "gui/settings/rsharesettings.h" +#include "gui/common/FilesDefs.h" RSTreeWidget::RSTreeWidget(QWidget *parent) : QTreeWidget(parent) { @@ -259,7 +260,7 @@ QMenu *RSTreeWidget::createStandardContextMenu(QMenu *contextMenu) hbox->setSpacing(6); QLabel *iconLabel = new QLabel(widget); - QPixmap pix = QPixmap(":/images/settings.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5); + QPixmap pix = FilesDefs::getPixmapFromQtResourcePath(":/images/settings.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5); iconLabel->setPixmap(pix); iconLabel->setMaximumSize(iconLabel->frameSize().height() + pix.height(), pix.width()); hbox->addWidget(iconLabel); diff --git a/retroshare-gui/src/gui/common/RsCollection.cpp b/retroshare-gui/src/gui/common/RsCollection.cpp index 53661fb75..6a78a1cf8 100644 --- a/retroshare-gui/src/gui/common/RsCollection.cpp +++ b/retroshare-gui/src/gui/common/RsCollection.cpp @@ -44,7 +44,7 @@ RsCollection::RsCollection(QObject *parent) _xml_doc.appendChild(_root); } -RsCollection::RsCollection(const FileTree& fr) +RsCollection::RsCollection(const RsFileTree& fr) : _xml_doc("RsCollection") { _root = _xml_doc.createElement("RsCollection"); @@ -153,7 +153,7 @@ void RsCollection::merge_in(const QString& fname,uint64_t size,const RsFileHash& recursAddElements(_xml_doc,info,_root) ; } -void RsCollection::merge_in(const FileTree& tree) +void RsCollection::merge_in(const RsFileTree& tree) { recursAddElements(_xml_doc,tree,0,_root) ; } @@ -273,14 +273,14 @@ void RsCollection::recursAddElements(QDomDocument& doc,const ColFileInfo& colFil } } -void RsCollection::recursAddElements(QDomDocument& doc,const FileTree& ft,uint32_t index,QDomElement& e) const +void RsCollection::recursAddElements( + QDomDocument& doc, const RsFileTree& ft, uint32_t index, + QDomElement& e ) const { - std::vector subdirs ; - std::vector subfiles ; + std::vector subdirs; + std::vector subfiles ; std::string name; - - if(!ft.getDirectoryContent(index,name,subdirs,subfiles)) - return ; + if(!ft.getDirectoryContent(name, subdirs, subfiles, index)) return; QDomElement d = doc.createElement("Directory") ; d.setAttribute(QString("name"),QString::fromUtf8(name.c_str())) ; diff --git a/retroshare-gui/src/gui/common/RsCollection.h b/retroshare-gui/src/gui/common/RsCollection.h index f1a8cb866..492d407d8 100644 --- a/retroshare-gui/src/gui/common/RsCollection.h +++ b/retroshare-gui/src/gui/common/RsCollection.h @@ -62,11 +62,11 @@ public: RsCollection(QObject *parent = 0) ; // create from list of files and directories RsCollection(const std::vector& file_entries, FileSearchFlags flags, QObject *parent = 0) ; - RsCollection(const FileTree& fr); + RsCollection(const RsFileTree& fr); virtual ~RsCollection() ; void merge_in(const QString& fname,uint64_t size,const RsFileHash& hash) ; - void merge_in(const FileTree& tree) ; + void merge_in(const RsFileTree& tree) ; static const QString ExtensionString ; @@ -99,7 +99,9 @@ private: void recursAddElements(QDomDocument&, const DirDetails&, QDomElement&, FileSearchFlags flags) const ; void recursAddElements(QDomDocument&,const ColFileInfo&,QDomElement&) const; - void recursAddElements(QDomDocument& doc,const FileTree& ft,uint32_t index,QDomElement& e) const; + void recursAddElements( + QDomDocument& doc, const RsFileTree& ft, uint32_t index, + QDomElement& e ) const; void recursCollectColFileInfos(const QDomElement&,std::vector& colFileInfos,const QString& current_dir,bool bad_chars_in_parent) const ; // check that the file is a valid rscollection file, and not a lol bomb or some shit like this diff --git a/retroshare-gui/src/gui/common/RsCollectionDialog.cpp b/retroshare-gui/src/gui/common/RsCollectionDialog.cpp index 190fcccc5..b3f53a220 100644 --- a/retroshare-gui/src/gui/common/RsCollectionDialog.cpp +++ b/retroshare-gui/src/gui/common/RsCollectionDialog.cpp @@ -18,6 +18,7 @@ * * *******************************************************************************/ +#include "gui/common/FilesDefs.h" #include "RsCollectionDialog.h" #include "RsCollection.h" @@ -142,7 +143,7 @@ RsCollectionDialog::RsCollectionDialog(const QString& collectionFileName setWindowTitle(QString("%1 - %2").arg(windowTitle()).arg(QFileInfo(_fileName).completeBaseName())); - ui.headerFrame->setHeaderImage(QPixmap(":/images/library64.png")); + ui.headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/icons/collections.png")); if(creation) { @@ -254,7 +255,7 @@ void RsCollectionDialog::openDestinationDirectoryMenu() contextMnu.addAction(QString::fromUtf8((*it).filename.c_str()), this, SLOT(setDestinationDirectory()))->setData(QString::fromUtf8( (*it).filename.c_str() ) ) ; } - contextMnu.addAction( QIcon(IMAGE_SEARCH),tr("Specify..."),this,SLOT(chooseDestinationDirectory())); + contextMnu.addAction( FilesDefs::getIconFromQtResourcePath(IMAGE_SEARCH),tr("Specify..."),this,SLOT(chooseDestinationDirectory())); contextMnu.exec(QCursor::pos()) ; } @@ -537,8 +538,9 @@ bool RsCollectionDialog::addChild(QTreeWidgetItem* parent, const std::vectorsetTextColor(COLUMN_FILE, QColor(255,80,120)) ; + item->setData(COLUMN_FILE, Qt::ForegroundRole, QColor(255,80,120)) ; } if (parentsFounds.empty()) { diff --git a/retroshare-gui/src/gui/common/RsCollectionDialog.ui b/retroshare-gui/src/gui/common/RsCollectionDialog.ui index 8b72f68e5..ceaea3b99 100644 --- a/retroshare-gui/src/gui/common/RsCollectionDialog.ui +++ b/retroshare-gui/src/gui/common/RsCollectionDialog.ui @@ -308,8 +308,8 @@ - - :/images/Update.png:/images/Update.png + + :/images/update.png:/images/update.png diff --git a/retroshare-gui/src/gui/common/SubscribeToolButton.cpp b/retroshare-gui/src/gui/common/SubscribeToolButton.cpp index 4bb0147e6..4015e641c 100644 --- a/retroshare-gui/src/gui/common/SubscribeToolButton.cpp +++ b/retroshare-gui/src/gui/common/SubscribeToolButton.cpp @@ -18,6 +18,7 @@ * * *******************************************************************************/ +#include "gui/common/FilesDefs.h" #include #include "SubscribeToolButton.h" @@ -64,14 +65,14 @@ void SubscribeToolButton::updateUi() #else setPopupMode(QToolButton::InstantPopup); #endif - //setIcon(QIcon(":/images/accepted16.png")); + //setIcon(FilesDefs::getIconFromQtResourcePath(":/images/accepted16.png")); setText(tr("Subscribed")); if(mMenu != NULL) // that's because setMenu does not give away memory ownership delete mMenu ; mMenu = new QMenu; - mMenu->addAction(QIcon(":/images/cancel.png"), tr("Unsubscribe"), this, SLOT(unsubscribePrivate())); + mMenu->addAction(FilesDefs::getIconFromQtResourcePath(":/images/cancel.png"), tr("Unsubscribe"), this, SLOT(unsubscribePrivate())); if (!mSubscribedActions.empty()) { mMenu->addSeparator(); @@ -86,7 +87,7 @@ void SubscribeToolButton::updateUi() } else { setPopupMode(QToolButton::DelayedPopup); setMenu(NULL); - //setIcon(QIcon(":/images/RSS_004_32.png")); + //setIcon(FilesDefs::getIconFromQtResourcePath(":/images/RSS_004_32.png")); setText(tr("Subscribe")); #ifndef USE_MENUBUTTONPOPUP diff --git a/retroshare-gui/src/gui/common/UserNotify.cpp b/retroshare-gui/src/gui/common/UserNotify.cpp index e030f9ccf..8884caea3 100644 --- a/retroshare-gui/src/gui/common/UserNotify.cpp +++ b/retroshare-gui/src/gui/common/UserNotify.cpp @@ -88,11 +88,12 @@ void UserNotify::setNotifyEnabled(bool enabled, bool combined, bool blink) Settings->endGroup(); } -void UserNotify::initialize(QToolBar *mainToolBar, QAction *mainAction, QListWidgetItem *listItem) +void UserNotify::initialize(QToolBar *mainToolBar, QAction *mainAction, QListWidgetItem *listItem,const QString& subtext) { mMainAction = mainAction; if (mMainAction) { mButtonText = mMainAction->text(); + mButtonText2 = subtext; if (mainToolBar) { mMainToolButton = dynamic_cast(mainToolBar->widgetForAction(mMainAction)); } @@ -165,7 +166,18 @@ void UserNotify::update() if (mMainAction) { mMainAction->setIcon(getMainIcon(count > 0)); - mMainAction->setText((count > 0) ? QString("%1 (%2)").arg(mButtonText).arg(count) : mButtonText); + + if(count > 0) + { + if(!mButtonText2.isNull()) + mMainAction->setToolTip(QString("%1 (%2 %3)").arg(mButtonText).arg(count).arg(mButtonText2)); + else + mMainAction->setToolTip(QString("%1 (%2)").arg(mButtonText).arg(count)); + + mMainAction->setText(QString("%1 (%2)").arg(mButtonText).arg(count)); + } + else + mMainAction->setText(mButtonText); QFont font = mMainAction->font(); font.setBold(count > 0); diff --git a/retroshare-gui/src/gui/common/UserNotify.h b/retroshare-gui/src/gui/common/UserNotify.h index 500c26dc3..8c8f99e2d 100644 --- a/retroshare-gui/src/gui/common/UserNotify.h +++ b/retroshare-gui/src/gui/common/UserNotify.h @@ -37,12 +37,17 @@ public: UserNotify(QObject *parent = 0); virtual ~UserNotify(); - void initialize(QToolBar *mainToolBar, QAction *mainAction, QListWidgetItem *listItem); + void initialize(QToolBar *mainToolBar, QAction *mainAction, QListWidgetItem *listItem,const QString& subtext); void createIcons(QMenu *notifyMenu); QSystemTrayIcon* getTrayIcon(){ return mTrayIcon;} QAction* getNotifyIcon(){ return mNotifyIcon;} virtual bool hasSetting(QString */*name*/, QString */*group*/) { return false; } + + // UserNotify is used to display tooltips when some services have no messages and so on, in the format of "Name (43242 new messages)" + // This method is used to pass the string that comes after the number. + virtual QString textInfo() const { return QString() ; } + bool notifyEnabled(); bool notifyCombined(); bool notifyBlink(); @@ -82,6 +87,7 @@ private: QAction *mNotifyIcon; unsigned int mNewCount; QString mButtonText; + QString mButtonText2; bool mLastBlinking; }; diff --git a/retroshare-gui/src/gui/connect/ConfCertDialog.cpp b/retroshare-gui/src/gui/connect/ConfCertDialog.cpp index fc56ebef5..a74740712 100644 --- a/retroshare-gui/src/gui/connect/ConfCertDialog.cpp +++ b/retroshare-gui/src/gui/connect/ConfCertDialog.cpp @@ -32,14 +32,16 @@ #include #include +#include + #include "gui/help/browser/helpbrowser.h" #include "gui/common/PeerDefs.h" #include "gui/common/StatusDefs.h" #include "gui/RetroShareLink.h" #include "gui/notifyqt.h" #include "gui/common/AvatarDefs.h" +#include "gui/common/FilesDefs.h" #include "gui/MainWindow.h" -#include "mainpage.h" #include "util/DateTime.h" #include "util/misc.h" @@ -83,7 +85,7 @@ ConfCertDialog::ConfCertDialog(const RsPeerId& id, const RsPgpId &pgp_id, QWidge /* Invoke Qt Designer generated QObject setup routine */ ui.setupUi(this); Settings->loadWidgetInformation(this); - ui.headerFrame->setHeaderImage(QPixmap(":/images/user/identityinfo64.png")); + ui.headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/images/user/identityinfo64.png")); //ui.headerFrame->setHeaderText(tr("Friend node details")); //ui._chat_CB->hide() ; diff --git a/retroshare-gui/src/gui/connect/ConfCertDialog.ui b/retroshare-gui/src/gui/connect/ConfCertDialog.ui index 5862464c7..5c6fdabde 100644 --- a/retroshare-gui/src/gui/connect/ConfCertDialog.ui +++ b/retroshare-gui/src/gui/connect/ConfCertDialog.ui @@ -446,6 +446,27 @@ 1 + + stabWidget + pgpfingerprint + peerid + loc + statusline + crypto_info + lastcontact + version + statusmessage + localAddress + localPort + extAddress + extPort + dynDNS + ipAddressList + userCertificateText + _shouldAddSignatures_CB + _shortFormat_CB + _includeIPHistory_CB + diff --git a/retroshare-gui/src/gui/connect/ConnectFriendWizard.cpp b/retroshare-gui/src/gui/connect/ConnectFriendWizard.cpp index 4ad6315b9..e822499a1 100755 --- a/retroshare-gui/src/gui/connect/ConnectFriendWizard.cpp +++ b/retroshare-gui/src/gui/connect/ConnectFriendWizard.cpp @@ -33,6 +33,7 @@ #include #endif +#include "gui/common/FilesDefs.h" #include "gui/settings/rsharesettings.h" #include "util/misc.h" #include "ConnectFriendWizard.h" @@ -94,10 +95,10 @@ ConnectFriendWizard::ConnectFriendWizard(QWidget *parent) : // setOption(HaveHelpButton, true); // connect(this, SIGNAL(helpRequested()), this, SLOT(showHelp())); - setPixmap(QWizard::LogoPixmap, QPixmap(":/icons/invite64.png")); + setPixmap(QWizard::LogoPixmap, FilesDefs::getPixmapFromQtResourcePath(":/icons/invite64.png")); // we have no good pictures for watermarks -// setPixmap(QWizard::WatermarkPixmap, QPixmap(":/images/connectFriendWatermark.png")); +// setPixmap(QWizard::WatermarkPixmap, FilesDefs::getPixmapFromQtResourcePath(":/images/connectFriendWatermark.png")); /* register global fields */ ui->ErrorMessagePage->registerField("errorMessage", ui->messageLabel, "text"); @@ -145,11 +146,11 @@ ConnectFriendWizard::ConnectFriendWizard(QWidget *parent) : switch (rsFiles->filePermDirectDL()) { case RS_FILE_PERM_DIRECT_DL_YES: - ui->_direct_transfer_CB_2->setIcon(QIcon(":/icons/warning_yellow_128.png")); + ui->_direct_transfer_CB_2->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/warning_yellow_128.png")); ui->_direct_transfer_CB_2->setToolTip(ui->_direct_transfer_CB_2->toolTip().append(tr("\nWarning: In your File-Transfer option, you select allow direct download to Yes."))); break ; case RS_FILE_PERM_DIRECT_DL_NO: - ui->_direct_transfer_CB_2->setIcon(QIcon(":/icons/warning_yellow_128.png")); + ui->_direct_transfer_CB_2->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/warning_yellow_128.png")); ui->_direct_transfer_CB_2->setToolTip(ui->_direct_transfer_CB_2->toolTip().append(tr("\nWarning: In your File-Transfer option, you select allow direct download to No."))); break ; @@ -562,7 +563,7 @@ void ConnectFriendWizard::initializePage(int id) ui->_addIPToWhiteList_ComboBox_2->addItem("(Hidden node)") ; int S = QFontMetricsF(ui->ipEdit->font()).height() ; ui->ipEdit->setToolTip("This is a Hidden node - you need tor/i2p proxy to connect"); - ui->ipLabel->setPixmap(QPixmap(":/images/anonymous_128_blue.png").scaledToHeight(S*2,Qt::SmoothTransformation)); + ui->ipLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/anonymous_128_blue.png").scaledToHeight(S*2,Qt::SmoothTransformation)); ui->ipLabel->setToolTip("This is a Hidden node - you need tor/i2p proxy to connect"); } if(mIsShortInvite) @@ -812,7 +813,7 @@ void ConnectFriendWizard::cleanFriendCert() std::string cert = ui->friendCertEdit->toPlainText().toUtf8().constData(); if (cert.empty()) { - ui->friendCertCleanLabel->setPixmap(QPixmap(":/images/delete.png")); + ui->friendCertCleanLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/delete.png")); ui->friendCertCleanLabel->setToolTip(""); ui->friendCertCleanLabel->setStyleSheet(""); errorMsg = tr(""); @@ -836,7 +837,7 @@ void ConnectFriendWizard::cleanFriendCert() } errorMsg = tr("Valid certificate") + (mIsShortInvite?" (Short format)":" (plain format with profile key)"); - ui->friendCertCleanLabel->setPixmap(QPixmap(":/images/accepted16.png")); + ui->friendCertCleanLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/accepted16.png")); } else { if (error_code > 0) { switch (error_code) { @@ -855,11 +856,11 @@ void ConnectFriendWizard::cleanFriendCert() ui->friendCertCleanLabel->setStyleSheet("QLabel#friendCertCleanLabel {border: 2px solid red; border-radius: 6px;}"); } } - ui->friendCertCleanLabel->setPixmap(QPixmap(":/images/delete.png")); + ui->friendCertCleanLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/delete.png")); } } - ui->friendCertCleanLabel->setPixmap(certValid ? QPixmap(":/images/accepted16.png") : QPixmap(":/images/delete.png")); + ui->friendCertCleanLabel->setPixmap(certValid ? FilesDefs::getPixmapFromQtResourcePath(":/images/accepted16.png") : FilesDefs::getPixmapFromQtResourcePath(":/images/delete.png")); ui->friendCertCleanLabel->setToolTip(errorMsg); ui->friendCertCleanLabel->setText(errorMsg); diff --git a/retroshare-gui/src/gui/connect/ConnectProgressDialog.cpp b/retroshare-gui/src/gui/connect/ConnectProgressDialog.cpp index 3de28c8d2..23bde6d6d 100755 --- a/retroshare-gui/src/gui/connect/ConnectProgressDialog.cpp +++ b/retroshare-gui/src/gui/connect/ConnectProgressDialog.cpp @@ -29,6 +29,7 @@ #include #include "gui/common/StatusDefs.h" +#include "gui/common/FilesDefs.h" /* maintain one static dialog per SSL ID */ @@ -56,7 +57,7 @@ ConnectProgressDialog::ConnectProgressDialog(const RsPeerId& id, QWidget *parent ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose, true); - ui->headerFrame->setHeaderImage(QPixmap(":/images/user/identityinfo64.png")); + ui->headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/images/user/identityinfo64.png")); ui->headerFrame->setHeaderText(tr("Connection Assistant")); connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(stopAndClose())); diff --git a/retroshare-gui/src/gui/connect/PGPKeyDialog.cpp b/retroshare-gui/src/gui/connect/PGPKeyDialog.cpp index d27e779fd..c488870ea 100644 --- a/retroshare-gui/src/gui/connect/PGPKeyDialog.cpp +++ b/retroshare-gui/src/gui/connect/PGPKeyDialog.cpp @@ -18,6 +18,7 @@ * * *******************************************************************************/ +#include "gui/common/FilesDefs.h" #include "PGPKeyDialog.h" #include @@ -32,6 +33,8 @@ #include #include +#include + #include "gui/help/browser/helpbrowser.h" #include "gui/common/PeerDefs.h" #include "gui/common/StatusDefs.h" @@ -39,7 +42,6 @@ #include "gui/notifyqt.h" #include "gui/common/AvatarDefs.h" #include "gui/MainWindow.h" -#include "mainpage.h" #include "util/DateTime.h" #include "util/misc.h" @@ -73,7 +75,7 @@ PGPKeyDialog::PGPKeyDialog(const RsPeerId& id, const RsPgpId &pgp_id, QWidget *p // ui._useOldFormat_CB->setEnabled(false) ; // } - ui.headerFrame->setHeaderImage(QPixmap(":/images/user/identityinfo64.png")); + ui.headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/images/user/identityinfo64.png")); ui.headerFrame->setHeaderText(tr("Retroshare profile")); //ui._chat_CB->hide() ; @@ -156,11 +158,11 @@ void PGPKeyDialog::load() switch (rsFiles->filePermDirectDL()) { case RS_FILE_PERM_DIRECT_DL_YES: - ui._direct_transfer_CB->setIcon(QIcon(":/icons/warning_yellow_128.png")); + ui._direct_transfer_CB->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/warning_yellow_128.png")); ui._direct_transfer_CB->setToolTip(ui._direct_transfer_CB->toolTip().append(tr("\nWarning: In your File-Transfer option, you select allow direct download to Yes."))); break ; case RS_FILE_PERM_DIRECT_DL_NO: - ui._direct_transfer_CB->setIcon(QIcon(":/icons/warning_yellow_128.png")); + ui._direct_transfer_CB->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/warning_yellow_128.png")); ui._direct_transfer_CB->setToolTip(ui._direct_transfer_CB->toolTip().append(tr("\nWarning: In your File-Transfer option, you select allow direct download to No."))); break ; diff --git a/retroshare-gui/src/gui/connect/PGPKeyDialog.ui b/retroshare-gui/src/gui/connect/PGPKeyDialog.ui index cbab657c4..b178da227 100644 --- a/retroshare-gui/src/gui/connect/PGPKeyDialog.ui +++ b/retroshare-gui/src/gui/connect/PGPKeyDialog.ui @@ -13,8 +13,8 @@ Dialog - - + + QFrame::NoFrame @@ -24,7 +24,7 @@ - + 0 @@ -33,18 +33,11 @@ Profile info - - + + - - - - Name : - - - @@ -52,13 +45,6 @@ - - - - Fingerprint : - - - @@ -69,13 +55,17 @@ - - + + - Trust level: + Fingerprint : - - true + + + + + + Name : @@ -140,11 +130,78 @@ + + + + Trust level: + + + true + + + + + + + Keysigning: + + + + + + + + + <!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:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Signing a friend's key is a way to express your trust into this friend, to your other friends. It helps them to decide whether to allow connections from that key based on your own trust. Signing a key is absolutely optional and cannot be undone, so do it wisely.</span></p></body></html> + + + Sign this key + + + + 48 + 48 + + + + Qt::ToolButtonTextUnderIcon + + + + + + + <html><head/><body><p><span style=" font-size:10pt;">Signing a friend's key is a way to express your trust into this friend, to your other friends. It helps them to decide whether to allow connections from that key based on your own trust. Signing a key is absolutely optional and cannot be undone, so do it wisely.</span></p></body></html> + + + Sign PGP key + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + - + @@ -157,7 +214,7 @@ - + Key signatures : @@ -173,7 +230,7 @@ - + 6 @@ -187,43 +244,6 @@ 6 - - - - <!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:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> -<p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Signing a friend's key is a way to express your trust into this friend, to your other friends. It helps them to decide whether to allow connections from that key based on your own trust. Signing a key is absolutely optional and cannot be undone, so do it wisely.</span></p></body></html> - - - Sign this key - - - - :/images/view-certificate-sign-48.png:/images/view-certificate-sign-48.png - - - - 48 - 48 - - - - Qt::ToolButtonTextUnderIcon - - - - - - - <html><head/><body><p><span style=" font-size:10pt;">Signing a friend's key is a way to express your trust into this friend, to your other friends. It helps them to decide whether to allow connections from that key based on your own trust. Signing a key is absolutely optional and cannot be undone, so do it wisely.</span></p></body></html> - - - Sign PGP key - - - @@ -451,7 +471,7 @@ p, li { white-space: pre-wrap; } - + Qt::Horizontal @@ -462,9 +482,6 @@ p, li { white-space: pre-wrap; } - stabWidget - headerFrame - buttonBox diff --git a/retroshare-gui/src/gui/elastic/elnode.cpp b/retroshare-gui/src/gui/elastic/elnode.cpp index 0fef49cba..86bb4e31e 100644 --- a/retroshare-gui/src/gui/elastic/elnode.cpp +++ b/retroshare-gui/src/gui/elastic/elnode.cpp @@ -20,6 +20,7 @@ // This code is inspired from http://doc.qt.io/qt-5/qtwidgets-graphicsview-elasticnodes-node-cpp.html +#include "gui/common/FilesDefs.h" #include #include @@ -366,11 +367,11 @@ void Node::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) QMenu contextMnu ; if(_type == GraphWidget::ELASTIC_NODE_TYPE_FRIEND) - contextMnu.addAction(QIcon(IMAGE_DENIED), QObject::tr( "Deny friend" ), this, SLOT(denyFriend()) ); + contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_DENIED), QObject::tr( "Deny friend" ), this, SLOT(denyFriend()) ); else if(_type != GraphWidget::ELASTIC_NODE_TYPE_OWN) - contextMnu.addAction(QIcon(IMAGE_MAKEFRIEND), QObject::tr( "Make friend" ), this, SLOT(makeFriend()) ); + contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MAKEFRIEND), QObject::tr( "Make friend" ), this, SLOT(makeFriend()) ); - contextMnu.addAction(QIcon(IMAGE_MAKEFRIEND), QObject::tr( "Peer details" ), this, SLOT(peerDetails()) ); + contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MAKEFRIEND), QObject::tr( "Peer details" ), this, SLOT(peerDetails()) ); contextMnu.exec(event->screenPos()); } diff --git a/retroshare-gui/src/gui/elastic/elnode.h b/retroshare-gui/src/gui/elastic/elnode.h index 1e0edd1a8..33043ddab 100644 --- a/retroshare-gui/src/gui/elastic/elnode.h +++ b/retroshare-gui/src/gui/elastic/elnode.h @@ -23,6 +23,10 @@ #ifndef ELNODE_H #define ELNODE_H +#include "graphwidget.h" + +#include + #include #if QT_VERSION >= 0x040600 #include @@ -30,9 +34,7 @@ #include #endif #include - -#include -#include "graphwidget.h" +#include class Edge; QT_BEGIN_NAMESPACE diff --git a/retroshare-gui/src/gui/emojione.qrc b/retroshare-gui/src/gui/emojione.qrc index fdbfc58aa..b636f7b09 100644 --- a/retroshare-gui/src/gui/emojione.qrc +++ b/retroshare-gui/src/gui/emojione.qrc @@ -937,5 +937,7 @@ emojione/1F1FF-1F1FC.png emojione/flags.png emojione/flags2.png + emojione/man-facepalming.png + emojione/woman-facepalming.png diff --git a/retroshare-gui/src/gui/emojione/emotes.acs b/retroshare-gui/src/gui/emojione/emotes.acs index 6e9007948..26af35787 100644 --- a/retroshare-gui/src/gui/emojione/emotes.acs +++ b/retroshare-gui/src/gui/emojione/emotes.acs @@ -113,6 +113,8 @@ "emojione/people2.png"|":man_with_gua_pi_mao:":"emojione/1F472.png"; "emojione/people2.png"|":levitate:|:man_in_business_suit_levitating:":"emojione/1F574.png"; "emojione/people2.png"|":dancer:":"emojione/1F483.png"; +"emojione/people2.png"|":man_facepalming:":"emojione/man-facepalming.png"; +"emojione/people2.png"|":woman_facepalming:":"emojione/woman-facepalming.png"; "emojione/people2.png"|":bust_in_silhouette:":"emojione/1F464.png"; "emojione/people2.png"|":busts_in_silhouette:":"emojione/1F465.png"; "emojione/people2.png"|":family:":"emojione/1F46A.png"; diff --git a/retroshare-gui/src/gui/emojione/man-facepalming.png b/retroshare-gui/src/gui/emojione/man-facepalming.png new file mode 100644 index 000000000..d0594eefd Binary files /dev/null and b/retroshare-gui/src/gui/emojione/man-facepalming.png differ diff --git a/retroshare-gui/src/gui/emojione/woman-facepalming.png b/retroshare-gui/src/gui/emojione/woman-facepalming.png new file mode 100644 index 000000000..10346185e Binary files /dev/null and b/retroshare-gui/src/gui/emojione/woman-facepalming.png differ diff --git a/retroshare-gui/src/gui/feeds/ChatMsgItem.ui b/retroshare-gui/src/gui/feeds/ChatMsgItem.ui index c9188d54b..89d8ec85c 100644 --- a/retroshare-gui/src/gui/feeds/ChatMsgItem.ui +++ b/retroshare-gui/src/gui/feeds/ChatMsgItem.ui @@ -6,7 +6,7 @@ 0 0 - 565 + 643 209 @@ -171,8 +171,8 @@ Write a quick Message - - :/images/message-mail.png:/images/message-mail.png + + :/icons/mail/write-mail.png:/icons/mail/write-mail.png false @@ -203,8 +203,8 @@ Write Message - - :/images/mail_send.png:/images/mail_send.png + + :/icons/mail/write-mail.png:/icons/mail/write-mail.png Qt::ToolButtonTextBesideIcon @@ -235,8 +235,8 @@ Start Chat - - :/images/chat.png:/images/chat.png + + :/icons/png/chats.png:/icons/png/chats.png Qt::ToolButtonTextBesideIcon diff --git a/retroshare-gui/src/gui/feeds/GxsChannelGroupItem.cpp b/retroshare-gui/src/gui/feeds/GxsChannelGroupItem.cpp index af3e575dd..41c61d896 100644 --- a/retroshare-gui/src/gui/feeds/GxsChannelGroupItem.cpp +++ b/retroshare-gui/src/gui/feeds/GxsChannelGroupItem.cpp @@ -23,6 +23,8 @@ #include "ui_GxsChannelGroupItem.h" #include "FeedHolder.h" +#include "util/qtthreadsutils.h" +#include "gui/common/FilesDefs.h" #include "gui/NewsFeed.h" #include "gui/RetroShareLink.h" @@ -89,29 +91,39 @@ bool GxsChannelGroupItem::setGroup(const RsGxsChannelGroup &group) return true; } -void GxsChannelGroupItem::loadGroup(const uint32_t &token) +void GxsChannelGroupItem::loadGroup() { -#ifdef DEBUG_ITEM - std::cerr << "GxsChannelGroupItem::loadGroup()"; - std::cerr << std::endl; -#endif - - std::vector groups; - if (!rsGxsChannels->getGroupData(token, groups)) + RsThread::async([this]() { - std::cerr << "GxsChannelGroupItem::loadGroup() ERROR getting data"; - std::cerr << std::endl; - return; - } + // 1 - get group data - if (groups.size() != 1) - { - std::cerr << "GxsChannelGroupItem::loadGroup() Wrong number of Items"; - std::cerr << std::endl; - return; - } + std::vector groups; + const std::list groupIds = { groupId() }; - setGroup(groups[0]); + if(!rsGxsChannels->getChannelsInfo(groupIds,groups)) + { + RsErr() << "PostedItem::loadGroup() ERROR getting data" << std::endl; + return; + } + + if (groups.size() != 1) + { + std::cerr << "GxsGxsChannelGroupItem::loadGroup() Wrong number of Items"; + std::cerr << std::endl; + return; + } + RsGxsChannelGroup group(groups[0]); + + RsQThreadUtils::postToObject( [group,this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ + + setGroup(group); + + }, this ); + }); } QString GxsChannelGroupItem::groupName() @@ -136,7 +148,7 @@ void GxsChannelGroupItem::fill() if (mGroup.mImage.mData != NULL) { QPixmap chanImage; GxsIdDetails::loadPixmapFromData(mGroup.mImage.mData, mGroup.mImage.mSize, chanImage,GxsIdDetails::ORIGINAL); - ui->logoLabel->setPixmap(QPixmap(chanImage)); + ui->logoLabel->setPixmap(chanImage); } if (IS_GROUP_SUBSCRIBED(mGroup.mMeta.mSubscribeFlags)) { @@ -176,13 +188,13 @@ void GxsChannelGroupItem::doExpand(bool open) if (open) { ui->expandFrame->show(); - ui->expandButton->setIcon(QIcon(QString(":/icons/png/up-arrow.png"))); + ui->expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/png/up-arrow.png"))); ui->expandButton->setToolTip(tr("Hide")); } else { ui->expandFrame->hide(); - ui->expandButton->setIcon(QIcon(QString(":/icons/png/down-arrow.png"))); + ui->expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/png/down-arrow.png"))); ui->expandButton->setToolTip(tr("Expand")); } diff --git a/retroshare-gui/src/gui/feeds/GxsChannelGroupItem.h b/retroshare-gui/src/gui/feeds/GxsChannelGroupItem.h index 266d1c019..cd7f6510b 100644 --- a/retroshare-gui/src/gui/feeds/GxsChannelGroupItem.h +++ b/retroshare-gui/src/gui/feeds/GxsChannelGroupItem.h @@ -48,7 +48,7 @@ protected: /* GxsGroupFeedItem */ virtual QString groupName(); - virtual void loadGroup(const uint32_t &token); + virtual void loadGroup() override; virtual RetroShareLink::enumType getLinkType() { return RetroShareLink::TYPE_CHANNEL; } private slots: diff --git a/retroshare-gui/src/gui/feeds/GxsChannelPostItem.cpp b/retroshare-gui/src/gui/feeds/GxsChannelPostItem.cpp index af2116616..560cbfa10 100644 --- a/retroshare-gui/src/gui/feeds/GxsChannelPostItem.cpp +++ b/retroshare-gui/src/gui/feeds/GxsChannelPostItem.cpp @@ -23,6 +23,7 @@ #include #include "gui/gxs/GxsIdDetails.h" +#include "gui/common/FilesDefs.h" #include "rshare.h" #include "GxsChannelPostItem.h" #include "ui_GxsChannelPostItem.h" @@ -30,6 +31,7 @@ #include "FeedHolder.h" #include "SubFileItem.h" #include "util/misc.h" +#include "util/qtthreadsutils.h" #include "gui/RetroShareLink.h" #include "util/HandleRichText.h" #include "util/DateTime.h" @@ -43,23 +45,13 @@ * #define DEBUG_ITEM 1 ****/ -GxsChannelPostItem::GxsChannelPostItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, bool isHome, bool autoUpdate,const std::set& older_versions) : - GxsFeedItem(feedHolder, feedId, groupId, messageId, isHome, rsGxsChannels, autoUpdate) +GxsChannelPostItem::GxsChannelPostItem(FeedHolder *feedHolder, uint32_t feedId, const RsGroupMetaData& group_meta, const RsGxsMessageId &messageId, bool isHome, bool autoUpdate,const std::set& older_versions) : + GxsFeedItem(feedHolder, feedId, group_meta.mGroupId, messageId, isHome, rsGxsChannels, autoUpdate), + mGroupMeta(group_meta) { mPost.mMeta.mMsgId = messageId; // useful for uniqueIdentifer() before the post is loaded - init(messageId,older_versions) ; -} + mPost.mMeta.mGroupId = mGroupMeta.mGroupId; -GxsChannelPostItem::GxsChannelPostItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsChannelPost& post, bool isHome, bool autoUpdate,const std::set& older_versions) : - GxsFeedItem(feedHolder, feedId, post.mMeta.mGroupId, post.mMeta.mMsgId, isHome, rsGxsChannels, autoUpdate) -{ - mPost.mMeta.mMsgId.clear(); // security - init(post.mMeta.mMsgId,older_versions) ; - mPost = post ; -} - -void GxsChannelPostItem::init(const RsGxsMessageId& messageId,const std::set& older_versions) -{ QVector v; //bool self = false; @@ -70,12 +62,57 @@ void GxsChannelPostItem::init(const RsGxsMessageId& messageId,const std::set& older_versions) : + GxsFeedItem(feedHolder, feedId, groupId, messageId, isHome, rsGxsChannels, autoUpdate) // this one should be in GxsFeedItem +{ + mPost.mMeta.mMsgId = messageId; // useful for uniqueIdentifer() before the post is loaded + + QVector v; + //bool self = false; + + for(std::set::const_iterator it(older_versions.begin());it!=older_versions.end();++it) + v.push_back(*it) ; + + if(older_versions.find(messageId) == older_versions.end()) + v.push_back(messageId); + + setMessageVersions(v) ; + setup(); + + loadGroup(); +} + +// GxsChannelPostItem::GxsChannelPostItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsChannelPost& post, bool isHome, bool autoUpdate,const std::set& older_versions) : +// GxsFeedItem(feedHolder, feedId, post.mMeta.mGroupId, post.mMeta.mMsgId, isHome, rsGxsChannels, autoUpdate) +// { +// mPost.mMeta.mMsgId.clear(); // security +// init(post.mMeta.mMsgId,older_versions) ; +// mPost = post ; +// } + +// void GxsChannelPostItem::init(const RsGxsMessageId& messageId,const std::set& older_versions) +// { +// QVector v; +// //bool self = false; +// +// for(std::set::const_iterator it(older_versions.begin());it!=older_versions.end();++it) +// v.push_back(*it) ; +// +// if(older_versions.find(messageId) == older_versions.end()) +// v.push_back(messageId); +// +// setMessageVersions(v) ; +// +// setup(); +// +// mLoaded = false ; +// } + void GxsChannelPostItem::paintEvent(QPaintEvent *e) { /* This method employs a trick to trigger a deferred loading. The post and group is requested only @@ -85,7 +122,11 @@ void GxsChannelPostItem::paintEvent(QPaintEvent *e) { mLoaded = true ; - requestGroup(); + std::set older_versions; // not so nice. We need to use std::set everywhere + for(auto& m:messageVersions()) + older_versions.insert(m); + + fill(); requestMessage(); requestComment(); } @@ -100,22 +141,42 @@ GxsChannelPostItem::~GxsChannelPostItem() bool GxsChannelPostItem::isUnread() const { - return IS_MSG_UNREAD(mPost.mMeta.mMsgStatus) ; + return IS_MSG_UNREAD(mPost.mMeta.mMsgStatus) ; } void GxsChannelPostItem::setup() { /* Invoke the Qt Designer generated object setup routine */ + ui = new Ui::GxsChannelPostItem; ui->setupUi(this); + // Manually set icons to allow to use clever resource sharing that is missing in Qt for Icons loaded from Qt resource file. + // This is particularly important here because a channel may contain many posts, so duplicating the QImages here is deadly for the + // memory. + + ui->logoLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/thumb-default-video.png")); + //ui->warn_image_label->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/status_unknown.png")); + ui->readButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/message-state-unread.png")); + ui->voteUpButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/vote_up.png")); + ui->voteDownButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/vote_down.png")); + ui->downloadButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/download.png")); + ui->playButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/play.png")); + ui->commentButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/comment.png")); + ui->editButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/pencil-edit-button.png")); + ui->copyLinkButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/copy.png")); + ui->expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/down-arrow.png")); + ui->readAndClearButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/correct.png")); + ui->clearButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/exit2.png")); + setAttribute(Qt::WA_DeleteOnClose, true); mInFill = false; mCloseOnRead = false; + mLoaded = false; /* clear ui */ - ui->titleLabel->setText(tr("Loading")); + ui->titleLabel->setText(tr("Loading...")); ui->datetimelabel->clear(); ui->filelabel->clear(); ui->newCommentLabel->hide(); @@ -150,12 +211,12 @@ void GxsChannelPostItem::setup() ui->downloadButton->hide(); ui->playButton->hide(); - ui->warn_image_label->hide(); - ui->warning_label->hide(); + //ui->warn_image_label->hide(); + //ui->warning_label->hide(); ui->titleLabel->setMinimumWidth(100); //ui->subjectLabel->setMinimumWidth(100); - ui->warning_label->setMinimumWidth(100); + //ui->warning_label->setMinimumWidth(100); ui->mainFrame->setProperty("new", false); ui->mainFrame->style()->unpolish(ui->mainFrame); @@ -164,29 +225,6 @@ void GxsChannelPostItem::setup() ui->expandFrame->hide(); } -bool GxsChannelPostItem::setGroup(const RsGxsChannelGroup &group, bool doFill) -{ - if (groupId() != group.mMeta.mGroupId) { - std::cerr << "GxsChannelPostItem::setGroup() - Wrong id, cannot set post"; - std::cerr << std::endl; - return false; - } - - mGroup = group; - - // If not publisher, hide the edit button. Without the publish key, there's no way to edit a message. -#ifdef DEBUG_ITEM - std::cerr << "Group subscribe flags = " << std::hex << mGroup.mMeta.mSubscribeFlags << std::dec << std::endl ; -#endif - if( !IS_GROUP_PUBLISHER(mGroup.mMeta.mSubscribeFlags) ) - ui->editButton->hide() ; - - if (doFill) { - fill(); - } - - return true; -} bool GxsChannelPostItem::setPost(const RsGxsChannelPost &post, bool doFill) { @@ -221,7 +259,7 @@ QString GxsChannelPostItem::getMsgLabel() QString GxsChannelPostItem::groupName() { - return QString::fromUtf8(mGroup.mMeta.mGroupName.c_str()); + return QString::fromUtf8(mGroupMeta.mGroupName.c_str()); } void GxsChannelPostItem::loadComments() @@ -230,107 +268,155 @@ void GxsChannelPostItem::loadComments() comments(title); } -void GxsChannelPostItem::loadGroup(const uint32_t &token) +void GxsChannelPostItem::loadGroup() { #ifdef DEBUG_ITEM std::cerr << "GxsChannelGroupItem::loadGroup()"; std::cerr << std::endl; #endif - std::vector groups; - if (!rsGxsChannels->getGroupData(token, groups)) + RsThread::async([this]() { - std::cerr << "GxsChannelGroupItem::loadGroup() ERROR getting data"; - std::cerr << std::endl; - return; - } + // 1 - get group data - if (groups.size() != 1) - { - std::cerr << "GxsChannelGroupItem::loadGroup() Wrong number of Items"; - std::cerr << std::endl; - return; - } + std::vector groups; + const std::list groupIds = { groupId() }; - setGroup(groups[0]); + if(!rsGxsChannels->getChannelsInfo(groupIds,groups)) // would be better to call channel Summaries for a single group + { + RsErr() << "GxsGxsChannelGroupItem::loadGroup() ERROR getting data" << std::endl; + return; + } + + if (groups.size() != 1) + { + std::cerr << "GxsGxsChannelGroupItem::loadGroup() Wrong number of Items"; + std::cerr << std::endl; + return; + } + RsGxsChannelGroup group(groups[0]); + + RsQThreadUtils::postToObject( [group,this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ + + mGroupMeta = group.mMeta; + + }, this ); + }); } - -void GxsChannelPostItem::loadMessage(const uint32_t &token) +void GxsChannelPostItem::loadMessage() { #ifdef DEBUG_ITEM std::cerr << "GxsChannelPostItem::loadMessage()"; std::cerr << std::endl; #endif - - std::vector posts; - std::vector cmts; - if (!rsGxsChannels->getPostData(token, posts, cmts)) + RsThread::async([this]() { - std::cerr << "GxsChannelPostItem::loadMessage() ERROR getting data"; - std::cerr << std::endl; - return; - } + // 1 - get group data - if (posts.size() == 1) - { - std::cerr << (void*)this << ": Obtained post, with msgId = " << posts[0].mMeta.mMsgId << std::endl; - setPost(posts[0]); - } - else if (cmts.size() == 1) - { - RsGxsComment cmt = cmts[0]; + std::vector posts; + std::vector comments; + std::vector votes; - std::cerr << (void*)this << ": Obtained comment, setting messageId to threadID = " << cmt.mMeta.mThreadId << std::endl; - ui->newCommentLabel->show(); - ui->commLabel->show(); - ui->commLabel->setText(QString::fromUtf8(cmt.mComment.c_str())); + if(! rsGxsChannels->getChannelContent( groupId(), std::set( { messageId() } ),posts,comments,votes)) + { + RsErr() << "GxsGxsChannelGroupItem::loadGroup() ERROR getting data" << std::endl; + return; + } - //Change this item to be uploaded with thread element. - setMessageId(cmt.mMeta.mThreadId); - requestMessage(); - } - else - { - std::cerr << "GxsChannelPostItem::loadMessage() Wrong number of Items. Remove It."; - std::cerr << std::endl; - removeItem(); - return; - } + if (posts.size() == 1) + { +#ifdef DEBUG_ITEM + std::cerr << (void*)this << ": Obtained post, with msgId = " << posts[0].mMeta.mMsgId << std::endl; +#endif + const RsGxsChannelPost& post(posts[0]); + + RsQThreadUtils::postToObject( [post,this]() { setPost(post); }, this ); + } + else if(comments.size() == 1) + { + const RsGxsComment& cmt = comments[0]; +#ifdef DEBUG_ITEM + std::cerr << (void*)this << ": Obtained comment, setting messageId to threadID = " << cmt.mMeta.mThreadId << std::endl; +#endif + + RsQThreadUtils::postToObject( [cmt,this]() + { + ui->newCommentLabel->show(); + ui->commLabel->show(); + ui->commLabel->setText(QString::fromUtf8(cmt.mComment.c_str())); + + //Change this item to be uploaded with thread element. + setMessageId(cmt.mMeta.mThreadId); + requestMessage(); + + }, this ); + + } + else + { +#ifdef DEBUG_ITEM + std::cerr << "GxsChannelPostItem::loadMessage() Wrong number of Items. Remove It."; + std::cerr << std::endl; +#endif + + RsQThreadUtils::postToObject( [this]() { removeItem(); }, this ); + } + }); } -void GxsChannelPostItem::loadComment(const uint32_t &token) +void GxsChannelPostItem::loadComment() { #ifdef DEBUG_ITEM std::cerr << "GxsChannelPostItem::loadComment()"; std::cerr << std::endl; #endif - std::vector cmts; - if (!rsGxsChannels->getRelatedComments(token, cmts)) + RsThread::async([this]() { - std::cerr << "GxsChannelPostItem::loadComment() ERROR getting data"; - std::cerr << std::endl; - return; - } + // 1 - get group data - size_t comNb = cmts.size(); - QString sComButText = tr("Comment"); - if (comNb == 1) { - sComButText = sComButText.append("(1)"); - } else if (comNb > 1) { - sComButText = tr("Comments ").append("(%1)").arg(comNb); - } - ui->commentButton->setText(sComButText); + std::set msgIds; + + for(auto MsgId: messageVersions()) + msgIds.insert(MsgId); + + std::vector posts; + std::vector comments; + + if(! rsGxsChannels->getChannelComments( groupId(),msgIds,comments)) + { + RsErr() << "GxsGxsChannelGroupItem::loadGroup() ERROR getting data" << std::endl; + return; + } + + int comNb = comments.size(); + + RsQThreadUtils::postToObject( [comNb,this]() + { + QString sComButText = tr("Comment"); + if (comNb == 1) + sComButText = sComButText.append("(1)"); + else if(comNb > 1) + sComButText = tr("Comments ").append("(%1)").arg(comNb); + + ui->commentButton->setText(sComButText); + + }, this ); + }); } void GxsChannelPostItem::fill() { /* fill in */ - if (isLoading()) { - /* Wait for all requests */ - return; - } +// if (isLoading()) { + // /* Wait for all requests */ + //return; +// } #ifdef DEBUG_ITEM std::cerr << "GxsChannelPostItem::fill()"; @@ -341,7 +427,7 @@ void GxsChannelPostItem::fill() QString title; - float f = QFontMetricsF(font()).height()/14.0 ; + //float f = QFontMetricsF(font()).height()/14.0 ; if(mPost.mThumbnail.mData != NULL) { @@ -365,6 +451,9 @@ void GxsChannelPostItem::fill() ui->logoLabel->setPixmap(thumbnail); } + if( !IS_GROUP_PUBLISHER(mGroupMeta.mSubscribeFlags) ) + ui->editButton->hide() ; + if (!mIsHome) { if (mCloseOnRead && !IS_MSG_NEW(mPost.mMeta.mMsgStatus)) { @@ -377,9 +466,9 @@ void GxsChannelPostItem::fill() ui->titleLabel->setText(title); RetroShareLink msgLink = RetroShareLink::createGxsMessageLink(RetroShareLink::TYPE_CHANNEL, mPost.mMeta.mGroupId, mPost.mMeta.mMsgId, messageName()); - //ui->subjectLabel->setText(msgLink.toHtml()); + ui->subjectLabel->setText(msgLink.toHtml()); - if (IS_GROUP_SUBSCRIBED(mGroup.mMeta.mSubscribeFlags) || IS_GROUP_ADMIN(mGroup.mMeta.mSubscribeFlags)) + if (IS_GROUP_SUBSCRIBED(mGroupMeta.mSubscribeFlags) || IS_GROUP_ADMIN(mGroupMeta.mSubscribeFlags)) { ui->unsubscribeButton->setEnabled(true); } @@ -400,12 +489,12 @@ void GxsChannelPostItem::fill() /* subject */ ui->titleLabel->setText(QString::fromUtf8(mPost.mMeta.mMsgName.c_str())); - //uint32_t autorized_lines = (int)floor((ui->logoLabel->height() - ui->titleLabel->height() - ui->buttonHLayout->sizeHint().height())/QFontMetricsF(ui->subjectLabel->font()).height()); + uint32_t autorized_lines = (int)floor((ui->logoLabel->height() - ui->titleLabel->height() - ui->buttonHLayout->sizeHint().height())/QFontMetricsF(ui->subjectLabel->font()).height()); // fill first 4 lines of message. (csoler) Disabled the replacement of smileys and links, because the cost is too crazy //ui->subjectLabel->setText(RsHtml().formatText(NULL, RsStringUtil::CopyLines(QString::fromUtf8(mPost.mMsg.c_str()), autorized_lines), RSHTML_FORMATTEXT_EMBED_SMILEYS | RSHTML_FORMATTEXT_EMBED_LINKS)); - //ui->subjectLabel->setText(RsStringUtil::CopyLines(QString::fromUtf8(mPost.mMsg.c_str()), 2)) ; + ui->subjectLabel->setText(RsStringUtil::CopyLines(QString::fromUtf8(mPost.mMsg.c_str()), 2)) ; //QString score = QString::number(post.mTopScore); // scoreLabel->setText(score); @@ -418,7 +507,7 @@ void GxsChannelPostItem::fill() ui->unsubscribeButton->hide(); ui->copyLinkButton->show(); - if (IS_GROUP_SUBSCRIBED(mGroup.mMeta.mSubscribeFlags) || IS_GROUP_ADMIN(mGroup.mMeta.mSubscribeFlags)) + if (IS_GROUP_SUBSCRIBED(mGroupMeta.mSubscribeFlags) || IS_GROUP_ADMIN(mGroupMeta.mSubscribeFlags)) { ui->readButton->setVisible(true); @@ -540,15 +629,22 @@ QString GxsChannelPostItem::messageName() void GxsChannelPostItem::setReadStatus(bool isNew, bool isUnread) { + if (isNew) + mPost.mMeta.mMsgStatus |= GXS_SERV::GXS_MSG_STATUS_GUI_NEW; + else + mPost.mMeta.mMsgStatus &= ~GXS_SERV::GXS_MSG_STATUS_GUI_NEW; + if (isUnread) { - ui->readButton->setChecked(true); - ui->readButton->setIcon(QIcon(":/images/message-state-unread.png")); + mPost.mMeta.mMsgStatus |= GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD; + whileBlocking(ui->readButton)->setChecked(true); + ui->readButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/message-state-unread.png")); } else { - ui->readButton->setChecked(false); - ui->readButton->setIcon(QIcon(":/images/message-state-read.png")); + mPost.mMeta.mMsgStatus &= ~GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD; + whileBlocking(ui->readButton)->setChecked(false); + ui->readButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/message-state-read.png")); } ui->newLabel->setVisible(isNew); @@ -558,21 +654,21 @@ void GxsChannelPostItem::setReadStatus(bool isNew, bool isUnread) ui->mainFrame->style()->polish( ui->mainFrame); } -void GxsChannelPostItem::setFileCleanUpWarning(uint32_t time_left) -{ - int hours = (int)time_left/3600; - int minutes = (time_left - hours*3600)%60; - - ui->warning_label->setText(tr("Warning! You have less than %1 hours and %2 minute before this file is deleted Consider saving it.").arg( - QString::number(hours)).arg(QString::number(minutes))); - - QFont warnFont = ui->warning_label->font(); - warnFont.setBold(true); - ui->warning_label->setFont(warnFont); - - ui->warn_image_label->setVisible(true); - ui->warning_label->setVisible(true); -} +// void GxsChannelPostItem::setFileCleanUpWarning(uint32_t time_left) +// { +// int hours = (int)time_left/3600; +// int minutes = (time_left - hours*3600)%60; +// +// ui->warning_label->setText(tr("Warning! You have less than %1 hours and %2 minute before this file is deleted Consider saving it.").arg( +// QString::number(hours)).arg(QString::number(minutes))); +// +// QFont warnFont = ui->warning_label->font(); +// warnFont.setBold(true); +// ui->warning_label->setFont(warnFont); +// +// ui->warn_image_label->setVisible(true); +// ui->warning_label->setVisible(true); +// } void GxsChannelPostItem::updateItem() { @@ -661,7 +757,7 @@ void GxsChannelPostItem::doExpand(bool open) if (open) { ui->expandFrame->show(); - ui->expandButton->setIcon(QIcon(QString(":/icons/png/up-arrow.png"))); + ui->expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/png/up-arrow.png"))); ui->expandButton->setToolTip(tr("Hide")); readToggled(false); @@ -669,7 +765,7 @@ void GxsChannelPostItem::doExpand(bool open) else { ui->expandFrame->hide(); - ui->expandButton->setIcon(QIcon(QString(":/icons/png/down-arrow.png"))); + ui->expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/png/down-arrow.png"))); ui->expandButton->setToolTip(tr("Expand")); } @@ -703,7 +799,6 @@ void GxsChannelPostItem::readAndClearItem() std::cerr << "GxsChannelPostItem::readAndClearItem()"; std::cerr << std::endl; #endif - readToggled(false); removeItem(); } @@ -731,7 +826,7 @@ void GxsChannelPostItem::download() void GxsChannelPostItem::edit() { - CreateGxsChannelMsg *msgDialog = new CreateGxsChannelMsg(mGroup.mMeta.mGroupId,mPost.mMeta.mMsgId); + CreateGxsChannelMsg *msgDialog = new CreateGxsChannelMsg(mGroupMeta.mGroupId,mPost.mMeta.mMsgId); msgDialog->show(); } @@ -747,7 +842,7 @@ void GxsChannelPostItem::play() } } -void GxsChannelPostItem::readToggled(bool checked) +void GxsChannelPostItem::readToggled(bool /*checked*/) { if (mInFill) { return; @@ -757,10 +852,9 @@ void GxsChannelPostItem::readToggled(bool checked) RsGxsGrpMsgIdPair msgPair = std::make_pair(groupId(), messageId()); - uint32_t token; - rsGxsChannels->setMessageReadStatus(token, msgPair, !checked); + rsGxsChannels->markRead(msgPair, isUnread()); - setReadStatus(false, checked); + //setReadStatus(false, checked); // Updated by events } void GxsChannelPostItem::makeDownVote() @@ -786,3 +880,5 @@ void GxsChannelPostItem::makeUpVote() emit vote(msgId, true); } + + diff --git a/retroshare-gui/src/gui/feeds/GxsChannelPostItem.h b/retroshare-gui/src/gui/feeds/GxsChannelPostItem.h index a067098d9..8370b885e 100644 --- a/retroshare-gui/src/gui/feeds/GxsChannelPostItem.h +++ b/retroshare-gui/src/gui/feeds/GxsChannelPostItem.h @@ -42,33 +42,35 @@ public: // It can be used for all apparences of channel posts. But in rder to merge comments from the previous versions of the post, the list of // previous posts should be supplied. It's optional. If not supplied only the comments of the new version will be displayed. - GxsChannelPostItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, bool isHome, bool autoUpdate, const std::set& older_versions = std::set()); + GxsChannelPostItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsGroupId& groupId, const RsGxsMessageId &messageId, bool isHome, bool autoUpdate, const std::set& older_versions = std::set()); - // This method can be called when additional information is known about the post. In this case, the widget will be initialized with some - // minimap information from the post and completed when the use displays it, which shouldn't cost anything more. + // This one is used in channel thread widget. We don't want the group data to reload at every post, so we load it in the hosting + // GxsChannelsPostsWidget and pass it to created items. - GxsChannelPostItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsChannelPost& post, bool isHome, bool autoUpdate, const std::set& older_versions = std::set()); + GxsChannelPostItem(FeedHolder *feedHolder, uint32_t feedId, const RsGroupMetaData& group, const RsGxsMessageId &messageId, bool isHome, bool autoUpdate, const std::set& older_versions = std::set()); - //GxsChannelPostItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsChannelGroup &group, const RsGxsChannelPost &post, bool isHome, bool autoUpdate); - //GxsChannelPostItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsChannelPost &post, bool isHome, bool autoUpdate); virtual ~GxsChannelPostItem(); uint64_t uniqueIdentifier() const override { return hash_64bits("GxsChannelPostItem " + messageId().toStdString()) ; } - bool setGroup(const RsGxsChannelGroup &group, bool doFill = true); - bool setPost(const RsGxsChannelPost &post, bool doFill = true); + bool setGroup(const RsGxsChannelGroup& group, bool doFill = true); + bool setPost(const RsGxsChannelPost& post, bool doFill = true); - void setFileCleanUpWarning(uint32_t time_left); + //void setFileCleanUpWarning(uint32_t time_left); QString getTitleLabel(); QString getMsgLabel(); const std::list &getFileItems() {return mFileItems; } - bool isUnread() const ; + bool isLoaded() const {return mLoaded;}; + bool isUnread() const ; + void setReadStatus(bool isNew, bool isUnread); - static uint64_t computeIdentifier(const RsGxsMessageId& msgid) { return hash64("GxsChannelPostItem " + msgid.toStdString()) ; } + const std::set& olderVersions() const { return mPost.mOlderVersions; } + + static uint64_t computeIdentifier(const RsGxsMessageId& msgid) { return hash64("GxsChannelPostItem " + msgid.toStdString()) ; } protected: - void init(const RsGxsMessageId& messageId,const std::set& older_versions); + //void init(const RsGxsMessageId& messageId,const std::set& older_versions); /* FeedItem */ virtual void doExpand(bool open); @@ -77,17 +79,17 @@ protected: // This does nothing except triggering the loading of the post data and comments. This function is mainly used to detect // when the post is actually made visible. - virtual void paintEvent(QPaintEvent *); + virtual void paintEvent(QPaintEvent *) override; /* GxsGroupFeedItem */ virtual QString groupName(); - virtual void loadGroup(const uint32_t &token); + virtual void loadGroup() override; virtual RetroShareLink::enumType getLinkType() { return RetroShareLink::TYPE_CHANNEL; } /* GxsFeedItem */ virtual QString messageName(); - virtual void loadMessage(const uint32_t &token); - virtual void loadComment(const uint32_t &token); + virtual void loadMessage(); + virtual void loadComment(); private slots: /* default stuff */ @@ -113,14 +115,13 @@ private: void setup(); void fill(); void fillExpandFrame(); - void setReadStatus(bool isNew, bool isUnread); private: bool mInFill; bool mCloseOnRead; bool mLoaded; - RsGxsChannelGroup mGroup; + RsGroupMetaData mGroupMeta; RsGxsChannelPost mPost; std::list mFileItems; diff --git a/retroshare-gui/src/gui/feeds/GxsChannelPostItem.ui b/retroshare-gui/src/gui/feeds/GxsChannelPostItem.ui index 0e15c0a4d..2188df912 100644 --- a/retroshare-gui/src/gui/feeds/GxsChannelPostItem.ui +++ b/retroshare-gui/src/gui/feeds/GxsChannelPostItem.ui @@ -6,8 +6,8 @@ 0 0 - 1433 - 541 + 1092 + 231 @@ -62,9 +62,6 @@ - - :/images/thumb-default-video.png - false @@ -80,7 +77,7 @@ - + 0 @@ -128,34 +125,27 @@ - + + + TextLabel + + + true + + - - - - - - 24 - 16777215 - - - - - - - :/images/status_unknown.png - - - - - - - TextLabel - - - - + + + Qt::Vertical + + + + 20 + 40 + + + @@ -200,10 +190,6 @@ Toggle Message Read Status - - - :/images/message-state-unread.png:/images/message-state-unread.png - true @@ -236,10 +222,6 @@ - - - :/images/vote_up.png:/images/vote_up.png - @@ -250,10 +232,6 @@ - - - :/images/vote_down.png:/images/vote_down.png - @@ -264,10 +242,6 @@ Download - - - :/icons/png/download.png:/icons/png/download.png - false @@ -281,10 +255,6 @@ Play - - - :/icons/png/play.png:/icons/png/play.png - @@ -292,10 +262,6 @@ Comments - - - :/icons/png/comment.png:/icons/png/comment.png - @@ -303,10 +269,6 @@ Edit - - - :/icons/png/pencil-edit-button.png:/icons/png/pencil-edit-button.png - @@ -323,10 +285,6 @@ Copy RetroShare Link - - - :/icons/png/copy.png:/icons/png/copy.png - @@ -385,10 +343,6 @@ Expand - - - :/icons/png/down-arrow.png:/icons/png/down-arrow.png - @@ -405,10 +359,6 @@ Set as read and remove item - - - :/icons/png/correct.png:/icons/png/correct.png - @@ -425,10 +375,6 @@ Remove Item - - - :/icons/png/exit2.png:/icons/png/exit2.png - @@ -524,9 +470,9 @@ - StyledLabel + StyledElidedLabel QLabel -
gui/common/StyledLabel.h
+
gui/common/StyledElidedLabel.h
ElidedLabel @@ -535,9 +481,6 @@ 1
- - - - + diff --git a/retroshare-gui/src/gui/feeds/GxsCircleItem.cpp b/retroshare-gui/src/gui/feeds/GxsCircleItem.cpp index 7673c6d19..18997a084 100644 --- a/retroshare-gui/src/gui/feeds/GxsCircleItem.cpp +++ b/retroshare-gui/src/gui/feeds/GxsCircleItem.cpp @@ -45,7 +45,6 @@ GxsCircleItem::GxsCircleItem(FeedHolder *feedHolder, uint32_t feedId, const RsGx setup(); } - GxsCircleItem::~GxsCircleItem() { delete(ui); @@ -76,83 +75,103 @@ void GxsCircleItem::setup() if(idDetails.mAvatar.mSize == 0 || !GxsIdDetails::loadPixmapFromData(idDetails.mAvatar.mData, idDetails.mAvatar.mSize, pixmap,GxsIdDetails::SMALL)) pixmap = GxsIdDetails::makeDefaultIcon(mGxsId,GxsIdDetails::SMALL); - /* update circle information */ + ui->membershipButton->setToolTip(tr("Grant membership request")); + ui->inviteeButton->setToolTip(tr("Revoke membership")); + + connect(ui->membershipButton, SIGNAL(clicked()), this, SLOT(toggleCircleMembership())); + connect(ui->inviteeButton, SIGNAL(clicked()), this, SLOT(toggleCircleInvite())); + RsGxsCircleDetails circleDetails; + if (rsGxsCircles->getCircleDetails(mCircleId, circleDetails)) { + ui->nameLabel->setText(QString::fromUtf8(circleDetails.mCircleName.c_str()) + " (ID: " + QString::fromStdString(circleDetails.mCircleId.toStdString()) + ")"); + + // from here we can figure out if we already have requested membership or not if (mType == RS_FEED_ITEM_CIRCLE_MEMB_REQ) { - ui->titleLabel->setText(tr("You received a membership request for circle:")); - ui->nameLabel->setText(QString::fromUtf8(circleDetails.mCircleName.c_str())); - ui->gxsIdLabel->setText(idName); + ui->titleLabel->setText(tr("You received a membership request a circle you're administrating:")); ui->iconLabel->setPixmap(pixmap); ui->gxsIdLabel->setId(mGxsId); - if(circleDetails.mAmIAdmin) - { - ui->acceptButton->setToolTip(tr("Grant membership request")); - ui->revokeButton->setToolTip(tr("Revoke membership request")); - connect(ui->acceptButton, SIGNAL(clicked()), this, SLOT(grantCircleMembership())); - connect(ui->revokeButton, SIGNAL(clicked()), this, SLOT(revokeCircleMembership())); - } - else - { - ui->acceptButton->setEnabled(false); - ui->revokeButton->setEnabled(false); - } + ui->inviteeButton->setHidden(false); + ui->inviteeButton->setText(tr("Grant membership")); + ui->inviteeButton->setToolTip(tr("Grant membership to this circle, for this identity")); + + ui->membershipButton->setHidden(true); } - else if (mType == RS_FEED_ITEM_CIRCLE_INVIT_REC) + else if (mType == RS_FEED_ITEM_CIRCLE_INVITE_REC) { - ui->titleLabel->setText(tr("You received an invitation for circle:")); - ui->nameLabel->setText(QString::fromUtf8(circleDetails.mCircleName.c_str())); - ui->gxsIdLabel->setText(idName); + ui->titleLabel->setText(tr("You received an invitation to join this circle:")); ui->iconLabel->setPixmap(pixmap); ui->gxsIdLabel->setId(mGxsId); - ui->acceptButton->setToolTip(tr("Accept invitation")); - connect(ui->acceptButton, SIGNAL(clicked()), this, SLOT(acceptCircleSubscription())); - ui->revokeButton->setHidden(true); + ui->membershipButton->setText(tr("Accept")); + ui->membershipButton->setToolTip(tr("Accept invitation")); + ui->membershipButton->setHidden(false); + + connect(ui->membershipButton, SIGNAL(clicked()), this, SLOT(requestCircleSubscription())); + ui->inviteeButton->setHidden(true); } else if (mType == RS_FEED_ITEM_CIRCLE_MEMB_LEAVE) { - ui->titleLabel->setText(idName + tr(" has left this circle you belong to.")); - ui->nameLabel->setText(QString::fromUtf8(circleDetails.mCircleName.c_str())); - ui->gxsIdLabel->setText(idName); + ui->titleLabel->setText(idName + tr(" has left this circle.")); ui->iconLabel->setPixmap(pixmap); ui->gxsIdLabel->setId(mGxsId); - ui->acceptButton->setHidden(true); - ui->revokeButton->setHidden(true); + ui->membershipButton->setHidden(true); + ui->inviteeButton->setHidden(true); } else if (mType == RS_FEED_ITEM_CIRCLE_MEMB_JOIN) { - ui->titleLabel->setText(idName + tr(" has join this circle you also belong to.")); - ui->nameLabel->setText(QString::fromUtf8(circleDetails.mCircleName.c_str())); - ui->gxsIdLabel->setText(idName); - ui->iconLabel->setPixmap(pixmap); - ui->gxsIdLabel->setId(mGxsId); - - ui->acceptButton->setHidden(true); - ui->revokeButton->setHidden(true); - } - else if (mType == RS_FEED_ITEM_CIRCLE_MEMB_REVOQUED) - { - if(rsIdentity->isOwnId(mGxsId)) - ui->titleLabel->setText(tr("Your identity %1 has been revoqued from this circle.").arg(idName)); + if(circleDetails.mAmIAdmin) + { + ui->titleLabel->setText(idName + tr(" which you invited, has joined this circle you're administrating.")); + ui->inviteeButton->setHidden(false); + ui->inviteeButton->setText(tr("Revoke membership")); + ui->inviteeButton->setToolTip(tr("Revoke membership for that identity")); + } else - ui->titleLabel->setText(tr("Identity %1 has been revoqued from this circle you belong to.").arg(idName)); + { + ui->inviteeButton->setHidden(true); + ui->titleLabel->setText(idName + tr(" has joined this circle.")); + } - ui->nameLabel->setText(QString::fromUtf8(circleDetails.mCircleName.c_str())); - ui->gxsIdLabel->setText(idName); ui->iconLabel->setPixmap(pixmap); ui->gxsIdLabel->setId(mGxsId); - ui->acceptButton->setHidden(true); - ui->revokeButton->setHidden(true); + ui->membershipButton->setHidden(true); } + else if (mType == RS_FEED_ITEM_CIRCLE_MEMB_REVOKED) + { + ui->titleLabel->setText(tr("Your identity %1 has been revoked from this circle.").arg(idName)); + + ui->iconLabel->setPixmap(pixmap); + ui->gxsIdLabel->setId(mGxsId); + + ui->membershipButton->setHidden(false); + ui->membershipButton->setText(tr("Cancel membership request")); + ui->membershipButton->setToolTip(tr("Cancel your membership request from that circle")); + + ui->inviteeButton->setHidden(true); + } + else if (mType == RS_FEED_ITEM_CIRCLE_MEMB_ACCEPTED) + { + ui->titleLabel->setText(tr("Your identity %1 as been accepted in this circle.").arg(idName)); + + ui->iconLabel->setPixmap(pixmap); + ui->gxsIdLabel->setId(mGxsId); + + ui->membershipButton->setHidden(false); + ui->membershipButton->setText(tr("Cancel membership")); + ui->membershipButton->setToolTip(tr("Cancel your membership from that circle")); + + ui->inviteeButton->setHidden(true); + } + } else { @@ -161,46 +180,13 @@ void GxsCircleItem::setup() ui->gxsIdLabel->setText(idName); ui->gxsIdLabel->setId(mGxsId); } - - /* Setup TokenQueue */ - mCircleQueue = new TokenQueue(rsGxsCircles->getTokenService(), this); - } uint64_t GxsCircleItem::uniqueIdentifier() const { - return hash_64bits("GxsCircle " + mCircleId.toStdString() + " " + mGxsId.toStdString() + " " + QString::number(mType).toStdString()); + return hash_64bits("GxsCircle " + mCircleId.toStdString() + " " + mGxsId.toStdString()); } -void GxsCircleItem::loadRequest(const TokenQueue * queue, const TokenRequest &req) -{ -#ifdef ID_DEBUG - std::cerr << "GxsCircleItem::loadRequest() UserType: " << req.mUserType; - std::cerr << std::endl; -#endif - if(queue == mCircleQueue) - { -#ifdef ID_DEBUG - std::cerr << "CirclesDialog::loadRequest() UserType: " << req.mUserType; - std::cerr << std::endl; -#endif - - /* now switch on req */ - switch(req.mUserType) - { - case CIRCLESDIALOG_GROUPUPDATE: - updateCircleGroup(req.mToken); - break; - - default: - std::cerr << "GxsCircleItem::loadRequest() ERROR: INVALID TYPE"; - std::cerr << std::endl; - break; - } - } -} - - /*********** SPECIFIC FUNCTIONS ***********************/ void GxsCircleItem::showCircleDetails() @@ -211,100 +197,40 @@ void GxsCircleItem::showCircleDetails() dlg.exec(); } -void GxsCircleItem::acceptCircleSubscription() +void GxsCircleItem::requestCircleSubscription() { - if (rsGxsCircles->requestCircleMembership(mGxsId, mCircleId)) - removeItem(); + rsGxsCircles->requestCircleMembership(mGxsId, mCircleId); } -void GxsCircleItem::grantCircleMembership() +void GxsCircleItem::toggleCircleMembership() { + if(!rsIdentity->isOwnId(mGxsId)) + { + RsErr() << __PRETTY_FUNCTION__ << ": inconsistent call: identity " << mGxsId << " doesn't belong to you" << std::endl; + return; + } - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; - - std::list grps ; - grps.push_back(RsGxsGroupId(mCircleId)); - - uint32_t token; - mCircleQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, grps, CIRCLESDIALOG_GROUPUPDATE); - - CircleUpdateOrder c ; - c.token = token ; - c.gxs_id = mGxsId ; - c.action = CircleUpdateOrder::GRANT_MEMBERSHIP ; - - mCircleUpdates[token] = c ; -} - -void GxsCircleItem::revokeCircleMembership() -{ - - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; - - std::list grps; - grps.push_back(RsGxsGroupId(mCircleId)); - - uint32_t token; - mCircleQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, grps, CIRCLESDIALOG_GROUPUPDATE); - - CircleUpdateOrder c; - c.token = token; - c.gxs_id = mGxsId; - c.action = CircleUpdateOrder::REVOKE_MEMBERSHIP; - - mCircleUpdates[token] = c; -} - -void GxsCircleItem::updateCircleGroup(const uint32_t& token) -{ -#ifdef ID_DEBUG - std::cerr << "Loading circle info" << std::endl; -#endif - - std::vector circle_grp_v ; - rsGxsCircles->getGroupData(token, circle_grp_v); - - if (circle_grp_v.empty()) - { - std::cerr << "(EE) unexpected empty result from getGroupData. Cannot process circle now!" << std::endl; - return ; - } - - if (circle_grp_v.size() != 1) - { - std::cerr << "(EE) very weird result from getGroupData. Should get exactly one circle" << std::endl; - return ; - } - - RsGxsCircleGroup cg = circle_grp_v.front(); - - /* now mark all the members */ - - //std::set members = cg.mInvitedMembers; - - std::map::iterator it = mCircleUpdates.find(token) ; - - if(it == mCircleUpdates.end()) - { - std::cerr << "(EE) Cannot find token " << token << " to perform group update!" << std::endl; - return ; - } - - if(it->second.action == CircleUpdateOrder::GRANT_MEMBERSHIP) - cg.mInvitedMembers.insert(it->second.gxs_id) ; - else if(it->second.action == CircleUpdateOrder::REVOKE_MEMBERSHIP) - cg.mInvitedMembers.erase(it->second.gxs_id) ; + if(mType == RS_FEED_ITEM_CIRCLE_INVITE_REC) + rsGxsCircles->requestCircleMembership(mGxsId,mCircleId); + else if(mType == RS_FEED_ITEM_CIRCLE_MEMB_REVOKED) + rsGxsCircles->cancelCircleMembership(mGxsId,mCircleId); else - { - std::cerr << "(EE) unrecognised membership action to perform: " << it->second.action << "!" << std::endl; - return ; - } - - uint32_t token2 ; - rsGxsCircles->updateGroup(token2,cg) ; - - mCircleUpdates.erase(it) ; + RsErr() << __PRETTY_FUNCTION__ << ": inconsistent call. mType is " << mType << std::endl; +} + +void GxsCircleItem::toggleCircleInvite() +{ + if(mType == RS_FEED_ITEM_CIRCLE_MEMB_JOIN) + RsThread::async([this]() + { + rsGxsCircles->revokeIdsFromCircle(std::set( { mGxsId } ),mCircleId); + }); + else if(mType == RS_FEED_ITEM_CIRCLE_MEMB_REQ) + RsThread::async([this]() + { + rsGxsCircles->inviteIdsToCircle(std::set( { mGxsId } ),mCircleId); + }); + else + RsErr() << __PRETTY_FUNCTION__ << ": inconsistent call. mType is " << mType << std::endl; } diff --git a/retroshare-gui/src/gui/feeds/GxsCircleItem.h b/retroshare-gui/src/gui/feeds/GxsCircleItem.h index 8c611c06b..857c898f9 100644 --- a/retroshare-gui/src/gui/feeds/GxsCircleItem.h +++ b/retroshare-gui/src/gui/feeds/GxsCircleItem.h @@ -42,7 +42,7 @@ struct CircleUpdateOrder }; -class GxsCircleItem : public FeedItem, public TokenResponse +class GxsCircleItem : public FeedItem { Q_OBJECT @@ -53,7 +53,6 @@ public: virtual ~GxsCircleItem(); uint64_t uniqueIdentifier() const override; - void loadRequest(const TokenQueue *queue, const TokenRequest &req); protected: @@ -66,9 +65,9 @@ protected: private slots: void showCircleDetails(); - void acceptCircleSubscription(); - void grantCircleMembership() ; - void revokeCircleMembership(); + void requestCircleSubscription(); + void toggleCircleMembership() ; + void toggleCircleInvite(); private: void setup(); @@ -78,10 +77,6 @@ private: RsGxsCircleId mCircleId; RsGxsId mGxsId; - TokenQueue *mCircleQueue; - std::map mCircleUpdates ; - - /** Qt Designer generated object */ Ui::GxsCircleItem *ui; }; diff --git a/retroshare-gui/src/gui/feeds/GxsCircleItem.ui b/retroshare-gui/src/gui/feeds/GxsCircleItem.ui index 408781e9b..fa8d30014 100644 --- a/retroshare-gui/src/gui/feeds/GxsCircleItem.ui +++ b/retroshare-gui/src/gui/feeds/GxsCircleItem.ui @@ -7,7 +7,7 @@ 0 0 618 - 104 + 217 @@ -104,8 +104,8 @@ QFrame::Sunken - - + + @@ -130,154 +130,8 @@ - - - - - - for identity - - - - - - - - 20 - 20 - - - - TextLabel - - - true - - - - - - - name - - - true - - - - - - - Qt::Horizontal - - - - 358 - 20 - - - - - - - - - - - 0 - 0 - - - - Qt::NoFocus - - - Accept - - - - :/images/accepted16.png:/images/accepted16.png - - - - - - - - 0 - 0 - - - - Qt::NoFocus - - - Revoke - - - - :/images/cancel.png:/images/cancel.png - - - - - - - - 0 - 0 - - - - Qt::NoFocus - - - Details - - - - :/images/informations_24x24.png:/images/informations_24x24.png - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 285 - 18 - - - - - - - - - 0 - 0 - - - - Qt::NoFocus - - - Remove Item - - - - :/icons/png/exit2.png:/icons/png/exit2.png - - - - - + + @@ -294,32 +148,193 @@
- Circle + Circle msg - - - name - - - true - - + + + + + Circle name: + + + + + + + name + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + - - - Qt::Horizontal - - - - 40 - 20 - - - + + + + + Identity: + + + + + + + + 20 + 20 + + + + TextLabel + + + true + + + + + + + name + + + true + + + + + + + Qt::Horizontal + + + + 358 + 20 + + + + + + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + Accept + + + + :/images/accepted16.png:/images/accepted16.png + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + Revoke + + + + :/images/cancel.png:/images/cancel.png + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + Details + + + + :/images/informations_24x24.png:/images/informations_24x24.png + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 285 + 18 + + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + Remove Item + + + + :/icons/png/exit2.png:/icons/png/exit2.png + + + + @@ -336,8 +351,8 @@ - + diff --git a/retroshare-gui/src/gui/feeds/GxsForumGroupItem.cpp b/retroshare-gui/src/gui/feeds/GxsForumGroupItem.cpp index 8b97d73cf..1d61bb98b 100644 --- a/retroshare-gui/src/gui/feeds/GxsForumGroupItem.cpp +++ b/retroshare-gui/src/gui/feeds/GxsForumGroupItem.cpp @@ -20,9 +20,12 @@ #include "GxsForumGroupItem.h" #include "ui_GxsForumGroupItem.h" +#include "gui/NewsFeed.h" +#include "gui/common/FilesDefs.h" #include "FeedHolder.h" #include "gui/RetroShareLink.h" +#include "util/qtthreadsutils.h" /**** * #define DEBUG_ITEM 1 @@ -36,6 +39,16 @@ GxsForumGroupItem::GxsForumGroupItem(FeedHolder *feedHolder, uint32_t feedId, co requestGroup(); } +GxsForumGroupItem::GxsForumGroupItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsGroupId &groupId, const std::list& added_moderators,const std::list& removed_moderators,bool isHome, bool autoUpdate): + GxsGroupFeedItem(feedHolder, feedId, groupId, isHome, rsGxsForums, autoUpdate), + mAddedModerators(added_moderators), + mRemovedModerators(removed_moderators) +{ + setup(); + + requestGroup(); +} + GxsForumGroupItem::GxsForumGroupItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsForumGroup &group, bool isHome, bool autoUpdate) : GxsGroupFeedItem(feedHolder, feedId, group.mMeta.mGroupId, isHome, rsGxsForums, autoUpdate) { @@ -58,7 +71,7 @@ void GxsForumGroupItem::setup() setAttribute(Qt::WA_DeleteOnClose, true); /* clear ui */ - ui->nameLabel->setText(tr("Loading")); + ui->nameLabel->setText(tr("Loading...")); ui->titleLabel->clear(); ui->descLabel->clear(); @@ -87,29 +100,43 @@ bool GxsForumGroupItem::setGroup(const RsGxsForumGroup &group) return true; } -void GxsForumGroupItem::loadGroup(const uint32_t &token) +void GxsForumGroupItem::loadGroup() { -#ifdef DEBUG_ITEM - std::cerr << "GxsForumGroupItem::loadGroup()"; - std::cerr << std::endl; + RsThread::async([this]() + { + // 1 - get group data + +#ifdef DEBUG_FORUMS + std::cerr << "Retrieving post data for post " << mThreadId << std::endl; #endif - std::vector groups; - if (!rsGxsForums->getGroupData(token, groups)) - { - std::cerr << "GxsForumGroupItem::loadGroup() ERROR getting data"; - std::cerr << std::endl; - return; - } + std::vector groups; + const std::list forumIds = { groupId() }; - if (groups.size() != 1) - { - std::cerr << "GxsForumGroupItem::loadGroup() Wrong number of Items"; - std::cerr << std::endl; - return; - } + if(!rsGxsForums->getForumsInfo(forumIds,groups)) + { + RsErr() << "GxsForumGroupItem::loadGroup() ERROR getting data" << std::endl; + return; + } - setGroup(groups[0]); + if (groups.size() != 1) + { + std::cerr << "GxsForumGroupItem::loadGroup() Wrong number of Items"; + std::cerr << std::endl; + return; + } + const RsGxsForumGroup& group(groups[0]); + + RsQThreadUtils::postToObject( [group,this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ + + setGroup(group); + + }, this ); + }); } QString GxsForumGroupItem::groupName() @@ -132,9 +159,9 @@ void GxsForumGroupItem::fill() ui->descLabel->setText(QString::fromUtf8(mGroup.mDescription.c_str())); if (IS_GROUP_PUBLISHER(mGroup.mMeta.mSubscribeFlags)) { - ui->forumlogo_label->setPixmap(QPixmap(":/icons/png/forums.png")); + ui->forumlogo_label->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/forums.png")); } else { - ui->forumlogo_label->setPixmap(QPixmap(":/icons/png/forums-default.png")); + ui->forumlogo_label->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/forums-default.png")); } if (IS_GROUP_SUBSCRIBED(mGroup.mMeta.mSubscribeFlags)) { @@ -143,10 +170,62 @@ void GxsForumGroupItem::fill() ui->subscribeButton->setEnabled(true); } -// if (mIsNew) -// { + if(feedId() == NEWSFEED_UPDATED_FORUM) + { + if(!mAddedModerators.empty() || !mRemovedModerators.empty()) + { + ui->titleLabel->setText(tr("Moderator list changed")); + ui->moderatorList_GB->show(); + + QString msg; + + if(!mAddedModerators.empty()) + { + msg += "Added moderators:" ; + msg += "

"; + for(auto& gxsid: mAddedModerators) + { + RsIdentityDetails det; + if(rsIdentity->getIdDetails(gxsid,det)) + msg += QString::fromUtf8(det.mNickname.c_str())+" ("+QString::fromStdString(gxsid.toStdString())+"), "; + else + msg += QString("[Unknown name]") + " ("+QString::fromStdString(gxsid.toStdString())+"), "; + } + msg.resize(msg.size()-2); + msg += "

"; + } + if(!mRemovedModerators.empty()) + { + msg += "Removed moderators:" ; + msg += "

"; + for(auto& gxsid: mRemovedModerators) + { + RsIdentityDetails det; + + if( rsIdentity->getIdDetails(gxsid,det)) + msg += QString::fromUtf8(det.mNickname.c_str())+" ("+QString::fromStdString(gxsid.toStdString())+"), "; + else + msg += QString("[Unknown name]") + " ("+QString::fromStdString(gxsid.toStdString())+"), "; + } + msg.resize(msg.size()-2); + msg += "

"; + } + ui->moderatorList_TE->setText(msg); + } + else + { + ui->moderatorList_GB->hide(); + + ui->titleLabel->setText(tr("Forum updated")); + ui->moderatorList_GB->hide(); + } + } + else + { ui->titleLabel->setText(tr("New Forum")); -// } + ui->moderatorList_GB->hide(); + } + // else // { // ui->titleLabel->setText(tr("Updated Forum")); @@ -173,13 +252,13 @@ void GxsForumGroupItem::doExpand(bool open) if (open) { ui->expandFrame->show(); - ui->expandButton->setIcon(QIcon(QString(":/icons/png/up-arrow.png"))); + ui->expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/png/up-arrow.png"))); ui->expandButton->setToolTip(tr("Hide")); } else { ui->expandFrame->hide(); - ui->expandButton->setIcon(QIcon(QString(":/icons/png/down-arrow.png"))); + ui->expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/png/down-arrow.png"))); ui->expandButton->setToolTip(tr("Expand")); } diff --git a/retroshare-gui/src/gui/feeds/GxsForumGroupItem.h b/retroshare-gui/src/gui/feeds/GxsForumGroupItem.h index 3ac198630..29b7851f2 100644 --- a/retroshare-gui/src/gui/feeds/GxsForumGroupItem.h +++ b/retroshare-gui/src/gui/feeds/GxsForumGroupItem.h @@ -37,6 +37,7 @@ class GxsForumGroupItem : public GxsGroupFeedItem public: /** Default Constructor */ GxsForumGroupItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsGroupId &groupId, bool isHome, bool autoUpdate); + GxsForumGroupItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsGroupId &groupId, const std::list& added_moderators,const std::list& removed_moderators,bool isHome, bool autoUpdate); GxsForumGroupItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsForumGroup &group, bool isHome, bool autoUpdate); ~GxsForumGroupItem(); @@ -50,7 +51,7 @@ protected: /* GxsGroupFeedItem */ virtual QString groupName(); - virtual void loadGroup(const uint32_t &token); + virtual void loadGroup() override; virtual RetroShareLink::enumType getLinkType() { return RetroShareLink::TYPE_FORUM; } private slots: @@ -65,6 +66,9 @@ private: /** Qt Designer generated object */ Ui::GxsForumGroupItem *ui; + + std::list mAddedModerators; + std::list mRemovedModerators; }; #endif diff --git a/retroshare-gui/src/gui/feeds/GxsForumGroupItem.ui b/retroshare-gui/src/gui/feeds/GxsForumGroupItem.ui index a506efa7b..cc3994307 100644 --- a/retroshare-gui/src/gui/feeds/GxsForumGroupItem.ui +++ b/retroshare-gui/src/gui/feeds/GxsForumGroupItem.ui @@ -6,8 +6,8 @@ 0 0 - 618 - 161 + 784 + 464 @@ -104,8 +104,8 @@ QFrame::Sunken - - + + @@ -280,7 +280,7 @@ - + @@ -334,6 +334,33 @@ + + + + Moderator list + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + diff --git a/retroshare-gui/src/gui/feeds/GxsForumMsgItem.cpp b/retroshare-gui/src/gui/feeds/GxsForumMsgItem.cpp index 0e691258c..de9dceb79 100644 --- a/retroshare-gui/src/gui/feeds/GxsForumMsgItem.cpp +++ b/retroshare-gui/src/gui/feeds/GxsForumMsgItem.cpp @@ -28,8 +28,10 @@ #include "FeedHolder.h" #include "gui/RetroShareLink.h" +#include "gui/common/FilesDefs.h" #include "gui/gxs/GxsIdDetails.h" #include "util/HandleRichText.h" +#include "util/qtthreadsutils.h" #include "util/DateTime.h" #include @@ -94,10 +96,9 @@ void GxsForumMsgItem::setup() mInFill = false; mCloseOnRead = false; - mTokenTypeParentMessage = nextTokenType(); /* clear ui */ - ui->titleLabel->setText(tr("Loading")); + ui->titleLabel->setText(tr("Loading...")); ui->subjectLabel->clear(); ui->timestamplabel->clear(); ui->parentNameLabel->clear(); @@ -154,13 +155,10 @@ bool GxsForumMsgItem::setMessage(const RsGxsForumMsg &msg, bool doFill) mMessage = msg; - if (!isTop()) { - requestParentMessage(mMessage.mMeta.mParentId); - } else { - if (doFill) { - fill(); - } - } + if (!isTop()) + loadParentMessage(mMessage.mMeta.mParentId); + else if(doFill) + fill(); return true; } @@ -170,91 +168,144 @@ QString GxsForumMsgItem::groupName() return QString::fromUtf8(mGroup.mMeta.mGroupName.c_str()); } -void GxsForumMsgItem::loadGroup(const uint32_t &token) +void GxsForumMsgItem::loadGroup() { -#ifdef DEBUG_ITEM - std::cerr << "GxsForumGroupItem::loadGroup()"; - std::cerr << std::endl; + RsThread::async([this]() + { + // 1 - get group data + +#ifdef DEBUG_FORUMS + std::cerr << "Retrieving post data for post " << mThreadId << std::endl; #endif - std::vector groups; - if (!rsGxsForums->getGroupData(token, groups)) - { - std::cerr << "GxsForumGroupItem::loadGroup() ERROR getting data"; - std::cerr << std::endl; - return; - } + std::vector groups; + const std::list forumIds = { groupId() }; - if (groups.size() != 1) - { - std::cerr << "GxsForumGroupItem::loadGroup() Wrong number of Items"; - std::cerr << std::endl; - return; - } + if(!rsGxsForums->getForumsInfo(forumIds,groups)) + { + RsErr() << "GxsForumGroupItem::loadGroup() ERROR getting data" << std::endl; + return; + } - setGroup(groups[0]); + if (groups.size() != 1) + { + std::cerr << "GxsForumGroupItem::loadGroup() Wrong number of Items"; + std::cerr << std::endl; + return; + } + RsGxsForumGroup group(groups[0]); + + RsQThreadUtils::postToObject( [group,this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ + + setGroup(group); + + }, this ); + }); } -void GxsForumMsgItem::loadMessage(const uint32_t &token) +void GxsForumMsgItem::loadMessage() { #ifdef DEBUG_ITEM std::cerr << "GxsForumMsgItem::loadMessage()"; std::cerr << std::endl; #endif - std::vector msgs; - if (!rsGxsForums->getMsgData(token, msgs)) + RsThread::async([this]() { - std::cerr << "GxsForumMsgItem::loadMessage() ERROR getting data"; - std::cerr << std::endl; - return; - } - - if (msgs.size() != 1) - { - std::cerr << "GxsForumMsgItem::loadMessage() Wrong number of Items"; - std::cerr << std::endl; - return; - } + // 1 - get group data - setMessage(msgs[0]); +#ifdef DEBUG_FORUMS + std::cerr << "Retrieving post data for post " << mThreadId << std::endl; +#endif + + std::vector msgs; + const std::list forumIds = { groupId() }; + + if(!rsGxsForums->getForumContent(groupId(),std::set( { messageId() } ),msgs)) + { + std::cerr << "GxsForumMsgItem::loadMessage() ERROR getting data"; + std::cerr << std::endl; + return; + } + + if (msgs.size() != 1) + { + std::cerr << "GxsForumMsgItem::loadMessage() Wrong number of Items"; + std::cerr << std::endl; + return; + } + const RsGxsForumMsg& msg(msgs[0]); + + RsQThreadUtils::postToObject( [msg,this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ + + setMessage(msg); + + }, this ); + }); } -void GxsForumMsgItem::loadParentMessage(const uint32_t &token) +void GxsForumMsgItem::loadParentMessage(const RsGxsMessageId& parent_msg) { #ifdef DEBUG_ITEM std::cerr << "GxsForumMsgItem::loadParentMessage()"; std::cerr << std::endl; #endif - std::vector msgs; - if (!rsGxsForums->getMsgData(token, msgs)) + RsThread::async([parent_msg,this]() { - std::cerr << "GxsForumMsgItem::loadParentMessage() ERROR getting data"; - std::cerr << std::endl; - return; - } + // 1 - get group data - if (msgs.size() != 1) - { - std::cerr << "GxsForumMsgItem::loadParentMessage() Wrong number of Items"; - std::cerr << std::endl; - return; - } +#ifdef DEBUG_FORUMS + std::cerr << "Retrieving post data for post " << mThreadId << std::endl; +#endif - mParentMessage = msgs[0]; + std::vector msgs; + const std::list forumIds = { groupId() }; - fill(); + if(!rsGxsForums->getForumContent(groupId(),std::set( { parent_msg } ),msgs)) + { + std::cerr << "GxsForumMsgItem::loadMessage() ERROR getting data"; + std::cerr << std::endl; + return; + } + + if (msgs.size() != 1) + { + std::cerr << "GxsForumMsgItem::loadMessage() Wrong number of Items"; + std::cerr << std::endl; + return; + } + const RsGxsForumMsg& msg(msgs[0]); + + RsQThreadUtils::postToObject( [msg,this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ + + mParentMessage = msg; + fill(); + + }, this ); + }); } void GxsForumMsgItem::fill() { /* fill in */ - if (isLoading()) { - /* Wait for all requests */ - return; - } +// if (isLoading()) { +// /* Wait for all requests */ +// return; +// } #ifdef DEBUG_ITEM std::cerr << "GxsForumMsgItem::fill()"; @@ -284,9 +335,9 @@ void GxsForumMsgItem::fill() } if (IS_GROUP_PUBLISHER(mGroup.mMeta.mSubscribeFlags)) { - ui->iconLabel->setPixmap(QPixmap(":/icons/png/forums.png")); + ui->iconLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/forums.png")); } else { - ui->iconLabel->setPixmap(QPixmap(":/icons/png/forums-default.png")); + ui->iconLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/forums-default.png")); } if (!mIsHome) { @@ -307,18 +358,6 @@ void GxsForumMsgItem::fill() ui->nameLabel->setId(mMessage.mMeta.mAuthorId); -// ui->avatar->setId(msg.srcId, true); - -// if (rsPeers->getPeerName(msg.srcId) != "") { -// RetroShareLink linkMessage; -// linkMessage.createMessage(msg.srcId, ""); -// nameLabel->setText(linkMessage.toHtml()); -// } -// else -// { -// nameLabel->setText(tr("Anonymous")); -// } - RetroShareLink msgLink = RetroShareLink::createGxsMessageLink(RetroShareLink::TYPE_FORUM, mMessage.mMeta.mGroupId, mMessage.mMeta.mMsgId, messageName()); ui->subLabel->setText(msgLink.toHtml()); if (wasExpanded() || ui->expandFrame->isVisible()) { @@ -330,14 +369,13 @@ void GxsForumMsgItem::fill() if (isTop()) { ui->parentFrame->hide(); } else { -// ui->parentAvatar->setId(msgParent.srcId, true); RetroShareLink linkParent = RetroShareLink::createGxsMessageLink(RetroShareLink::TYPE_FORUM, mParentMessage.mMeta.mGroupId, mParentMessage.mMeta.mMsgId, QString::fromUtf8(mParentMessage.mMeta.mMsgName.c_str())); ui->parentSubLabel->setText(linkParent.toHtml()); ui->parentMsgLabel->setText(RsHtml().formatText(NULL, QString::fromUtf8(mParentMessage.mMsg.c_str()), RSHTML_FORMATTEXT_EMBED_SMILEYS | RSHTML_FORMATTEXT_EMBED_LINKS)); ui->parentNameLabel->setId(mParentMessage.mMeta.mAuthorId); - + RsIdentityDetails idDetails ; rsIdentity->getIdDetails(mParentMessage.mMeta.mAuthorId,idDetails); @@ -348,16 +386,6 @@ void GxsForumMsgItem::fill() ui->parentAvatar->setPixmap(pixmap); -// if (rsPeers->getPeerName(msgParent.srcId) !="") -// { -// RetroShareLink linkMessage; -// linkMessage.createMessage(msgParent.srcId, ""); -// ui->parentNameLabel->setText(linkMessage.toHtml()); -// } -// else -// { -// ui->parentNameLabel->setText(tr("Anonymous")); -// } } /* header stuff */ @@ -394,7 +422,7 @@ void GxsForumMsgItem::doExpand(bool open) if (open) { ui->expandFrame->show(); - ui->expandButton->setIcon(QIcon(QString(":/icons/png/up-arrow.png"))); + ui->expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/png/up-arrow.png"))); ui->expandButton->setToolTip(tr("Hide")); if (!mParentMessage.mMeta.mMsgId.isNull()) { @@ -407,7 +435,7 @@ void GxsForumMsgItem::doExpand(bool open) { ui->expandFrame->hide(); ui->parentFrame->hide(); - ui->expandButton->setIcon(QIcon(QString(":/icons/png/down-arrow.png"))); + ui->expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/png/down-arrow.png"))); ui->expandButton->setToolTip(tr("Expand")); } @@ -440,63 +468,6 @@ void GxsForumMsgItem::setReadStatus(bool isNew, bool /*isUnread*/) ui->frame->style()->polish( ui->frame); } -void GxsForumMsgItem::requestParentMessage(const RsGxsMessageId &msgId) -{ -#ifdef DEBUG_ITEM - std::cerr << "GxsFeedItem::requestMessage()"; - std::cerr << std::endl; -#endif - - if (!initLoadQueue()) { - return; - } - - if (mLoadQueue->activeRequestExist(mTokenTypeParentMessage)) { - /* Request already running */ - return; - } - - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; - - GxsMsgReq msgIds; - std::set &vect_msgIds = msgIds[groupId()]; - vect_msgIds.insert(msgId); - - uint32_t token; - mLoadQueue->requestMsgInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, msgIds, mTokenTypeParentMessage); -} - -void GxsForumMsgItem::loadRequest(const TokenQueue *queue, const TokenRequest &req) -{ -#ifdef DEBUG_ITEM - std::cerr << "GxsFeedItem::loadRequest()"; - std::cerr << std::endl; -#endif - - if (queue == mLoadQueue) { - if (req.mUserType == mTokenTypeParentMessage) { - loadParentMessage(req.mToken); - return; - } - } - - GxsFeedItem::loadRequest(queue, req); -} - -bool GxsForumMsgItem::isLoading() -{ - if (GxsFeedItem::isLoading()) { - return true; - } - - if (mLoadQueue && mLoadQueue->activeRequestExist(mTokenTypeParentMessage)) { - return true; - } - - return false; -} - /*********** SPECIFIC FUNCTIONS ***********************/ void GxsForumMsgItem::readAndClearItem() diff --git a/retroshare-gui/src/gui/feeds/GxsForumMsgItem.h b/retroshare-gui/src/gui/feeds/GxsForumMsgItem.h index 332d5877e..35872ab9c 100644 --- a/retroshare-gui/src/gui/feeds/GxsForumMsgItem.h +++ b/retroshare-gui/src/gui/feeds/GxsForumMsgItem.h @@ -50,20 +50,18 @@ protected: virtual void expandFill(bool first); /* load message data */ - void requestParentMessage(const RsGxsMessageId &msgId); - virtual void loadParentMessage(const uint32_t &token); + virtual void loadParentMessage(const RsGxsMessageId &parent_msg); /* GxsGroupFeedItem */ virtual QString groupName(); - virtual void loadGroup(const uint32_t &token); - virtual void loadRequest(const TokenQueue *queue, const TokenRequest &req); + virtual void loadGroup() override; virtual RetroShareLink::enumType getLinkType() { return RetroShareLink::TYPE_FORUM; } - virtual bool isLoading(); + //virtual bool isLoading(); /* GxsFeedItem */ virtual QString messageName(); - virtual void loadMessage(const uint32_t &token); - virtual void loadComment(const uint32_t &/*token*/){ return;} + virtual void loadMessage() override; + virtual void loadComment() override { return; } private slots: /* default stuff */ @@ -90,7 +88,6 @@ private: RsGxsForumGroup mGroup; RsGxsForumMsg mMessage; RsGxsForumMsg mParentMessage; - uint32_t mTokenTypeParentMessage; /** Qt Designer generated object */ Ui::GxsForumMsgItem *ui; diff --git a/retroshare-gui/src/gui/feeds/MsgItem.cpp b/retroshare-gui/src/gui/feeds/MsgItem.cpp index 181774a00..a25e97873 100644 --- a/retroshare-gui/src/gui/feeds/MsgItem.cpp +++ b/retroshare-gui/src/gui/feeds/MsgItem.cpp @@ -29,6 +29,7 @@ #include "util/HandleRichText.h" #include "util/DateTime.h" #include "gui/common/AvatarDefs.h" +#include "gui/common/FilesDefs.h" #include "gui/notifyqt.h" #include @@ -231,7 +232,7 @@ void MsgItem::doExpand(bool open) if (open) { expandFrame->show(); - expandButton->setIcon(QIcon(QString(":/icons/png/up-arrow.png"))); + expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/png/up-arrow.png"))); expandButton->setToolTip(tr("Hide")); mCloseOnRead = false; @@ -241,7 +242,7 @@ void MsgItem::doExpand(bool open) else { expandFrame->hide(); - expandButton->setIcon(QIcon(QString(":/icons/png/down-arrow.png"))); + expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/png/down-arrow.png"))); expandButton->setToolTip(tr("Expand")); } diff --git a/retroshare-gui/src/gui/feeds/MsgItem.ui b/retroshare-gui/src/gui/feeds/MsgItem.ui index 0437b72b5..592376182 100644 --- a/retroshare-gui/src/gui/feeds/MsgItem.ui +++ b/retroshare-gui/src/gui/feeds/MsgItem.ui @@ -162,7 +162,7 @@ Reply Message - + :/images/mail_reply.png:/images/mail_reply.png @@ -196,7 +196,7 @@ - :/icons/png/cancel.png:/icons/png/cancel.png + :/icons/mail/delete.png:/icons/mail/delete.png diff --git a/retroshare-gui/src/gui/feeds/NewsFeedUserNotify.cpp b/retroshare-gui/src/gui/feeds/NewsFeedUserNotify.cpp index e71c166f2..65a377edf 100644 --- a/retroshare-gui/src/gui/feeds/NewsFeedUserNotify.cpp +++ b/retroshare-gui/src/gui/feeds/NewsFeedUserNotify.cpp @@ -18,6 +18,7 @@ * * *******************************************************************************/ +#include "gui/common/FilesDefs.h" #include "NewsFeedUserNotify.h" #include "gui/NewsFeed.h" @@ -37,7 +38,7 @@ void NewsFeedUserNotify::newsFeedChanged(int count) QIcon NewsFeedUserNotify::getMainIcon(bool hasNew) { - return hasNew ? QIcon(":/icons/png/newsfeed-notify.png") : QIcon(":/icons/png/newsfeed.png"); + return hasNew ? FilesDefs::getIconFromQtResourcePath(":/icons/png/newsfeed-notify.png") : FilesDefs::getIconFromQtResourcePath(":/icons/png/newsfeed.png"); } unsigned int NewsFeedUserNotify::getNewCount() diff --git a/retroshare-gui/src/gui/feeds/NewsFeedUserNotify.h b/retroshare-gui/src/gui/feeds/NewsFeedUserNotify.h index 081053593..c5206f24f 100644 --- a/retroshare-gui/src/gui/feeds/NewsFeedUserNotify.h +++ b/retroshare-gui/src/gui/feeds/NewsFeedUserNotify.h @@ -29,6 +29,7 @@ class NewsFeedUserNotify : public UserNotify { Q_OBJECT + virtual QString textInfo() const override { return tr("logged event(s)"); } public: NewsFeedUserNotify(NewsFeed *newsFeed, QObject *parent = 0); diff --git a/retroshare-gui/src/gui/feeds/PeerItem.cpp b/retroshare-gui/src/gui/feeds/PeerItem.cpp index 2a6f0b19d..a03572f61 100644 --- a/retroshare-gui/src/gui/feeds/PeerItem.cpp +++ b/retroshare-gui/src/gui/feeds/PeerItem.cpp @@ -26,6 +26,7 @@ #include "retroshare-gui/RsAutoUpdatePage.h" #include "gui/msgs/MessageComposer.h" #include "gui/common/StatusDefs.h" +#include "gui/common/FilesDefs.h" #include "gui/common/AvatarDefs.h" #include "util/DateTime.h" @@ -243,13 +244,13 @@ void PeerItem::doExpand(bool open) if (open) { expandFrame->show(); - expandButton->setIcon(QIcon(QString(":/icons/png/up-arrow.png"))); + expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/png/up-arrow.png"))); expandButton->setToolTip(tr("Hide")); } else { expandFrame->hide(); - expandButton->setIcon(QIcon(QString(":/icons/png/down-arrow.png"))); + expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/png/down-arrow.png"))); expandButton->setToolTip(tr("Expand")); } diff --git a/retroshare-gui/src/gui/feeds/PeerItem.ui b/retroshare-gui/src/gui/feeds/PeerItem.ui index 8baa1c832..27c38211f 100644 --- a/retroshare-gui/src/gui/feeds/PeerItem.ui +++ b/retroshare-gui/src/gui/feeds/PeerItem.ui @@ -159,8 +159,8 @@ Send Message - - :/images/mail_new.png:/images/mail_new.png + + :/icons/mail/write-mail.png:/icons/mail/write-mail.png Qt::ToolButtonTextBesideIcon @@ -188,8 +188,8 @@ Start Chat - - :/images/chat_24.png:/images/chat_24.png + + :/icons/png/chats.png:/icons/png/chats.png Qt::ToolButtonTextBesideIcon diff --git a/retroshare-gui/src/gui/feeds/PostedGroupItem.cpp b/retroshare-gui/src/gui/feeds/PostedGroupItem.cpp index 1a9ebeb9e..4ed8a95c4 100644 --- a/retroshare-gui/src/gui/feeds/PostedGroupItem.cpp +++ b/retroshare-gui/src/gui/feeds/PostedGroupItem.cpp @@ -22,8 +22,10 @@ #include "ui_PostedGroupItem.h" #include "FeedHolder.h" +#include "util/qtthreadsutils.h" #include "gui/RetroShareLink.h" #include "gui/gxs/GxsIdDetails.h" +#include "gui/common/FilesDefs.h" /**** * #define DEBUG_ITEM 1 @@ -59,7 +61,7 @@ void PostedGroupItem::setup() setAttribute(Qt::WA_DeleteOnClose, true); /* clear ui */ - ui->nameLabel->setText(tr("Loading")); + ui->nameLabel->setText(tr("Loading...")); ui->titleLabel->clear(); ui->descLabel->clear(); @@ -90,29 +92,43 @@ bool PostedGroupItem::setGroup(const RsPostedGroup &group) return true; } -void PostedGroupItem::loadGroup(const uint32_t &token) +void PostedGroupItem::loadGroup() { -#ifdef DEBUG_ITEM - std::cerr << "PostedGroupItem::loadGroup()"; - std::cerr << std::endl; + RsThread::async([this]() + { + // 1 - get group data + +#ifdef DEBUG_FORUMS + std::cerr << "Retrieving post data for post " << mThreadId << std::endl; #endif - std::vector groups; - if (!rsPosted->getGroupData(token, groups)) - { - std::cerr << "PostedGroupItem::loadGroup() ERROR getting data"; - std::cerr << std::endl; - return; - } + std::vector groups; + const std::list groupIds = { groupId() }; - if (groups.size() != 1) - { - std::cerr << "PostedGroupItem::loadGroup() Wrong number of Items"; - std::cerr << std::endl; - return; - } + if(!rsPosted->getBoardsInfo(groupIds,groups)) + { + RsErr() << "GxsPostedGroupItem::loadGroup() ERROR getting data" << std::endl; + return; + } - setGroup(groups[0]); + if (groups.size() != 1) + { + std::cerr << "GxsPostedGroupItem::loadGroup() Wrong number of Items"; + std::cerr << std::endl; + return; + } + RsPostedGroup group(groups[0]); + + RsQThreadUtils::postToObject( [group,this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ + + setGroup(group); + + }, this ); + }); } QString PostedGroupItem::groupName() @@ -141,15 +157,15 @@ void PostedGroupItem::fill() GxsIdDetails::loadPixmapFromData(mGroup.mGroupImage.mData, mGroup.mGroupImage.mSize, postedImage,GxsIdDetails::ORIGINAL); ui->logoLabel->setPixmap(QPixmap(postedImage)); } else { - ui->logoLabel->setPixmap(QPixmap(":/images/posted_64.png")); + ui->logoLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/posted.png")); } //TODO - nice icon for subscribed group // if (IS_GROUP_PUBLISHER(mGroup.mMeta.mSubscribeFlags)) { -// ui->logoLabel->setPixmap(QPixmap(":/images/posted_64.png")); +// ui->logoLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/posted.png")); // } else { -// ui->logoLabel->setPixmap(QPixmap(":/images/posted_64.png")); +// ui->logoLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/posted.png")); // } if (IS_GROUP_SUBSCRIBED(mGroup.mMeta.mSubscribeFlags)) { @@ -160,11 +176,11 @@ void PostedGroupItem::fill() // if (mIsNew) // { - ui->titleLabel->setText(tr("New Posted")); + ui->titleLabel->setText(tr("New Board")); // } // else // { -// ui->titleLabel->setText(tr("Updated Posted")); +// ui->titleLabel->setText(tr("Updated Board")); // } if (mIsHome) @@ -189,13 +205,13 @@ void PostedGroupItem::doExpand(bool open) if (open) { ui->expandFrame->show(); - ui->expandButton->setIcon(QIcon(QString(":/icons/png/up-arrow.png"))); + ui->expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/png/up-arrow.png"))); ui->expandButton->setToolTip(tr("Hide")); } else { ui->expandFrame->hide(); - ui->expandButton->setIcon(QIcon(QString(":/icons/png/down-arrow.png"))); + ui->expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/png/down-arrow.png"))); ui->expandButton->setToolTip(tr("Expand")); } diff --git a/retroshare-gui/src/gui/feeds/PostedGroupItem.h b/retroshare-gui/src/gui/feeds/PostedGroupItem.h index 88e1906dc..642dc4be0 100644 --- a/retroshare-gui/src/gui/feeds/PostedGroupItem.h +++ b/retroshare-gui/src/gui/feeds/PostedGroupItem.h @@ -50,7 +50,7 @@ protected: /* GxsGroupFeedItem */ virtual QString groupName(); - virtual void loadGroup(const uint32_t &token); + virtual void loadGroup() override; virtual RetroShareLink::enumType getLinkType() { return RetroShareLink::TYPE_UNKNOWN; } private slots: diff --git a/retroshare-gui/src/gui/feeds/PostedGroupItem.ui b/retroshare-gui/src/gui/feeds/PostedGroupItem.ui index 2ebd46dbc..27226fd28 100644 --- a/retroshare-gui/src/gui/feeds/PostedGroupItem.ui +++ b/retroshare-gui/src/gui/feeds/PostedGroupItem.ui @@ -125,7 +125,10 @@ - :/images/posted_64.png + :/icons/png/posted.png + + + true @@ -147,7 +150,7 @@
- Posted + Board @@ -298,7 +301,7 @@ - Posted Description + Board Description @@ -321,7 +324,7 @@ Description - of Posted + of Board
diff --git a/retroshare-gui/src/gui/feeds/SecurityIpItem.cpp b/retroshare-gui/src/gui/feeds/SecurityIpItem.cpp index 590bd0588..6e543108d 100644 --- a/retroshare-gui/src/gui/feeds/SecurityIpItem.cpp +++ b/retroshare-gui/src/gui/feeds/SecurityIpItem.cpp @@ -29,6 +29,7 @@ #include "util/DateTime.h" #include "gui/common/PeerDefs.h" #include "gui/common/RsBanListDefs.h" +#include "gui/common/FilesDefs.h" #include #include @@ -199,13 +200,13 @@ void SecurityIpItem::doExpand(bool open) if (open) { ui->expandFrame->show(); - ui->expandButton->setIcon(QIcon(":/icons/png/up-arrow.png")); + ui->expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/up-arrow.png")); ui->expandButton->setToolTip(tr("Hide")); } else { ui->expandFrame->hide(); - ui->expandButton->setIcon(QIcon(":/icons/png/down-arrow.png")); + ui->expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/down-arrow.png")); ui->expandButton->setToolTip(tr("Expand")); } diff --git a/retroshare-gui/src/gui/feeds/SecurityItem.cpp b/retroshare-gui/src/gui/feeds/SecurityItem.cpp index aedfd4e6c..b2bbc2cf3 100644 --- a/retroshare-gui/src/gui/feeds/SecurityItem.cpp +++ b/retroshare-gui/src/gui/feeds/SecurityItem.cpp @@ -29,6 +29,7 @@ #include "gui/common/StatusDefs.h" #include "gui/connect/ConfCertDialog.h" #include "gui/connect/PGPKeyDialog.h" +#include "gui/common/FilesDefs.h" #include "gui/connect/ConnectFriendWizard.h" #include "gui/common/AvatarDefs.h" #include "util/DateTime.h" @@ -295,13 +296,13 @@ void SecurityItem::doExpand(bool open) if (open) { expandFrame->show(); - expandButton->setIcon(QIcon(QString(":/icons/png/up-arrow.png"))); + expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/png/up-arrow.png"))); expandButton->setToolTip(tr("Hide")); } else { expandFrame->hide(); - expandButton->setIcon(QIcon(QString(":/icons/png/down-arrow.png"))); + expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/png/down-arrow.png"))); expandButton->setToolTip(tr("Expand")); } diff --git a/retroshare-gui/src/gui/groups/CreateGroup.cpp b/retroshare-gui/src/gui/groups/CreateGroup.cpp index b7c01564d..91380f98b 100644 --- a/retroshare-gui/src/gui/groups/CreateGroup.cpp +++ b/retroshare-gui/src/gui/groups/CreateGroup.cpp @@ -26,6 +26,7 @@ #include "gui/common/GroupDefs.h" #include "gui/settings/rsharesettings.h" #include "util/misc.h" +#include "gui/common/FilesDefs.h" #include @@ -40,7 +41,7 @@ CreateGroup::CreateGroup(const RsNodeGroupId &groupId, QWidget *parent) mIsStandard = false; - ui.headerFrame->setHeaderImage(QPixmap(":/images/user/add_group256.png")); + ui.headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/images/user/add_group256.png")); mGroupId = groupId; @@ -68,7 +69,7 @@ CreateGroup::CreateGroup(const RsNodeGroupId &groupId, QWidget *parent) } setWindowTitle(tr("Edit Group")); - ui.headerFrame->setHeaderImage(QPixmap(":/images/user/edit_group64.png")); + ui.headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/images/user/edit_group64.png")); ui.headerFrame->setHeaderText(tr("Edit Group")); ui.groupName->setDisabled(mIsStandard); diff --git a/retroshare-gui/src/gui/gxs/GxsCommentDialog.cpp b/retroshare-gui/src/gui/gxs/GxsCommentDialog.cpp index f9b8b1dbc..43dc43d13 100644 --- a/retroshare-gui/src/gui/gxs/GxsCommentDialog.cpp +++ b/retroshare-gui/src/gui/gxs/GxsCommentDialog.cpp @@ -36,10 +36,12 @@ GxsCommentDialog::GxsCommentDialog(QWidget *parent, RsTokenService *token_servic /* Invoke the Qt Designer generated QObject setup routine */ ui->setupUi(this); - //ui->postFrame->setVisible(false); - - ui->treeWidget->setup(token_service, comment_service); + setTokenService(token_service,comment_service); + init(); +} +void GxsCommentDialog::init() +{ /* Set header resize modes and initial section sizes */ QHeaderView * ttheader = ui->treeWidget->header () ; ttheader->resizeSection (0, 440); @@ -50,6 +52,7 @@ GxsCommentDialog::GxsCommentDialog(QWidget *parent, RsTokenService *token_servic connect(ui->refreshButton, SIGNAL(clicked()), this, SLOT(refresh())); connect(ui->idChooser, SIGNAL(currentIndexChanged( int )), this, SLOT(voterSelectionChanged( int ))); connect(ui->idChooser, SIGNAL(idsLoaded()), this, SLOT(idChooserReady())); + connect(ui->treeWidget,SIGNAL(commentsLoaded(int)),this,SLOT(notifyCommentsLoaded(int))); connect(ui->commentButton, SIGNAL(clicked()), ui->treeWidget, SLOT(makeComment())); connect(ui->sortBox, SIGNAL(currentIndexChanged(int)), this, SLOT(sortComments(int))); @@ -62,6 +65,20 @@ GxsCommentDialog::GxsCommentDialog(QWidget *parent, RsTokenService *token_servic ui->sortBox->setIconSize(QSize(S*1.5,S*1.5)); } +void GxsCommentDialog::setTokenService(RsTokenService *token_service, RsGxsCommentService *comment_service) +{ + ui->treeWidget->setup(token_service, comment_service); +} + +GxsCommentDialog::GxsCommentDialog(QWidget *parent) + : QWidget(parent), ui(new Ui::GxsCommentDialog) +{ + /* Invoke the Qt Designer generated QObject setup routine */ + ui->setupUi(this); + + init(); +} + GxsCommentDialog::~GxsCommentDialog() { delete(ui); @@ -79,6 +96,11 @@ void GxsCommentDialog::commentLoad(const RsGxsGroupId &grpId, const std::settreeWidget->requestComments(mGrpId,msg_versions,most_recent_msgId); } +void GxsCommentDialog::notifyCommentsLoaded(int n) +{ + emit commentsLoaded(n); +} + void GxsCommentDialog::refresh() { std::cerr << "GxsCommentDialog::refresh()"; diff --git a/retroshare-gui/src/gui/gxs/GxsCommentDialog.h b/retroshare-gui/src/gui/gxs/GxsCommentDialog.h index 82d2fdbd1..00982fdbb 100644 --- a/retroshare-gui/src/gui/gxs/GxsCommentDialog.h +++ b/retroshare-gui/src/gui/gxs/GxsCommentDialog.h @@ -32,9 +32,11 @@ class GxsCommentDialog: public QWidget Q_OBJECT public: + GxsCommentDialog(QWidget *parent); GxsCommentDialog(QWidget *parent, RsTokenService *token_service, RsGxsCommentService *comment_service); virtual ~GxsCommentDialog(); + void setTokenService(RsTokenService *token_service, RsGxsCommentService *comment_service); void setCommentHeader(QWidget *header); void commentLoad(const RsGxsGroupId &grpId, const std::set &msg_versions, const RsGxsMessageId &most_recent_msgId); @@ -46,8 +48,14 @@ private slots: void idChooserReady(); void voterSelectionChanged( int index ); void sortComments(int); + void notifyCommentsLoaded(int n); + +signals: + void commentsLoaded(int); private: + void init(); + RsGxsGroupId mGrpId; RsGxsMessageId mMostRecentMsgId; std::set mMsgVersions; diff --git a/retroshare-gui/src/gui/gxs/GxsCommentTreeWidget.cpp b/retroshare-gui/src/gui/gxs/GxsCommentTreeWidget.cpp index 296ce68c8..cc2087044 100644 --- a/retroshare-gui/src/gui/gxs/GxsCommentTreeWidget.cpp +++ b/retroshare-gui/src/gui/gxs/GxsCommentTreeWidget.cpp @@ -18,6 +18,14 @@ * * *******************************************************************************/ +#include "GxsCommentTreeWidget.h" + +#include "gui/common/FilesDefs.h" +#include "gui/common/RSElidedItemDelegate.h" +#include "gui/common/RSTreeWidgetItem.h" +#include "gui/gxs/GxsCreateCommentDialog.h" +#include "gui/gxs/GxsIdTreeWidgetItem.h" + #include #include #include @@ -25,14 +33,9 @@ #include #include #include +#include #include -#include "gui/common/RSElidedItemDelegate.h" -#include "gui/gxs/GxsCommentTreeWidget.h" -#include "gui/gxs/GxsCreateCommentDialog.h" -#include "gui/gxs/GxsIdTreeWidgetItem.h" -#include "gui/common/RSTreeWidgetItem.h" - #include #define PCITEM_COLUMN_COMMENT 0 @@ -186,18 +189,18 @@ void GxsCommentTreeWidget::setCurrentCommentMsgId(QTreeWidgetItem *current, QTre void GxsCommentTreeWidget::customPopUpMenu(const QPoint& /*point*/) { QMenu contextMnu( this ); - QAction* action = contextMnu.addAction(QIcon(IMAGE_MESSAGE), tr("Reply to Comment"), this, SLOT(replyToComment())); + QAction* action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MESSAGE), tr("Reply to Comment"), this, SLOT(replyToComment())); action->setDisabled(mCurrentCommentMsgId.isNull()); - action = contextMnu.addAction(QIcon(IMAGE_MESSAGE), tr("Submit Comment"), this, SLOT(makeComment())); + action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MESSAGE), tr("Submit Comment"), this, SLOT(makeComment())); action->setDisabled(mMsgVersions.empty()); - action = contextMnu.addAction(QIcon(IMAGE_COPY), tr("Copy Comment"), this, SLOT(copyComment())); + action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COPY), tr("Copy Comment"), this, SLOT(copyComment())); action->setDisabled(mCurrentCommentMsgId.isNull()); contextMnu.addSeparator(); - action = contextMnu.addAction(QIcon(IMAGE_VOTEUP), tr("Vote Up"), this, SLOT(voteUp())); + action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_VOTEUP), tr("Vote Up"), this, SLOT(voteUp())); action->setDisabled(mVoterId.isNull()); - action = contextMnu.addAction(QIcon(IMAGE_VOTEDOWN), tr("Vote Down"), this, SLOT(voteDown())); + action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_VOTEDOWN), tr("Vote Down"), this, SLOT(voteDown())); action->setDisabled(mVoterId.isNull()); @@ -208,14 +211,14 @@ void GxsCommentTreeWidget::customPopUpMenu(const QPoint& /*point*/) contextMnu.addSeparator(); QMenu *rep_menu = contextMnu.addMenu(tr("Reputation")); - action = rep_menu->addAction(QIcon(IMAGE_MESSAGE), tr("Show Reputation"), this, SLOT(showReputation())); + action = rep_menu->addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MESSAGE), tr("Show Reputation"), this, SLOT(showReputation())); contextMnu.addSeparator(); - action = rep_menu->addAction(QIcon(IMAGE_MESSAGE), tr("Interesting User"), this, SLOT(markInteresting())); + action = rep_menu->addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MESSAGE), tr("Interesting User"), this, SLOT(markInteresting())); contextMnu.addSeparator(); - action = rep_menu->addAction(QIcon(IMAGE_MESSAGE), tr("Mark Spammy"), this, SLOT(markSpammer())); - action = rep_menu->addAction(QIcon(IMAGE_MESSAGE), tr("Ban User"), this, SLOT(banUser())); + action = rep_menu->addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MESSAGE), tr("Mark Spammy"), this, SLOT(markSpammer())); + action = rep_menu->addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MESSAGE), tr("Ban User"), this, SLOT(banUser())); */ } @@ -225,8 +228,10 @@ void GxsCommentTreeWidget::customPopUpMenu(const QPoint& /*point*/) void GxsCommentTreeWidget::voteUp() { - std::cerr << "GxsCommentTreeWidget::voteUp()"; +#ifdef DEBUG_GXSCOMMENT_TREEWIDGET + std::cerr << "GxsCommentTreeWidget::voteUp()"; std::cerr << std::endl; +#endif vote(mGroupId, mLatestMsgId, mCurrentCommentMsgId, mVoterId, true); } @@ -234,8 +239,10 @@ void GxsCommentTreeWidget::voteUp() void GxsCommentTreeWidget::voteDown() { - std::cerr << "GxsCommentTreeWidget::voteDown()"; +#ifdef DEBUG_GXSCOMMENT_TREEWIDGET + std::cerr << "GxsCommentTreeWidget::voteDown()"; std::cerr << std::endl; +#endif vote(mGroupId, mLatestMsgId, mCurrentCommentMsgId, mVoterId, false); } @@ -243,8 +250,10 @@ void GxsCommentTreeWidget::voteDown() void GxsCommentTreeWidget::setVoteId(const RsGxsId &voterId) { mVoterId = voterId; - std::cerr << "GxsCommentTreeWidget::setVoterId(" << mVoterId << ")"; +#ifdef DEBUG_GXSCOMMENT_TREEWIDGET + std::cerr << "GxsCommentTreeWidget::setVoterId(" << mVoterId << ")"; std::cerr << std::endl; +#endif } @@ -267,6 +276,7 @@ void GxsCommentTreeWidget::vote(const RsGxsGroupId &groupId, const RsGxsMessageI vote.mVoteType = GXS_VOTE_DOWN; } +#ifdef DEBUG_GXSCOMMENT_TREEWIDGET std::cerr << "GxsCommentTreeWidget::vote()"; std::cerr << std::endl; @@ -274,6 +284,7 @@ void GxsCommentTreeWidget::vote(const RsGxsGroupId &groupId, const RsGxsMessageI std::cerr << "ThreadId : " << vote.mMeta.mThreadId << std::endl; std::cerr << "ParentId : " << vote.mMeta.mParentId << std::endl; std::cerr << "AuthorId : " << vote.mMeta.mAuthorId << std::endl; +#endif uint32_t token; mCommentService->createNewVote(token, vote); @@ -307,7 +318,7 @@ void GxsCommentTreeWidget::banUser() void GxsCommentTreeWidget::makeComment() { - GxsCreateCommentDialog pcc(mTokenQueue, mCommentService, std::make_pair(mGroupId,mLatestMsgId), mLatestMsgId, this); + GxsCreateCommentDialog pcc(mCommentService, std::make_pair(mGroupId,mLatestMsgId), mLatestMsgId, this); pcc.exec(); } @@ -316,7 +327,7 @@ void GxsCommentTreeWidget::replyToComment() RsGxsGrpMsgIdPair msgId; msgId.first = mGroupId; msgId.second = mCurrentCommentMsgId; - GxsCreateCommentDialog pcc(mTokenQueue, mCommentService, msgId, mLatestMsgId, this); + GxsCreateCommentDialog pcc(mCommentService, msgId, mLatestMsgId, this); pcc.loadComment(mCurrentCommentText, mCurrentCommentAuthor, mCurrentCommentAuthorId); pcc.exec(); @@ -354,16 +365,21 @@ void GxsCommentTreeWidget::requestComments(const RsGxsGroupId& group, const std: service_requestComments(group,message_versions); } + void GxsCommentTreeWidget::service_requestComments(const RsGxsGroupId& group_id,const std::set & msgIds) { /* request comments */ - std::cerr << "GxsCommentTreeWidget::service_requestComments for group " << group_id << std::endl; +#ifdef DEBUG_GXSCOMMENT_TREEWIDGET + std::cerr << "GxsCommentTreeWidget::service_requestComments for group " << group_id << std::endl; +#endif std::vector ids_to_ask; for(std::set::const_iterator it(msgIds.begin());it!=msgIds.end();++it) { - std::cerr << " asking for msg " << *it << std::endl; +#ifdef DEBUG_GXSCOMMENT_TREEWIDGET + std::cerr << " asking for msg " << *it << std::endl; +#endif ids_to_ask.push_back(std::make_pair(group_id,*it)); } @@ -395,14 +411,18 @@ void GxsCommentTreeWidget::completeItems() std::map::iterator lit; std::multimap::iterator pit; - std::cerr << "GxsCommentTreeWidget::completeItems() " << mPendingInsertMap.size(); +#ifdef DEBUG_GXSCOMMENT_TREEWIDGET + std::cerr << "GxsCommentTreeWidget::completeItems() " << mPendingInsertMap.size(); std::cerr << " PendingItems"; std::cerr << std::endl; +#endif for(pit = mPendingInsertMap.begin(); pit != mPendingInsertMap.end(); ++pit) { - std::cerr << "GxsCommentTreeWidget::completeItems() item->parent: " << pit->first; +#ifdef DEBUG_GXSCOMMENT_TREEWIDGET + std::cerr << "GxsCommentTreeWidget::completeItems() item->parent: " << pit->first; std::cerr << std::endl; +#endif if (pit->first != parentId) { @@ -421,15 +441,19 @@ void GxsCommentTreeWidget::completeItems() if (parent) { - std::cerr << "GxsCommentTreeWidget::completeItems() Added to Parent"; +#ifdef DEBUG_GXSCOMMENT_TREEWIDGET + std::cerr << "GxsCommentTreeWidget::completeItems() Added to Parent"; std::cerr << std::endl; +#endif parent->addChild(pit->second); } else if (mMsgVersions.find(parentId) != mMsgVersions.end()) { - std::cerr << "GxsCommentTreeWidget::completeItems() Added to topLevelItems"; +#ifdef DEBUG_GXSCOMMENT_TREEWIDGET + std::cerr << "GxsCommentTreeWidget::completeItems() Added to topLevelItems"; std::cerr << std::endl; +#endif topLevelItems.append(pit->second); } @@ -439,8 +463,10 @@ void GxsCommentTreeWidget::completeItems() /* missing parent -> insert At Top Level */ QTreeWidgetItem *missingItem = service_createMissingItem(pit->first); - std::cerr << "GxsCommentTreeWidget::completeItems() Added MissingItem"; +#ifdef DEBUG_GXSCOMMENT_TREEWIDGET + std::cerr << "GxsCommentTreeWidget::completeItems() Added MissingItem"; std::cerr << std::endl; +#endif parent = missingItem; parent->addChild(pit->second); @@ -460,9 +486,11 @@ void GxsCommentTreeWidget::completeItems() void GxsCommentTreeWidget::addItem(RsGxsMessageId itemId, RsGxsMessageId parentId, QTreeWidgetItem *item) { - std::cerr << "GxsCommentTreeWidget::addItem() Id: " << itemId; +#ifdef DEBUG_GXSCOMMENT_TREEWIDGET + std::cerr << "GxsCommentTreeWidget::addItem() Id: " << itemId; std::cerr << " ParentId: " << parentId; std::cerr << std::endl; +#endif /* store in map -> for children */ mLoadingMap[itemId] = item; @@ -471,20 +499,48 @@ void GxsCommentTreeWidget::addItem(RsGxsMessageId itemId, RsGxsMessageId parentI it = mLoadingMap.find(parentId); if (it != mLoadingMap.end()) { - std::cerr << "GxsCommentTreeWidget::addItem() Added to Parent"; +#ifdef DEBUG_GXSCOMMENT_TREEWIDGET + std::cerr << "GxsCommentTreeWidget::addItem() Added to Parent"; std::cerr << std::endl; +#endif it->second->addChild(item); } else { - std::cerr << "GxsCommentTreeWidget::addItem() Added to Pending List"; +#ifdef DEBUG_GXSCOMMENT_TREEWIDGET + std::cerr << "GxsCommentTreeWidget::addItem() Added to Pending List"; std::cerr << std::endl; +#endif mPendingInsertMap.insert(std::make_pair(parentId, item)); } } +int treeCount(QTreeWidget *tree, QTreeWidgetItem *parent = 0) +{ + int count = 0; + if (parent == 0) { + int topCount = tree->topLevelItemCount(); + for (int i = 0; i < topCount; i++) { + QTreeWidgetItem *item = tree->topLevelItem(i); + if (item->isExpanded()) { + count += treeCount(tree, item); + } + } + count += topCount; + } else { + int childCount = parent->childCount(); + for (int i = 0; i < childCount; i++) { + QTreeWidgetItem *item = parent->child(i); + if (item->isExpanded()) { + count += treeCount(tree, item); + } + } + count += childCount; + } + return count; +} void GxsCommentTreeWidget::loadThread(const uint32_t &token) { clearItems(); @@ -492,6 +548,8 @@ void GxsCommentTreeWidget::loadThread(const uint32_t &token) service_loadThread(token); completeItems(); + + emit commentsLoaded(treeCount(this)); } void GxsCommentTreeWidget::acknowledgeComment(const uint32_t &token) @@ -611,8 +669,10 @@ QTreeWidgetItem *GxsCommentTreeWidget::service_createMissingItem(const RsGxsMess void GxsCommentTreeWidget::loadRequest(const TokenQueue *queue, const TokenRequest &req) { +#ifdef DEBUG_GXSCOMMENT_TREEWIDGET std::cerr << "GxsCommentTreeWidget::loadRequest() UserType: " << req.mUserType; std::cerr << std::endl; +#endif if (queue != mTokenQueue) { diff --git a/retroshare-gui/src/gui/gxs/GxsCommentTreeWidget.h b/retroshare-gui/src/gui/gxs/GxsCommentTreeWidget.h index a1977ab93..cdf6063af 100644 --- a/retroshare-gui/src/gui/gxs/GxsCommentTreeWidget.h +++ b/retroshare-gui/src/gui/gxs/GxsCommentTreeWidget.h @@ -80,6 +80,9 @@ public slots: void markSpammer(); void banUser(); +signals: + void commentsLoaded(int); + protected: void vote(const RsGxsGroupId &groupId, const RsGxsMessageId &threadId, diff --git a/retroshare-gui/src/gui/gxs/GxsCreateCommentDialog.cpp b/retroshare-gui/src/gui/gxs/GxsCreateCommentDialog.cpp index 5158d8981..dd3f2f97c 100644 --- a/retroshare-gui/src/gui/gxs/GxsCreateCommentDialog.cpp +++ b/retroshare-gui/src/gui/gxs/GxsCreateCommentDialog.cpp @@ -26,20 +26,16 @@ #include #include -GxsCreateCommentDialog::GxsCreateCommentDialog(TokenQueue *tokQ, RsGxsCommentService *service, - const RsGxsGrpMsgIdPair &parentId, const RsGxsMessageId& threadId, QWidget *parent) : +GxsCreateCommentDialog::GxsCreateCommentDialog(RsGxsCommentService *service, const RsGxsGrpMsgIdPair &parentId, const RsGxsMessageId& threadId, QWidget *parent) : QDialog(parent), - ui(new Ui::GxsCreateCommentDialog), mTokenQueue(tokQ), mCommentService(service), mParentId(parentId), mThreadId(threadId) + ui(new Ui::GxsCreateCommentDialog), mCommentService(service), mParentId(parentId), mThreadId(threadId) { ui->setupUi(this); connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(createComment())); connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(close())); - /* fill in the available OwnIds for signing */ ui->idChooser->loadIds(IDCHOOSER_ID_REQUIRED, RsGxsId()); - - } void GxsCreateCommentDialog::loadComment(const QString &msgText, const QString &msgAuthor, const RsGxsId &msgAuthorId) @@ -80,9 +76,9 @@ void GxsCreateCommentDialog::createComment() std::cerr << "ThreadId : " << comment.mMeta.mThreadId << std::endl; std::cerr << "ParentId : " << comment.mMeta.mParentId << std::endl; - RsGxsId authorId; - switch (ui->idChooser->getChosenId(authorId)) { + switch (ui->idChooser->getChosenId(authorId)) + { case GxsIdChooser::KnowId: case GxsIdChooser::UnKnowId: comment.mMeta.mAuthorId = authorId; @@ -96,17 +92,12 @@ void GxsCreateCommentDialog::createComment() std::cerr << "GxsCreateCommentDialog::createComment() ERROR GETTING AuthorId!"; std::cerr << std::endl; - int ret = QMessageBox::information(this, tr("Comment Signing Error"), - tr("You need to create an Identity\n" - "before you can comment"), - QMessageBox::Ok); - Q_UNUSED(ret) + QMessageBox::information(this, tr("Comment Signing Error"), tr("You need to create an Identity\n" "before you can comment"), QMessageBox::Ok); return; - }//switch (ui->idChooser->getChosenId(authorId)) + } uint32_t token; - mCommentService->createNewComment(token, comment); - mTokenQueue->queueRequest(token, TOKENREQ_MSGINFO, RS_TOKREQ_ANSTYPE_ACK, 0); + mCommentService->createComment(comment); close(); } diff --git a/retroshare-gui/src/gui/gxs/GxsCreateCommentDialog.h b/retroshare-gui/src/gui/gxs/GxsCreateCommentDialog.h index 8267e448a..967649179 100644 --- a/retroshare-gui/src/gui/gxs/GxsCreateCommentDialog.h +++ b/retroshare-gui/src/gui/gxs/GxsCreateCommentDialog.h @@ -36,8 +36,7 @@ class GxsCreateCommentDialog : public QDialog Q_OBJECT public: - explicit GxsCreateCommentDialog(TokenQueue* tokQ, RsGxsCommentService *service, - const RsGxsGrpMsgIdPair& parentId, const RsGxsMessageId& threadId, QWidget *parent = 0); + explicit GxsCreateCommentDialog(RsGxsCommentService *service, const RsGxsGrpMsgIdPair& parentId, const RsGxsMessageId& threadId, QWidget *parent = 0); ~GxsCreateCommentDialog(); void loadComment(const QString &msgText, const QString &msgAuthor, const RsGxsId &msgAuthorId); @@ -47,7 +46,6 @@ private slots: private: Ui::GxsCreateCommentDialog *ui; - TokenQueue *mTokenQueue; RsGxsCommentService *mCommentService; RsGxsGrpMsgIdPair mParentId; diff --git a/retroshare-gui/src/gui/gxs/GxsCreateCommentDialog.ui b/retroshare-gui/src/gui/gxs/GxsCreateCommentDialog.ui index 550610748..27d128867 100644 --- a/retroshare-gui/src/gui/gxs/GxsCreateCommentDialog.ui +++ b/retroshare-gui/src/gui/gxs/GxsCreateCommentDialog.ui @@ -112,6 +112,12 @@ p, li { white-space: pre-wrap; } true + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + diff --git a/retroshare-gui/src/gui/gxs/GxsFeedItem.cpp b/retroshare-gui/src/gui/gxs/GxsFeedItem.cpp index 98873445a..b9f2e68ee 100644 --- a/retroshare-gui/src/gui/gxs/GxsFeedItem.cpp +++ b/retroshare-gui/src/gui/gxs/GxsFeedItem.cpp @@ -39,9 +39,6 @@ GxsFeedItem::GxsFeedItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsGro /* load data if we can */ mMessageId = messageId; - - mTokenTypeMessage = nextTokenType(); - mTokenTypeComment = nextTokenType(); } GxsFeedItem::~GxsFeedItem() @@ -86,110 +83,14 @@ void GxsFeedItem::copyMessageLink() } } -void GxsFeedItem::fillDisplay(RsGxsUpdateBroadcastBase *updateBroadcastBase, bool complete) -{ - GxsGroupFeedItem::fillDisplay(updateBroadcastBase, complete); - - std::map > msgs; - updateBroadcastBase->getAllMsgIds(msgs); - - if (!msgs.empty()) - { - std::map >::const_iterator mit = msgs.find(groupId()); - - if (mit != msgs.end() && mit->second.find(messageId()) != mit->second.end()) - requestMessage(); - } -} - void GxsFeedItem::requestMessage() { -#ifdef DEBUG_ITEM - std::cerr << "GxsFeedItem::requestMessage()"; - std::cerr << std::endl; -#endif + loadMessage(); - if (!initLoadQueue()) { - return; - } - - if (mLoadQueue->activeRequestExist(mTokenTypeMessage)) { - /* Request already running */ - return; - } - - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; - - GxsMsgReq msgIds; - std::set &vect_msgIds = msgIds[groupId()]; - vect_msgIds.insert(mMessageId); - - uint32_t token; - mLoadQueue->requestMsgInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, msgIds, mTokenTypeMessage); } void GxsFeedItem::requestComment() { -#ifdef DEBUG_ITEM - std::cerr << "GxsFeedItem::requestComment()"; - std::cerr << std::endl; -#endif - - if (!initLoadQueue()) { - return; - } - - if (mLoadQueue->activeRequestExist(mTokenTypeComment)) { - /* Request already running */ - return; - } - - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_MSG_RELATED_DATA; - opts.mOptions = RS_TOKREQOPT_MSG_THREAD | RS_TOKREQOPT_MSG_LATEST; - - std::vector msgIds; - - for(int i=0;irequestMsgRelatedInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, msgIds, mTokenTypeComment); + loadComment(); } -void GxsFeedItem::loadRequest(const TokenQueue *queue, const TokenRequest &req) -{ -#ifdef DEBUG_ITEM - std::cerr << "GxsFeedItem::loadRequest()"; - std::cerr << std::endl; -#endif - - if (queue == mLoadQueue) { - if (req.mUserType == mTokenTypeMessage) { - loadMessage(req.mToken); - return; - } - if (req.mUserType == mTokenTypeComment) { - loadComment(req.mToken); - return; - } - } - - GxsGroupFeedItem::loadRequest(queue, req); -} - -bool GxsFeedItem::isLoading() -{ - if (GxsGroupFeedItem::isLoading()) { - return true; - } - - if (mLoadQueue && mLoadQueue->activeRequestExist(mTokenTypeMessage)) { - return true; - } - - return false; -} diff --git a/retroshare-gui/src/gui/gxs/GxsFeedItem.h b/retroshare-gui/src/gui/gxs/GxsFeedItem.h index 9b0fa9474..c7c16ea79 100644 --- a/retroshare-gui/src/gui/gxs/GxsFeedItem.h +++ b/retroshare-gui/src/gui/gxs/GxsFeedItem.h @@ -45,15 +45,17 @@ protected: void requestComment(); virtual QString messageName() = 0; - virtual void loadMessage(const uint32_t &token) = 0; - virtual void loadComment(const uint32_t &token) = 0; + virtual void loadMessage() = 0; + virtual void loadComment() = 0; /* GxsGroupFeedItem */ - virtual bool isLoading(); - virtual void fillDisplay(RsGxsUpdateBroadcastBase *updateBroadcastBase, bool complete); + //virtual bool isLoading(); + //virtual void fillDisplay(RsGxsUpdateBroadcastBase *updateBroadcastBase, bool complete); +#ifdef TO_REMOVE /* TokenResponse */ virtual void loadRequest(const TokenQueue *queue, const TokenRequest &req); +#endif protected slots: void comments(const QString &title); diff --git a/retroshare-gui/src/gui/gxs/GxsGroupDialog.cpp b/retroshare-gui/src/gui/gxs/GxsGroupDialog.cpp index c2d8b86e0..240f321db 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupDialog.cpp +++ b/retroshare-gui/src/gui/gxs/GxsGroupDialog.cpp @@ -19,9 +19,11 @@ *******************************************************************************/ #include +#include #include "util/misc.h" #include "util/DateTime.h" +#include "util/qtthreadsutils.h" #include "GxsGroupDialog.h" #include "gui/common/PeerDefs.h" #include "gui/RetroShareLink.h" @@ -62,25 +64,21 @@ #define GXSGROUP_INTERNAL_LOADGROUP 3 /** Constructor */ -GxsGroupDialog::GxsGroupDialog(TokenQueue *tokenExternalQueue, uint32_t enableFlags, uint32_t defaultFlags, QWidget *parent) - : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint), mTokenService(NULL), mExternalTokenQueue(tokenExternalQueue), mInternalTokenQueue(NULL), mGrpMeta(), mMode(MODE_CREATE), mEnabledFlags(enableFlags), mReadonlyFlags(0), mDefaultsFlags(defaultFlags) +GxsGroupDialog::GxsGroupDialog(uint32_t enableFlags, uint32_t defaultFlags, QWidget *parent) + : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint), mGrpMeta(), mMode(MODE_CREATE), mEnabledFlags(enableFlags), mReadonlyFlags(0), mDefaultsFlags(defaultFlags) { /* Invoke the Qt Designer generated object setup routine */ ui.setupUi(this); - mInternalTokenQueue = NULL; - init(); } -GxsGroupDialog::GxsGroupDialog(TokenQueue *tokenExternalQueue, RsTokenService *tokenService, Mode mode, RsGxsGroupId groupId, uint32_t enableFlags, uint32_t defaultFlags, QWidget *parent) - : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint), mTokenService(NULL), mExternalTokenQueue(tokenExternalQueue), mInternalTokenQueue(NULL), mGrpMeta(), mMode(mode), mEnabledFlags(enableFlags), mReadonlyFlags(0), mDefaultsFlags(defaultFlags) +GxsGroupDialog::GxsGroupDialog(Mode mode, RsGxsGroupId groupId, uint32_t enableFlags, uint32_t defaultFlags, QWidget *parent) + : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint), mGrpMeta(), mMode(mode), mEnabledFlags(enableFlags), mReadonlyFlags(0), mDefaultsFlags(defaultFlags) { /* Invoke the Qt Designer generated object setup routine */ ui.setupUi(this); - mTokenService = tokenService; - mInternalTokenQueue = new TokenQueue(tokenService, this); mGrpMeta.mGroupId = groupId; init(); @@ -89,9 +87,6 @@ GxsGroupDialog::GxsGroupDialog(TokenQueue *tokenExternalQueue, RsTokenService *t GxsGroupDialog::~GxsGroupDialog() { Settings->saveWidgetInformation(this); - if (mInternalTokenQueue) { - delete(mInternalTokenQueue); - } } void GxsGroupDialog::init() @@ -105,7 +100,6 @@ void GxsGroupDialog::init() connect(ui.groupLogo, SIGNAL(clicked() ), this , SLOT(addGroupLogo())); - connect(ui.addLogoButton, SIGNAL(clicked() ), this , SLOT(addGroupLogo())); ui.typePublic->setChecked(true); ui.distributionValueLabel->setText(tr("Public")); @@ -160,6 +154,14 @@ void GxsGroupDialog::init() Settings->loadWidgetInformation(this); } +void GxsGroupDialog::injectExtraWidget(QWidget *widget) +{ + // add extra widget into layout. + QVBoxLayout *vbox = new QVBoxLayout(); + vbox->addWidget(widget); + ui.extraFrame->setLayout(vbox); +} + QIcon GxsGroupDialog::serviceWindowIcon() { return qApp->windowIcon(); @@ -171,6 +173,9 @@ void GxsGroupDialog::showEvent(QShowEvent*) setWindowIcon(serviceWindowIcon()); initUi(); + + if(!mGrpMeta.mGroupId.isNull() && mGrpMeta.mPublishTs == 0) // group not actually loaded yet + loadGroup(mGrpMeta.mGroupId); } void GxsGroupDialog::setUiText(UiType uiType, const QString &text) @@ -229,7 +234,6 @@ void GxsGroupDialog::initMode() ui.stackedWidget->setCurrentIndex(1); mReadonlyFlags = 0xffffffff; // Force all to readonly. ui.buttonBox->setStandardButtons(QDialogButtonBox::Close); - requestGroup(mGrpMeta.mGroupId); } break; @@ -238,7 +242,6 @@ void GxsGroupDialog::initMode() ui.stackedWidget->setCurrentIndex(0); ui.buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); ui.buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Submit Group Changes")); - requestGroup(mGrpMeta.mGroupId); } break; } @@ -368,7 +371,6 @@ void GxsGroupDialog::setupVisibility() ui.groupName->setVisible(mEnabledFlags & GXS_GROUP_FLAGS_NAME); ui.groupLogo->setVisible(mEnabledFlags & GXS_GROUP_FLAGS_ICON); - ui.addLogoButton->setVisible(mEnabledFlags & GXS_GROUP_FLAGS_ICON); ui.groupDesc->setVisible(mEnabledFlags & GXS_GROUP_FLAGS_DESCRIPTION); @@ -409,8 +411,6 @@ void GxsGroupDialog::setAllReadonly() void GxsGroupDialog::setupReadonly() { - ui.addLogoButton->setEnabled(!(mReadonlyFlags & GXS_GROUP_FLAGS_ICON)); - ui.publishGroupBox->setEnabled(!(mReadonlyFlags & GXS_GROUP_FLAGS_PUBLISHSIGN)); ui.pubKeyShare_cb->setEnabled(!(mReadonlyFlags & GXS_GROUP_FLAGS_SHAREKEYS)); @@ -580,25 +580,18 @@ void GxsGroupDialog::editGroup() RsGroupMetaData newMeta; newMeta.mGroupId = mGrpMeta.mGroupId; - - if(!prepareGroupMetaData(newMeta)) + QString reason; + if(!prepareGroupMetaData(newMeta, reason)) { /* error message */ - QMessageBox::warning(this, "RetroShare", tr("Failed to Prepare Group MetaData - please Review"), QMessageBox::Ok, QMessageBox::Ok); + QMessageBox::warning(this, "RetroShare", tr("Failed to Prepare Group MetaData: ") + reason, QMessageBox::Ok, QMessageBox::Ok); return; //Don't add a empty name!! } std::cerr << "GxsGroupDialog::editGroup() calling service_EditGroup"; std::cerr << std::endl; - uint32_t token; - if (service_EditGroup(token, newMeta)) - { - // get the Queue to handle response. - if(mExternalTokenQueue != NULL) - mExternalTokenQueue->queueRequest(token, TOKENREQ_GROUPINFO, RS_TOKREQ_ANSTYPE_ACK, GXSGROUP_NEWGROUPID); - } - else + if (!service_updateGroup(newMeta)) { std::cerr << "GxsGroupDialog::editGroup() ERROR"; std::cerr << std::endl; @@ -607,14 +600,22 @@ void GxsGroupDialog::editGroup() close(); } -bool GxsGroupDialog::prepareGroupMetaData(RsGroupMetaData &meta) +bool GxsGroupDialog::prepareGroupMetaData(RsGroupMetaData &meta, QString &reason) { std::cerr << "GxsGroupDialog::prepareGroupMetaData()"; std::cerr << std::endl; // here would be the place to check for empty author id // but GXS_SERV::GRP_OPTION_AUTHEN_AUTHOR_SIGN is currently not used by any service + ui.idChooser->getChosenId(meta.mAuthorId); + if ((mDefaultsFlags & GXS_GROUP_DEFAULTS_PERSONAL_GROUP) && (meta.mAuthorId.isNull())) { + std::cerr << "GxsGroupDialog::prepareGroupMetaData()"; + std::cerr << " Group needs a Personal Signature"; + std::cerr << std::endl; + reason = "Missing AuthorId"; + return false; + } QString name = getName(); uint32_t flags = GXS_SERV::FLAG_PRIVACY_PUBLIC; @@ -623,6 +624,7 @@ bool GxsGroupDialog::prepareGroupMetaData(RsGroupMetaData &meta) std::cerr << "GxsGroupDialog::prepareGroupMetaData()"; std::cerr << " Invalid GroupName"; std::cerr << std::endl; + reason = "Missing GroupName"; return false; } @@ -636,6 +638,7 @@ bool GxsGroupDialog::prepareGroupMetaData(RsGroupMetaData &meta) std::cerr << "GxsGroupDialog::prepareGroupMetaData()"; std::cerr << " Invalid Circles"; std::cerr << std::endl; + reason = "Invalid Circle Parameters"; return false; } @@ -661,20 +664,25 @@ void GxsGroupDialog::createGroup() return; //Don't add a empty name!! } - uint32_t token; RsGroupMetaData meta; - if (!prepareGroupMetaData(meta)) + QString reason; + if (!prepareGroupMetaData(meta, reason)) { /* error message */ - QMessageBox::warning(this, "RetroShare", tr("Failed to Prepare Group MetaData - please Review"), QMessageBox::Ok, QMessageBox::Ok); + QMessageBox::warning(this, "RetroShare", tr("Failed to Prepare Group MetaData: ") + reason, QMessageBox::Ok, QMessageBox::Ok); return; //Don't add with invalid circle. } - if (service_CreateGroup(token, meta)) + if (service_createGroup(meta)) { - // get the Queue to handle response. + // now update the UI +#warning Missing code here! +#ifdef TODO + // + // get the Queue to handle response. What is this for? if(mExternalTokenQueue != NULL) mExternalTokenQueue->queueRequest(token, TOKENREQ_GROUPINFO, RS_TOKREQ_ANSTYPE_ACK, GXSGROUP_NEWGROUPID); +#endif } close(); @@ -884,6 +892,10 @@ void GxsGroupDialog::getSelectedModerators(std::set& ids) void GxsGroupDialog::setSelectedModerators(const std::set& ids) { + ui.addAdmins_cb->setChecked(true); + ui.adminsList->show(); + ui.filtercomboBox->show(); + ui.adminsList->setSelectedIds(ids, false); QString moderatorsListString ; @@ -968,51 +980,36 @@ void GxsGroupDialog::filterComboBoxChanged(int i) Loading Group. ***********************************************************************************/ -void GxsGroupDialog::requestGroup(const RsGxsGroupId &groupId) +void GxsGroupDialog::loadGroup(const RsGxsGroupId& grpId) { - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; - - std::list groupIds; - groupIds.push_back(groupId); - - std::cerr << "GxsGroupDialog::requestGroup() Requesting Group Summary(" << groupId << ")"; - std::cerr << std::endl; - - uint32_t token; - if (mInternalTokenQueue) - mInternalTokenQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, groupIds, GXSGROUP_INTERNAL_LOADGROUP) ; -} - -void GxsGroupDialog::loadGroup(uint32_t token) -{ - std::cerr << "GxsGroupDialog::loadGroup(" << token << ")"; - std::cerr << std::endl; - - QString description; - if (service_loadGroup(token, mMode, mGrpMeta, description)) + RsThread::async([this,grpId]() { - updateFromExistingMeta(description); - } -} + RsGxsGenericGroupData *groupData; -void GxsGroupDialog::loadRequest(const TokenQueue *queue, const TokenRequest &req) -{ - std::cerr << "GxsGroupDialog::loadRequest() UserType: " << req.mUserType; - std::cerr << std::endl; - - if (queue == mInternalTokenQueue) - { - /* now switch on req */ - switch(req.mUserType) + if(!service_getGroupData(grpId,groupData)) { - case GXSGROUP_INTERNAL_LOADGROUP: - loadGroup(req.mToken); - break; - default: - std::cerr << "GxsGroupDialog::loadGroup() UNKNOWN UserType "; - std::cerr << std::endl; - break; + std::cerr << __PRETTY_FUNCTION__ << " failed to collect group info " << std::endl; + return; } - } + + RsQThreadUtils::postToObject( [this,groupData]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete, note that + * Qt::QueuedConnection is important! + */ + + mGrpMeta = groupData->mMeta; + + QString description; + + if (service_loadGroup(groupData, mMode, description)) + updateFromExistingMeta(description); + + delete groupData; + + }, this ); + }); } + diff --git a/retroshare-gui/src/gui/gxs/GxsGroupDialog.h b/retroshare-gui/src/gui/gxs/GxsGroupDialog.h index 13cde700f..4860f52a8 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupDialog.h +++ b/retroshare-gui/src/gui/gxs/GxsGroupDialog.h @@ -84,10 +84,13 @@ public: #define GXS_GROUP_DEFAULTS_PERSONAL_PGP 0x00000100 #define GXS_GROUP_DEFAULTS_PERSONAL_REQUIRED 0x00000200 #define GXS_GROUP_DEFAULTS_PERSONAL_IFNOPUB 0x00000400 +// independent from other PERSONAL FLAGS. If Group requires a AuthorId. +#define GXS_GROUP_DEFAULTS_PERSONAL_GROUP 0x00000800 #define GXS_GROUP_DEFAULTS_COMMENTS_YES 0x00001000 #define GXS_GROUP_DEFAULTS_COMMENTS_NO 0x00002000 + #define GXS_GROUP_DEFAULTS_ANTISPAM_FAVOR_PGP 0x00100000 #define GXS_GROUP_DEFAULTS_ANTISPAM_TRACK 0x00200000 #define GXS_GROUP_DEFAULTS_ANTISPAM_FAVOR_PGP_KNOWN 0x00400000 @@ -105,7 +108,7 @@ public: * The long term plan is perhap logic structure (i.e. code) will be moved into each GXS \n * service for better customisation of group creation, or perhaps not! */ -class GxsGroupDialog : public QDialog, public TokenResponse +class GxsGroupDialog : public QDialog { Q_OBJECT @@ -128,15 +131,12 @@ public: /*! * Constructs a GxsGroupDialog for creating group - * @param tokenQueue This should be the TokenQueue of the (parent) service - * in order to receive acknowledgement of group creation, if set to NULL with create mode \n - * creation will not happen * @param enableFlags This determines what options are enabled such as Icon, Description, publish type and key sharing * @param defaultFlags This deter * @param parent The parent dialog * @param mode */ - GxsGroupDialog(TokenQueue* tokenQueue, uint32_t enableFlags, uint32_t defaultFlags, QWidget *parent = NULL); + GxsGroupDialog(uint32_t enableFlags, uint32_t defaultFlags, QWidget *parent = NULL); /*! * Contructs a GxsGroupDialog for display a group or editing @@ -144,15 +144,12 @@ public: * @param mode This determines whether the dialog starts in show or edit mode (Edit not supported yet) * @param parent */ - GxsGroupDialog(TokenQueue *tokenExternalQueue, RsTokenService *tokenService, Mode mode, RsGxsGroupId groupId, uint32_t enableFlags, uint32_t defaultFlags, QWidget *parent = NULL); + GxsGroupDialog(Mode mode, RsGxsGroupId groupId, uint32_t enableFlags, uint32_t defaultFlags, QWidget *parent = NULL); ~GxsGroupDialog(); uint32_t mode() { return mMode; } - // overloaded from TokenResponse - virtual void loadRequest(const TokenQueue *queue, const TokenRequest &req); - private: void newGroup(); void init(); @@ -172,6 +169,13 @@ protected: virtual QPixmap serviceImage() = 0; virtual QIcon serviceWindowIcon(); + /*! + * Inject Extra Widget for additional Group configuration options. + * NB: These are only inserted for createMode currently. + * @param widget Addtional widget which is added to extraFrame. + */ + void injectExtraWidget(QWidget *widget); + /*! * \brief setUiToolTip/setUiText * Sets the text and tooltip of some parts of the UI @@ -181,24 +185,37 @@ protected: void setUiToolTip(UiType uiType, const QString &text); void setUiText (UiType uiType, const QString &text); + /*! + * It is up to the service to retrieve its own group data, which derives from RsGxsGenericGroupData. That data will be passed down + * to the service itself for specific tasks. + * \param grpId Id of the group to retrieve + * \param data Generic group data for this group. /!\ The pointer should be deleted by the client when released. + * \return True if everything does fine. + */ + virtual bool service_getGroupData(const RsGxsGroupId& grpId,RsGxsGenericGroupData *& data) = 0; + /*! * It is up to the service to do the actual group creation * Service can also modify initial meta going into group - * @param token This should be set to the token retrieved * @param meta The deriving GXS service should set their grp meta to this value */ - virtual bool service_CreateGroup(uint32_t &token, const RsGroupMetaData &meta) = 0; + virtual bool service_createGroup(RsGroupMetaData& meta) = 0; /*! * It is up to the service to do the actual group editing - * @param token This should be set to the token retrieved * @param meta The deriving GXS service should set their grp meta to this value */ - virtual bool service_EditGroup(uint32_t &token, RsGroupMetaData &editedMeta) = 0; + virtual bool service_updateGroup(const RsGroupMetaData& editedMeta) = 0; - // To be overloaded by users. - // use Token to retrieve from service, fill in metaData. - virtual bool service_loadGroup(uint32_t token, Mode mode, RsGroupMetaData& groupMetaData, QString &description) = 0; + /*! + * Should be overloaded by the service in order to extract meaningful information from the group data (that is usually group-specific). + * One of them however, common to all groups is the description. So it is returned by this method so that the GxsGroupDialog updates it. + * \param data Generic group data, to be dynamic_cast by the client to specific service-level group data + * \param mode Editing mode (?) + * \param description Description string for the group. Common to all services, but still present in the service-specific data part. + * \return + */ + virtual bool service_loadGroup(const RsGxsGenericGroupData *data, Mode mode, QString &description) = 0; /*! * This returns a group logo from the ui \n @@ -260,11 +277,10 @@ private: void loadNewGroupId(const uint32_t &token); // loading existing Groups. - void requestGroup(const RsGxsGroupId &groupId); - void loadGroup(uint32_t token); + void loadGroup(const RsGxsGroupId &groupId); void updateFromExistingMeta(const QString &description); - bool prepareGroupMetaData(RsGroupMetaData &meta); + bool prepareGroupMetaData(RsGroupMetaData &meta, QString &reason); std::list mShareList; QPixmap mPicture; diff --git a/retroshare-gui/src/gui/gxs/GxsGroupDialog.ui b/retroshare-gui/src/gui/gxs/GxsGroupDialog.ui index 00e49d8b3..88777cd59 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupDialog.ui +++ b/retroshare-gui/src/gui/gxs/GxsGroupDialog.ui @@ -6,8 +6,8 @@ 0 0 - 600 - 563 + 1231 + 967 @@ -59,160 +59,114 @@ - - - 0 - - - 4 - + - 0 - - - 4 + 3 - 4 + 3 - - - + + + + + 64 + 64 + + + + + 64 + 64 + + + + <html><head/><body><p>Click to add a Logo</p></body></html> + + + QFrame::StyledPanel + + + QFrame::Raised + + + :/icons/png/add-image.png + + + true + + + + + + 6 - - 0 + + 6 - - - - - 64 - 64 - - - - - 64 - 64 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - :/images/channels.png - - - true + + + + Name - - - - 6 - - - 6 - - - - - Name - - - - - - - - - - - - 9 - - - 3 - - - 3 - - - 3 - - - 3 - - - - - Add Icon - - - - :/images/add_image24.png:/images/add_image24.png - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - <html><head/><body><p>The identity here can be used to send feedback.</p></body></html> - - - Owner: - - - - - - - - - - - - - Add moderators - - - - :/icons/settings/permissions.svg:/icons/settings/permissions.svg - - - - - - - Key recipients can publish to restricted-type group and can view and publish for private-type channels - - - Share Publish Key - - - - + + + + + + Qt::Horizontal + + + + 132 + 20 + + + + + + + + <html><head/><body><p>The identity here can be used to send feedback.</p></body></html> + + + Owner: + + + + + + + + + + + + + Add moderators + + + + :/icons/settings/permissions.svg:/icons/settings/permissions.svg + + + + + + + Key recipients can publish to restricted-type group and can view and publish for private-type channels + + + Share Publish Key + + + @@ -269,14 +223,14 @@ - Required + Re&quired - Encrypted Msgs + Encrypted &Msgs @@ -894,10 +848,14 @@ QComboBox
gui/common/GroupChooser.h
+ + ClickableLabel + QLabel +
util/ClickableLabel.h
+
- diff --git a/retroshare-gui/src/gui/gxs/GxsGroupFeedItem.cpp b/retroshare-gui/src/gui/gxs/GxsGroupFeedItem.cpp index 1f5b09cb8..fb530a3ef 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupFeedItem.cpp +++ b/retroshare-gui/src/gui/gxs/GxsGroupFeedItem.cpp @@ -45,19 +45,6 @@ GxsGroupFeedItem::GxsGroupFeedItem(FeedHolder *feedHolder, uint32_t feedId, cons /* load data if we can */ mGroupId = groupId; mGxsIface = iface; - - mNextTokenType = 0; - mTokenTypeGroup = nextTokenType(); - - mLoadQueue = NULL; - - if (mGxsIface && autoUpdate) { - /* Connect to update broadcast */ - mUpdateBroadcastBase = new RsGxsUpdateBroadcastBase(mGxsIface); - connect(mUpdateBroadcastBase, SIGNAL(fillDisplay(bool)), this, SLOT(fillDisplaySlot(bool))); - } else { - mUpdateBroadcastBase = NULL; - } } GxsGroupFeedItem::~GxsGroupFeedItem() @@ -67,28 +54,6 @@ GxsGroupFeedItem::~GxsGroupFeedItem() std::cerr << std::endl; #endif - if (mLoadQueue) { - delete mLoadQueue; - } - - if (mUpdateBroadcastBase) - { - delete(mUpdateBroadcastBase); - } -} - -bool GxsGroupFeedItem::initLoadQueue() -{ - if (mLoadQueue) { - return true; - } - - if (!mGxsIface) { - return false; - } - - mLoadQueue = new TokenQueue(mGxsIface->getTokenService(), this); - return (mLoadQueue != NULL); } void GxsGroupFeedItem::unsubscribe() @@ -139,15 +104,6 @@ void GxsGroupFeedItem::copyGroupLink() void GxsGroupFeedItem::fillDisplaySlot(bool complete) { - fillDisplay(mUpdateBroadcastBase, complete); -} - -void GxsGroupFeedItem::fillDisplay(RsGxsUpdateBroadcastBase *updateBroadcastBase, bool /*complete*/) -{ - std::set grpIds; - updateBroadcastBase->getAllGrpIds(grpIds); - - if (grpIds.find(groupId()) != grpIds.end()) requestGroup(); } @@ -155,51 +111,6 @@ void GxsGroupFeedItem::fillDisplay(RsGxsUpdateBroadcastBase *updateBroadcastBase void GxsGroupFeedItem::requestGroup() { -#ifdef DEBUG_ITEM - std::cerr << "GxsGroupFeedItem::requestGroup()"; - std::cerr << std::endl; -#endif - - if (!initLoadQueue()) { - return; - } - - if (mLoadQueue->activeRequestExist(mTokenTypeGroup)) { - /* Request already running */ - return; - } - - std::list ids; - ids.push_back(mGroupId); - - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; - uint32_t token; - mLoadQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_SUMMARY, opts, ids, mTokenTypeGroup); + loadGroup(); } -void GxsGroupFeedItem::loadRequest(const TokenQueue *queue, const TokenRequest &req) -{ -#ifdef DEBUG_ITEM - std::cerr << "GxsGroupFeedItem::loadRequest()"; - std::cerr << std::endl; -#endif - - if (queue == mLoadQueue) { - if (req.mUserType == mTokenTypeGroup) { - loadGroup(req.mToken); - } else { - std::cerr << "GxsGroupFeedItem::loadRequest() ERROR: INVALID TYPE"; - std::cerr << std::endl; - } - } -} - -bool GxsGroupFeedItem::isLoading() -{ - if (mLoadQueue && mLoadQueue->activeRequestExist(mTokenTypeGroup)) { - return true; - } - - return false; -} diff --git a/retroshare-gui/src/gui/gxs/GxsGroupFeedItem.h b/retroshare-gui/src/gui/gxs/GxsGroupFeedItem.h index 9696490d9..653d62bb1 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupFeedItem.h +++ b/retroshare-gui/src/gui/gxs/GxsGroupFeedItem.h @@ -25,7 +25,6 @@ #include #include "gui/feeds/FeedItem.h" -#include "util/TokenQueue.h" #include "gui/RetroShareLink.h" #include @@ -33,7 +32,7 @@ class FeedHolder; class RsGxsUpdateBroadcastBase; -class GxsGroupFeedItem : public FeedItem, public TokenResponse +class GxsGroupFeedItem : public FeedItem { Q_OBJECT @@ -46,20 +45,12 @@ public: uint32_t feedId() const { return mFeedId; } protected: - uint32_t nextTokenType() { return ++mNextTokenType; } - bool initLoadQueue(); - /* load group data */ void requestGroup(); - virtual bool isLoading(); - virtual void loadGroup(const uint32_t &token) = 0; + virtual void loadGroup() = 0; virtual RetroShareLink::enumType getLinkType() = 0; virtual QString groupName() = 0; - virtual void fillDisplay(RsGxsUpdateBroadcastBase *updateBroadcastBase, bool complete); - - /* TokenResponse */ - virtual void loadRequest(const TokenQueue *queue, const TokenRequest &req); protected slots: void subscribe(); @@ -69,7 +60,6 @@ protected slots: protected: bool mIsHome; RsGxsIfaceHelper *mGxsIface; - TokenQueue *mLoadQueue; private slots: /* RsGxsUpdateBroadcastBase */ @@ -77,9 +67,6 @@ private slots: private: RsGxsGroupId mGroupId; - RsGxsUpdateBroadcastBase *mUpdateBroadcastBase; - uint32_t mNextTokenType; - uint32_t mTokenTypeGroup; }; Q_DECLARE_METATYPE(RsGxsGroupId) diff --git a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp index d2aa30ce3..556d31760 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp +++ b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp @@ -29,10 +29,13 @@ #include "gui/settings/rsharesettings.h" #include "gui/RetroShareLink.h" #include "gui/gxs/GxsGroupShareKey.h" +#include "gui/common/GroupTreeWidget.h" #include "gui/common/RSTreeWidget.h" #include "gui/notifyqt.h" #include "gui/common/UIStateHelper.h" #include "gui/common/UserNotify.h" +#include "util/qtthreadsutils.h" +#include "retroshare/rsgxsifacetypes.h" #include "GxsCommentDialog.h" //#define DEBUG_GROUPFRAMEDIALOG @@ -43,7 +46,7 @@ #define IMAGE_INFO ":/images/info16.png" //#define IMAGE_GROUPAUTHD ":/images/konv_message2.png" #define IMAGE_COPYLINK ":/images/copyrslink.png" -#define IMAGE_EDIT ":/images/edit_16.png" +#define IMAGE_EDIT ":/icons/png/pencil-edit-button.png" #define IMAGE_SHARE ":/images/share-icon-16.png" #define IMAGE_TABNEW ":/images/tab-new.png" #define IMAGE_DELETE ":/images/delete.png" @@ -57,6 +60,8 @@ #define MAX_COMMENT_TITLE 32 +static const uint32_t DELAY_BETWEEN_GROUP_STATISTICS_UPDATE = 120; // do not update group statistics more often than once every 2 mins + /* * Transformation Notes: * there are still a couple of things that the new groups differ from Old version. @@ -68,12 +73,15 @@ /** Constructor */ GxsGroupFrameDialog::GxsGroupFrameDialog(RsGxsIfaceHelper *ifaceImpl, QWidget *parent,bool allow_dist_sync) -: RsGxsUpdateBroadcastPage(ifaceImpl, parent) +: MainPage(parent) { /* Invoke the Qt Designer generated object setup routine */ ui = new Ui::GxsGroupFrameDialog(); ui->setupUi(this); + mShouldUpdateMessageSummaryList = true; + mShouldUpdateGroupStatistics = false; + mLastGroupStatisticsUpdateTs=0; mInitialized = false; mDistSyncAllowed = allow_dist_sync; mInFill = false; @@ -82,12 +90,9 @@ GxsGroupFrameDialog::GxsGroupFrameDialog(RsGxsIfaceHelper *ifaceImpl, QWidget *p mSubscribedGroups = NULL; mPopularGroups = NULL; mOtherGroups = NULL; - mMessageWidget = NULL; /* Setup Queue */ mInterface = ifaceImpl; - mTokenService = mInterface->getTokenService(); - mTokenQueue = new TokenQueue(mInterface->getTokenService(), this); /* Setup UI helper */ mStateHelper = new UIStateHelper(this); @@ -125,7 +130,6 @@ GxsGroupFrameDialog::~GxsGroupFrameDialog() // save settings processSettings(false); - delete(mTokenQueue); delete(ui); } @@ -134,27 +138,29 @@ void GxsGroupFrameDialog::getGroupList(std::map & group_list = mCachedGroupMetas ; if(group_list.empty()) - requestGroupSummary(); + updateGroupSummary(); + else + std::cerr << "************** Using cached GroupMetaData" << std::endl; } void GxsGroupFrameDialog::initUi() { registerHelpButton(ui->helpButton, getHelpString(),pageName()) ; - ui->titleBarPixmap->setPixmap(QPixmap(icon(ICON_NAME))); + ui->titleBarPixmap->setPixmap(FilesDefs::getPixmapFromQtResourcePath(icon(ICON_NAME))); ui->titleBarLabel->setText(text(TEXT_NAME)); /* Initialize group tree */ QToolButton *newGroupButton = new QToolButton(this); - newGroupButton->setIcon(QIcon(icon(ICON_NEW))); + newGroupButton->setIcon(FilesDefs::getIconFromQtResourcePath(icon(ICON_NEW))); newGroupButton->setToolTip(text(TEXT_NEW)); connect(newGroupButton, SIGNAL(clicked()), this, SLOT(newGroup())); ui->groupTreeWidget->addToolButton(newGroupButton); /* Create group tree */ - mYourGroups = ui->groupTreeWidget->addCategoryItem(text(TEXT_YOUR_GROUP), QIcon(icon(ICON_YOUR_GROUP)), true); - mSubscribedGroups = ui->groupTreeWidget->addCategoryItem(text(TEXT_SUBSCRIBED_GROUP), QIcon(icon(ICON_SUBSCRIBED_GROUP)), true); - mPopularGroups = ui->groupTreeWidget->addCategoryItem(text(TEXT_POPULAR_GROUP), QIcon(icon(ICON_POPULAR_GROUP)), false); - mOtherGroups = ui->groupTreeWidget->addCategoryItem(text(TEXT_OTHER_GROUP), QIcon(icon(ICON_OTHER_GROUP)), false); + mYourGroups = ui->groupTreeWidget->addCategoryItem(text(TEXT_YOUR_GROUP), FilesDefs::getIconFromQtResourcePath(icon(ICON_YOUR_GROUP)), true); + mSubscribedGroups = ui->groupTreeWidget->addCategoryItem(text(TEXT_SUBSCRIBED_GROUP), FilesDefs::getIconFromQtResourcePath(icon(ICON_SUBSCRIBED_GROUP)), true); + mPopularGroups = ui->groupTreeWidget->addCategoryItem(text(TEXT_POPULAR_GROUP), FilesDefs::getIconFromQtResourcePath(icon(ICON_POPULAR_GROUP)), false); + mOtherGroups = ui->groupTreeWidget->addCategoryItem(text(TEXT_OTHER_GROUP), FilesDefs::getIconFromQtResourcePath(icon(ICON_OTHER_GROUP)), false); if (text(TEXT_TODO).isEmpty()) { ui->todoPushButton->hide(); @@ -168,18 +174,56 @@ void GxsGroupFrameDialog::initUi() connect(NotifyQt::getInstance(), SIGNAL(settingsChanged()), this, SLOT(settingsChanged())); settingsChanged(); } + + mInitialized = true; } void GxsGroupFrameDialog::showEvent(QShowEvent *event) { - if (!mInitialized) { + if (!mInitialized ) + { /* Problem: virtual methods cannot be used in constructor */ - mInitialized = true; initUi(); } - RsGxsUpdateBroadcastPage::showEvent(event); + uint32_t children = mYourGroups->childCount() + mSubscribedGroups->childCount() + mPopularGroups->childCount() + mOtherGroups->childCount(); + + bool empty = mCachedGroupMetas.empty() || children==0; + + updateDisplay( empty ); +} + +void GxsGroupFrameDialog::paintEvent(QPaintEvent *pe) +{ + if(mShouldUpdateMessageSummaryList) + { + if(!mGroupIdsSummaryToUpdate.empty()) + for(auto& group_id: mGroupIdsSummaryToUpdate) + updateMessageSummaryListReal(group_id); + else + updateMessageSummaryListReal(RsGxsGroupId()); + + mShouldUpdateMessageSummaryList = false; + mGroupIdsSummaryToUpdate.clear(); + } + + rstime_t now = time(nullptr); + + if(mShouldUpdateGroupStatistics && now > DELAY_BETWEEN_GROUP_STATISTICS_UPDATE + mLastGroupStatisticsUpdateTs) + { + // This mechanism allows to gather multiple updateGroupStatistics events at once and not send too many of them at the same time. + // it avoids re-loadign all the group everytime a friend sends new statistics. + + for(auto& groupId: mGroupStatisticsToUpdate) + updateGroupStatisticsReal(groupId); + + mShouldUpdateGroupStatistics = false; + mLastGroupStatisticsUpdateTs = time(nullptr); + mGroupStatisticsToUpdate.clear(); + } + + MainPage::paintEvent(pe); } void GxsGroupFrameDialog::processSettings(bool load) @@ -207,6 +251,13 @@ void GxsGroupFrameDialog::processSettings(bool load) Settings->endGroup(); } +bool GxsGroupFrameDialog::useTabs() +{ + GroupFrameSettings groupFrameSettings; + + return Settings->getGroupFrameSettings(groupFrameSettingsType(), groupFrameSettings) && groupFrameSettings.mOpenAllInNewTab; +} + void GxsGroupFrameDialog::settingsChanged() { GroupFrameSettings groupFrameSettings; @@ -218,17 +269,15 @@ void GxsGroupFrameDialog::settingsChanged() void GxsGroupFrameDialog::setSingleTab(bool singleTab) { - if (singleTab) { - if (!mMessageWidget) { - mMessageWidget = createMessageWidget(RsGxsGroupId()); - // remove close button of the the first tab - ui->messageTabWidget->hideCloseButton(ui->messageTabWidget->indexOf(mMessageWidget)); - } - } else { - if (mMessageWidget) { - delete(mMessageWidget); - mMessageWidget = NULL; - } + if (singleTab) + { + while(ui->messageTabWidget->count() > 1) + { + auto w = ui->messageTabWidget->widget(0) ; + ui->messageTabWidget->removeTab(0); + delete w; + } + ui->messageTabWidget->hideCloseButton(0); } } @@ -239,68 +288,59 @@ void GxsGroupFrameDialog::setHideTabBarWithOneTab(bool hideTabBarWithOneTab) void GxsGroupFrameDialog::updateDisplay(bool complete) { - if (complete || !getGrpIds().empty() || !getGrpIdsMeta().empty()) { - /* Update group list */ - requestGroupSummary(); - } else { - /* Update all groups of changed messages */ - std::map > msgIds; - getAllMsgIds(msgIds); + if(complete) // || !getGrpIds().empty() || !getGrpIdsMeta().empty()) { + updateGroupSummary(); /* Update group list */ - for (auto msgIt = msgIds.begin(); msgIt != msgIds.end(); ++msgIt) { - updateMessageSummaryList(msgIt->first); - } - } - - updateSearchResults() ; +// updateSearchResults() ; } void GxsGroupFrameDialog::updateSearchResults() { - const std::set& reqs = getSearchResults(); + for(auto& it:mSearchGroupsItems) + updateSearchResults(it.first); +} - for(auto it(reqs.begin());it!=reqs.end();++it) +void GxsGroupFrameDialog::updateSearchResults(const TurtleRequestId& sid) +{ + std::cerr << "updating search ID " << std::hex << sid << std::dec << std::endl; + + std::map group_infos; + + getDistantSearchResults(sid,group_infos) ; + + std::cerr << "retrieved " << std::endl; + + auto it2 = mSearchGroupsItems.find(sid); + + if(it2 == mSearchGroupsItems.end()) { - std::cerr << "updating search ID " << std::hex << *it << std::dec << std::endl; - - std::map group_infos; - - getDistantSearchResults(*it,group_infos) ; - - std::cerr << "retrieved " << std::endl; - - auto it2 = mSearchGroupsItems.find(*it); - - if(mSearchGroupsItems.end() == it2) - { - std::cerr << "GxsGroupFrameDialog::updateSearchResults(): received result notification for req " << std::hex << *it << std::dec << " but no item present!" << std::endl; - continue ; // we could create the item just as well but since this situation is not supposed to happen, I prefer to make this a failure case. - } - - QList group_items ; - - for(auto it3(group_infos.begin());it3!=group_infos.end();++it3) - if(mCachedGroupMetas.find(it3->first) == mCachedGroupMetas.end()) - { - std::cerr << " adding new group " << it3->first << " " - << it3->second.mGroupId << " \"" - << it3->second.mGroupName << "\"" << std::endl; - - GroupItemInfo i; - i.id = QString(it3->second.mGroupId.toStdString().c_str()); - i.name = QString::fromUtf8(it3->second.mGroupName.c_str()); - i.popularity = 0; // could be set to the number of hits - i.lastpost = QDateTime::fromTime_t(it3->second.mLastMessageTs); - i.subscribeFlags = 0; // irrelevant here - i.publishKey = false ; // IS_GROUP_PUBLISHER(groupInfo.mSubscribeFlags); - i.adminKey = false ; // IS_GROUP_ADMIN(groupInfo.mSubscribeFlags); - i.max_visible_posts = it3->second.mNumberOfMessages; - - group_items.push_back(i); - } - - ui->groupTreeWidget->fillGroupItems(it2->second, group_items); + std::cerr << "(EE) received a channel group turtle search result with ID " << sid << " but no item is known for this search" << std::endl; + return; } + + QList group_items ; + + for(auto it3(group_infos.begin());it3!=group_infos.end();++it3) + { + std::cerr << " adding group " << it3->first << " " << it3->second.mGroupId << " \"" << it3->second.mGroupName << "\"" << std::endl; + for(auto s:it3->second.mSearchContexts) + std::cerr << " Context string \"" << s << "\"" << std::endl; + + GroupItemInfo i; + i.id = QString(it3->second.mGroupId.toStdString().c_str()); + i.name = QString::fromUtf8(it3->second.mGroupName.c_str()); + i.popularity = 0; // could be set to the number of hits + i.lastpost = QDateTime::fromTime_t(it3->second.mLastMessageTs); + i.subscribeFlags = 0; // irrelevant here + i.publishKey = false ; // IS_GROUP_PUBLISHER(groupInfo.mSubscribeFlags); + i.adminKey = false ; // IS_GROUP_ADMIN(groupInfo.mSubscribeFlags); + i.max_visible_posts = it3->second.mNumberOfMessages; + i.context_strings = it3->second.mSearchContexts; + + group_items.push_back(i); + } + + ui->groupTreeWidget->fillGroupItems(it2->second, group_items); } void GxsGroupFrameDialog::todo() @@ -326,13 +366,22 @@ void GxsGroupFrameDialog::removeCurrentSearch() mSearchGroupsItems.erase(it); mKnownGroups.erase(search_request_id); + + clearDistantSearchResults(search_request_id); } void GxsGroupFrameDialog::removeAllSearches() { for(auto it(mSearchGroupsItems.begin());it!=mSearchGroupsItems.end();++it) - ui->groupTreeWidget->removeSearchItem(it->second) ; + { + QString group_id; + TurtleRequestId search_request_id; + if(ui->groupTreeWidget->isSearchRequestResultItem(it->second,group_id,search_request_id)) + clearDistantSearchResults(search_request_id); + + ui->groupTreeWidget->removeSearchItem(it->second) ; + } mSearchGroupsItems.clear(); mKnownGroups.clear(); } @@ -356,6 +405,7 @@ static uint32_t checkDelay(uint32_t time_in_secs) return 365 * 86400; } + void GxsGroupFrameDialog::groupTreeCustomPopupMenu(QPoint point) { // First separately handle the case of search top level items @@ -366,8 +416,8 @@ void GxsGroupFrameDialog::groupTreeCustomPopupMenu(QPoint point) { QMenu contextMnu(this); - contextMnu.addAction(QIcon(IMAGE_DELETE), tr("Remove this search"), this, SLOT(removeCurrentSearch()))->setData(search_request_id); - contextMnu.addAction(QIcon(IMAGE_DELETE), tr("Remove all searches"), this, SLOT(removeAllSearches())); + contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_DELETE), tr("Remove this search"), this, SLOT(removeCurrentSearch()))->setData(search_request_id); + contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_DELETE), tr("Remove all searches"), this, SLOT(removeAllSearches())); contextMnu.exec(QCursor::pos()); return ; } @@ -380,7 +430,7 @@ void GxsGroupFrameDialog::groupTreeCustomPopupMenu(QPoint point) { QMenu contextMnu(this); - contextMnu.addAction(QIcon(IMAGE_RETRIEVE), tr("Request data"), this, SLOT(distantRequestGroupData()))->setData(group_id_s); + contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_RETRIEVE), tr("Request data"), this, SLOT(distantRequestGroupData()))->setData(group_id_s); contextMnu.exec(QCursor::pos()); return ; } @@ -398,29 +448,27 @@ void GxsGroupFrameDialog::groupTreeCustomPopupMenu(QPoint point) QMenu contextMnu(this); QAction *action; - if (mMessageWidget) { - action = contextMnu.addAction(QIcon(IMAGE_TABNEW), tr("Open in new tab"), this, SLOT(openInNewTab())); - if (mGroupId.isNull() || messageWidget(mGroupId, true)) { - action->setEnabled(false); - } - } + action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_TABNEW), tr("Open in new tab"), this, SLOT(openInNewTab())); + + if(mGroupId.isNull()) // dont enable the open in tab if a tab is already here + action->setEnabled(false); if (isSubscribed) { - action = contextMnu.addAction(QIcon(IMAGE_UNSUBSCRIBE), tr("Unsubscribe"), this, SLOT(unsubscribeGroup())); + action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_UNSUBSCRIBE), tr("Unsubscribe"), this, SLOT(unsubscribeGroup())); action->setEnabled (!mGroupId.isNull() && IS_GROUP_SUBSCRIBED(subscribeFlags)); } else { - action = contextMnu.addAction(QIcon(IMAGE_SUBSCRIBE), tr("Subscribe"), this, SLOT(subscribeGroup())); + action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_SUBSCRIBE), tr("Subscribe"), this, SLOT(subscribeGroup())); action->setDisabled (mGroupId.isNull() || IS_GROUP_SUBSCRIBED(subscribeFlags)); } contextMnu.addSeparator(); - contextMnu.addAction(QIcon(icon(ICON_NEW)), text(TEXT_NEW), this, SLOT(newGroup())); + contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(icon(ICON_NEW)), text(TEXT_NEW), this, SLOT(newGroup())); - action = contextMnu.addAction(QIcon(IMAGE_INFO), tr("Show Details"), this, SLOT(showGroupDetails())); + action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_INFO), tr("Show Details"), this, SLOT(showGroupDetails())); action->setEnabled (!mGroupId.isNull()); - action = contextMnu.addAction(QIcon(IMAGE_EDIT), tr("Edit Details"), this, SLOT(editGroupDetails())); + action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_EDIT), tr("Edit Details"), this, SLOT(editGroupDetails())); action->setEnabled (!mGroupId.isNull() && isAdmin); uint32_t current_store_time = checkDelay(mInterface->getStoragePeriod(mGroupId))/86400 ; @@ -430,39 +478,39 @@ void GxsGroupFrameDialog::groupTreeCustomPopupMenu(QPoint point) QAction *actnn = NULL; QMenu *ctxMenu2 = contextMnu.addMenu(tr("Synchronise posts of last...")) ; - actnn = ctxMenu2->addAction(tr(" 5 days" ),this,SLOT(setSyncPostsDelay())) ; actnn->setData(QVariant( 5)) ; if(current_sync_time == 5) { actnn->setEnabled(false);actnn->setIcon(QIcon(":/images/start.png"));} - actnn = ctxMenu2->addAction(tr(" 2 weeks" ),this,SLOT(setSyncPostsDelay())) ; actnn->setData(QVariant( 15)) ; if(current_sync_time == 15) { actnn->setEnabled(false);actnn->setIcon(QIcon(":/images/start.png"));} - actnn = ctxMenu2->addAction(tr(" 1 month" ),this,SLOT(setSyncPostsDelay())) ; actnn->setData(QVariant( 30)) ; if(current_sync_time == 30) { actnn->setEnabled(false);actnn->setIcon(QIcon(":/images/start.png"));} - actnn = ctxMenu2->addAction(tr(" 3 months" ),this,SLOT(setSyncPostsDelay())) ; actnn->setData(QVariant( 90)) ; if(current_sync_time == 90) { actnn->setEnabled(false);actnn->setIcon(QIcon(":/images/start.png"));} - actnn = ctxMenu2->addAction(tr(" 6 months" ),this,SLOT(setSyncPostsDelay())) ; actnn->setData(QVariant(180)) ; if(current_sync_time ==180) { actnn->setEnabled(false);actnn->setIcon(QIcon(":/images/start.png"));} - actnn = ctxMenu2->addAction(tr(" 1 year " ),this,SLOT(setSyncPostsDelay())) ; actnn->setData(QVariant(365)) ; if(current_sync_time ==365) { actnn->setEnabled(false);actnn->setIcon(QIcon(":/images/start.png"));} - actnn = ctxMenu2->addAction(tr(" Indefinitly"),this,SLOT(setSyncPostsDelay())) ; actnn->setData(QVariant( 0)) ; if(current_sync_time == 0) { actnn->setEnabled(false);actnn->setIcon(QIcon(":/images/start.png"));} + actnn = ctxMenu2->addAction(tr(" 5 days" ),this,SLOT(setSyncPostsDelay())) ; actnn->setData(QVariant( 5)) ; if(current_sync_time == 5) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));} + actnn = ctxMenu2->addAction(tr(" 2 weeks" ),this,SLOT(setSyncPostsDelay())) ; actnn->setData(QVariant( 15)) ; if(current_sync_time == 15) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));} + actnn = ctxMenu2->addAction(tr(" 1 month" ),this,SLOT(setSyncPostsDelay())) ; actnn->setData(QVariant( 30)) ; if(current_sync_time == 30) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));} + actnn = ctxMenu2->addAction(tr(" 3 months" ),this,SLOT(setSyncPostsDelay())) ; actnn->setData(QVariant( 90)) ; if(current_sync_time == 90) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));} + actnn = ctxMenu2->addAction(tr(" 6 months" ),this,SLOT(setSyncPostsDelay())) ; actnn->setData(QVariant(180)) ; if(current_sync_time ==180) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));} + actnn = ctxMenu2->addAction(tr(" 1 year " ),this,SLOT(setSyncPostsDelay())) ; actnn->setData(QVariant(365)) ; if(current_sync_time ==365) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));} + actnn = ctxMenu2->addAction(tr(" Indefinitly"),this,SLOT(setSyncPostsDelay())) ; actnn->setData(QVariant( 0)) ; if(current_sync_time == 0) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));} ctxMenu2 = contextMnu.addMenu(tr("Store posts for at most...")) ; - actnn = ctxMenu2->addAction(tr(" 5 days" ),this,SLOT(setStorePostsDelay())) ; actnn->setData(QVariant( 5)) ; if(current_store_time == 5) { actnn->setEnabled(false);actnn->setIcon(QIcon(":/images/start.png"));} - actnn = ctxMenu2->addAction(tr(" 2 weeks" ),this,SLOT(setStorePostsDelay())) ; actnn->setData(QVariant( 15)) ; if(current_store_time == 15) { actnn->setEnabled(false);actnn->setIcon(QIcon(":/images/start.png"));} - actnn = ctxMenu2->addAction(tr(" 1 month" ),this,SLOT(setStorePostsDelay())) ; actnn->setData(QVariant( 30)) ; if(current_store_time == 30) { actnn->setEnabled(false);actnn->setIcon(QIcon(":/images/start.png"));} - actnn = ctxMenu2->addAction(tr(" 3 months" ),this,SLOT(setStorePostsDelay())) ; actnn->setData(QVariant( 90)) ; if(current_store_time == 90) { actnn->setEnabled(false);actnn->setIcon(QIcon(":/images/start.png"));} - actnn = ctxMenu2->addAction(tr(" 6 months" ),this,SLOT(setStorePostsDelay())) ; actnn->setData(QVariant(180)) ; if(current_store_time ==180) { actnn->setEnabled(false);actnn->setIcon(QIcon(":/images/start.png"));} - actnn = ctxMenu2->addAction(tr(" 1 year " ),this,SLOT(setStorePostsDelay())) ; actnn->setData(QVariant(365)) ; if(current_store_time ==365) { actnn->setEnabled(false);actnn->setIcon(QIcon(":/images/start.png"));} - actnn = ctxMenu2->addAction(tr(" Indefinitly"),this,SLOT(setStorePostsDelay())) ; actnn->setData(QVariant( 0)) ; if(current_store_time == 0) { actnn->setEnabled(false);actnn->setIcon(QIcon(":/images/start.png"));} + actnn = ctxMenu2->addAction(tr(" 5 days" ),this,SLOT(setStorePostsDelay())) ; actnn->setData(QVariant( 5)) ; if(current_store_time == 5) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));} + actnn = ctxMenu2->addAction(tr(" 2 weeks" ),this,SLOT(setStorePostsDelay())) ; actnn->setData(QVariant( 15)) ; if(current_store_time == 15) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));} + actnn = ctxMenu2->addAction(tr(" 1 month" ),this,SLOT(setStorePostsDelay())) ; actnn->setData(QVariant( 30)) ; if(current_store_time == 30) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));} + actnn = ctxMenu2->addAction(tr(" 3 months" ),this,SLOT(setStorePostsDelay())) ; actnn->setData(QVariant( 90)) ; if(current_store_time == 90) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));} + actnn = ctxMenu2->addAction(tr(" 6 months" ),this,SLOT(setStorePostsDelay())) ; actnn->setData(QVariant(180)) ; if(current_store_time ==180) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));} + actnn = ctxMenu2->addAction(tr(" 1 year " ),this,SLOT(setStorePostsDelay())) ; actnn->setData(QVariant(365)) ; if(current_store_time ==365) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));} + actnn = ctxMenu2->addAction(tr(" Indefinitly"),this,SLOT(setStorePostsDelay())) ; actnn->setData(QVariant( 0)) ; if(current_store_time == 0) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));} if (shareKeyType()) { - action = contextMnu.addAction(QIcon(IMAGE_SHARE), tr("Share publish permissions"), this, SLOT(sharePublishKey())); + action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_SHARE), tr("Share publish permissions..."), this, SLOT(sharePublishKey())); action->setEnabled(!mGroupId.isNull() && isPublisher); } if (getLinkType() != RetroShareLink::TYPE_UNKNOWN) { - action = contextMnu.addAction(QIcon(IMAGE_COPYLINK), tr("Copy RetroShare Link"), this, SLOT(copyGroupLink())); + action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COPYLINK), tr("Copy RetroShare Link"), this, SLOT(copyGroupLink())); action->setEnabled(!mGroupId.isNull()); } contextMnu.addSeparator(); - action = contextMnu.addAction(QIcon(":/images/message-mail-read.png"), tr("Mark all as read"), this, SLOT(markMsgAsRead())); + action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/message-mail-read.png"), tr("Mark all as read"), this, SLOT(markMsgAsRead())); action->setEnabled (!mGroupId.isNull() && isSubscribed); - action = contextMnu.addAction(QIcon(":/images/message-mail.png"), tr("Mark all as unread"), this, SLOT(markMsgAsUnread())); + action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/message-mail.png"), tr("Mark all as unread"), this, SLOT(markMsgAsUnread())); action->setEnabled (!mGroupId.isNull() && isSubscribed); /* Add special actions */ @@ -554,7 +602,8 @@ void GxsGroupFrameDialog::restoreGroupKeys(void) void GxsGroupFrameDialog::newGroup() { - GxsGroupDialog *dialog = createNewGroupDialog(mTokenQueue); + GxsGroupDialog *dialog = createNewGroupDialog(); + if (!dialog) { return; } @@ -581,8 +630,6 @@ void GxsGroupFrameDialog::groupSubscribe(bool subscribe) uint32_t token; mInterface->subscribeToGroup(token, mGroupId, subscribe); -// Replaced by meta data changed -// mTokenQueue->queueRequest(token, 0, RS_TOKREQ_ANSTYPE_ACK, TOKEN_TYPE_SUBSCRIBE_CHANGE); } void GxsGroupFrameDialog::showGroupDetails() @@ -591,7 +638,7 @@ void GxsGroupFrameDialog::showGroupDetails() return; } - GxsGroupDialog *dialog = createGroupDialog(mTokenQueue, mInterface->getTokenService(), GxsGroupDialog::MODE_SHOW, mGroupId); + GxsGroupDialog *dialog = createGroupDialog(GxsGroupDialog::MODE_SHOW, mGroupId); if (!dialog) { return; } @@ -606,7 +653,7 @@ void GxsGroupFrameDialog::editGroupDetails() return; } - GxsGroupDialog *dialog = createGroupDialog(mTokenQueue, mInterface->getTokenService(), GxsGroupDialog::MODE_EDIT, mGroupId); + GxsGroupDialog *dialog = createGroupDialog(GxsGroupDialog::MODE_EDIT, mGroupId); if (!dialog) { return; } @@ -640,7 +687,7 @@ bool GxsGroupFrameDialog::getCurrentGroupName(QString& name) void GxsGroupFrameDialog::markMsgAsRead() { - GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId, false); + GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId); if (msgWidget) { msgWidget->setAllMessagesRead(true); } @@ -648,7 +695,7 @@ void GxsGroupFrameDialog::markMsgAsRead() void GxsGroupFrameDialog::markMsgAsUnread() { - GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId, false); + GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId); if (msgWidget) { msgWidget->setAllMessagesRead(false); } @@ -698,7 +745,7 @@ void GxsGroupFrameDialog::loadComment(const RsGxsGroupId &grpId, const QVectorcommentLoad(grpId, msgv,most_recent_msgId); int index = ui->messageTabWidget->addTab(commentDialog, comments); - ui->messageTabWidget->setTabIcon(index, QIcon(IMAGE_COMMENT)); + ui->messageTabWidget->setTabIcon(index, FilesDefs::getIconFromQtResourcePath(IMAGE_COMMENT)); } ui->messageTabWidget->setCurrentWidget(commentDialog); @@ -726,7 +773,7 @@ bool GxsGroupFrameDialog::navigate(const RsGxsGroupId &groupId, const RsGxsMessa changedCurrentGroup(groupIdString); /* search exisiting tab */ - GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId, false); + GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId); if (!msgWidget) { return false; } @@ -738,17 +785,16 @@ bool GxsGroupFrameDialog::navigate(const RsGxsGroupId &groupId, const RsGxsMessa return msgWidget->navigate(msgId); } -GxsMessageFrameWidget *GxsGroupFrameDialog::messageWidget(const RsGxsGroupId &groupId, bool ownTab) +GxsMessageFrameWidget *GxsGroupFrameDialog::messageWidget(const RsGxsGroupId &groupId) { int tabCount = ui->messageTabWidget->count(); - for (int index = 0; index < tabCount; ++index) { + + for (int index = 0; index < tabCount; ++index) + { GxsMessageFrameWidget *childWidget = dynamic_cast(ui->messageTabWidget->widget(index)); - if (ownTab && mMessageWidget && childWidget == mMessageWidget) { - continue; - } - if (childWidget && childWidget->groupId() == groupId) { + + if (childWidget && childWidget->groupId() == groupId) return childWidget; - } } return NULL; @@ -757,9 +803,9 @@ GxsMessageFrameWidget *GxsGroupFrameDialog::messageWidget(const RsGxsGroupId &gr GxsMessageFrameWidget *GxsGroupFrameDialog::createMessageWidget(const RsGxsGroupId &groupId) { GxsMessageFrameWidget *msgWidget = createMessageFrameWidget(groupId); - if (!msgWidget) { + + if (!msgWidget) return NULL; - } int index = ui->messageTabWidget->addTab(msgWidget, msgWidget->groupName(true)); ui->messageTabWidget->setTabIcon(index, msgWidget->groupIcon()); @@ -784,40 +830,44 @@ GxsCommentDialog *GxsGroupFrameDialog::commentWidget(const RsGxsMessageId& msgId return NULL; } -void GxsGroupFrameDialog::changedCurrentGroup(const QString &groupId) +void GxsGroupFrameDialog::changedCurrentGroup(const QString& groupId) { if (mInFill) { return; } - if (groupId.isEmpty()) { - if (mMessageWidget) { - mMessageWidget->setGroupId(RsGxsGroupId()); - ui->messageTabWidget->setCurrentWidget(mMessageWidget); - } + if (groupId.isEmpty()) + { + auto w = currentWidget(); + + if(w) + w->setGroupId(RsGxsGroupId()); + return; } mGroupId = RsGxsGroupId(groupId.toStdString()); - if (mGroupId.isNull()) { + + if (mGroupId.isNull()) return; - } /* search exisiting tab */ - GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId, true); + GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId); - if (!msgWidget) { - if (mMessageWidget) { - /* not found, use standard tab */ - msgWidget = mMessageWidget; - msgWidget->setGroupId(mGroupId); - } else { - /* create new tab */ - msgWidget = createMessageWidget(mGroupId); + // check that we have at least one tab + + if(msgWidget) + ui->messageTabWidget->setCurrentWidget(msgWidget); + else + { + if(useTabs() || ui->messageTabWidget->count()==0) + { + msgWidget = createMessageWidget(RsGxsGroupId(groupId.toStdString())); + ui->messageTabWidget->setCurrentWidget(msgWidget); } + else + currentWidget()->setGroupId(mGroupId); } - - ui->messageTabWidget->setCurrentWidget(msgWidget); } void GxsGroupFrameDialog::groupTreeMiddleButtonClicked(QTreeWidgetItem *item) @@ -837,37 +887,31 @@ void GxsGroupFrameDialog::openGroupInNewTab(const RsGxsGroupId &groupId) } /* search exisiting tab */ - GxsMessageFrameWidget *msgWidget = messageWidget(groupId, true); - if (!msgWidget) { - /* not found, create new tab */ - msgWidget = createMessageWidget(groupId); - } + GxsMessageFrameWidget *msgWidget = createMessageWidget(groupId); ui->messageTabWidget->setCurrentWidget(msgWidget); } void GxsGroupFrameDialog::messageTabCloseRequested(int index) { - QWidget *widget = ui->messageTabWidget->widget(index); - if (!widget) { + if(ui->messageTabWidget->count() == 1) /* Don't close single tab */ return; - } - GxsMessageFrameWidget *msgWidget = dynamic_cast(widget); - if (msgWidget && msgWidget == mMessageWidget) { - /* Don't close single tab */ - return; - } + GxsMessageFrameWidget *msgWidget = dynamic_cast(ui->messageTabWidget->widget(index)); + delete msgWidget ; +} - delete(widget); +GxsMessageFrameWidget *GxsGroupFrameDialog::currentWidget() const +{ + return dynamic_cast(ui->messageTabWidget->widget(ui->messageTabWidget->currentIndex())); } void GxsGroupFrameDialog::messageTabChanged(int index) { GxsMessageFrameWidget *msgWidget = dynamic_cast(ui->messageTabWidget->widget(index)); - if (!msgWidget) { + + if (!msgWidget) return; - } ui->groupTreeWidget->activateId(QString::fromStdString(msgWidget->groupId().toStdString()), false); } @@ -904,30 +948,30 @@ void GxsGroupFrameDialog::messageTabWaitingChanged(QWidget *widget) } ///***** INSERT GROUP LISTS *****/ -void GxsGroupFrameDialog::groupInfoToGroupItemInfo(const RsGroupMetaData &groupInfo, GroupItemInfo &groupItemInfo, const RsUserdata */*userdata*/) +void GxsGroupFrameDialog::groupInfoToGroupItemInfo(const RsGxsGenericGroupData *groupInfo, GroupItemInfo &groupItemInfo) { - groupItemInfo.id = QString::fromStdString(groupInfo.mGroupId.toStdString()); - groupItemInfo.name = QString::fromUtf8(groupInfo.mGroupName.c_str()); - groupItemInfo.popularity = groupInfo.mPop; - groupItemInfo.lastpost = QDateTime::fromTime_t(groupInfo.mLastPost); - groupItemInfo.subscribeFlags = groupInfo.mSubscribeFlags; - groupItemInfo.publishKey = IS_GROUP_PUBLISHER(groupInfo.mSubscribeFlags) ; - groupItemInfo.adminKey = IS_GROUP_ADMIN(groupInfo.mSubscribeFlags) ; - groupItemInfo.max_visible_posts = groupInfo.mVisibleMsgCount ; + groupItemInfo.id = QString::fromStdString(groupInfo->mMeta.mGroupId.toStdString()); + groupItemInfo.name = QString::fromUtf8(groupInfo->mMeta.mGroupName.c_str()); + groupItemInfo.popularity = groupInfo->mMeta.mPop; + groupItemInfo.lastpost = QDateTime::fromTime_t(groupInfo->mMeta.mLastPost); + groupItemInfo.subscribeFlags = groupInfo->mMeta.mSubscribeFlags; + groupItemInfo.publishKey = IS_GROUP_PUBLISHER(groupInfo->mMeta.mSubscribeFlags) ; + groupItemInfo.adminKey = IS_GROUP_ADMIN(groupInfo->mMeta.mSubscribeFlags) ; + groupItemInfo.max_visible_posts = groupInfo->mMeta.mVisibleMsgCount ; #if TOGXS if (groupInfo.mGroupFlags & RS_DISTRIB_AUTHEN_REQ) { groupItemInfo.name += " (" + tr("AUTHD") + ")"; - groupItemInfo.icon = QIcon(IMAGE_GROUPAUTHD); + groupItemInfo.icon = FilesDefs::getIconFromQtResourcePath(IMAGE_GROUPAUTHD); } else #endif { - groupItemInfo.icon = QIcon(icon(ICON_DEFAULT)); + groupItemInfo.icon = FilesDefs::getIconFromQtResourcePath(icon(ICON_DEFAULT)); } } -void GxsGroupFrameDialog::insertGroupsData(const std::map &groupList, const RsUserdata *userdata) +void GxsGroupFrameDialog::insertGroupsData(const std::list& groupList) { if (!mInitialized) { return; @@ -941,57 +985,44 @@ void GxsGroupFrameDialog::insertGroupsData(const std::map otherList; std::multimap popMap; - for (auto it = groupList.begin(); it != groupList.end(); ++it) { + for (auto& g:groupList) + { /* sort it into Publish (Own), Subscribed, Popular and Other */ - uint32_t flags = it->second.mSubscribeFlags; + uint32_t flags = g->mMeta.mSubscribeFlags; GroupItemInfo groupItemInfo; - groupInfoToGroupItemInfo(it->second, groupItemInfo, userdata); + groupInfoToGroupItemInfo(g, groupItemInfo); if (IS_GROUP_SUBSCRIBED(flags)) { if (IS_GROUP_ADMIN(flags)) - { adminList.push_back(groupItemInfo); - } else - { - /* subscribed group */ - subList.push_back(groupItemInfo); - } + subList.push_back(groupItemInfo); /* subscribed group */ } else - { - //popMap.insert(std::make_pair(it->mPop, groupItemInfo)); /* rate the others by popularity */ - popMap.insert(std::make_pair(it->second.mLastPost, groupItemInfo)); /* rate the others by time of last post */ - } + { + popMap.insert(std::make_pair(g->mMeta.mLastPost, groupItemInfo)); /* rate the others by time of last post */ + } } /* iterate backwards through popMap - take the top 5 or 10% of list */ uint32_t popCount = 5; if (popCount < popMap.size() / 10) - { popCount = popMap.size() / 10; - } uint32_t i = 0; std::multimap::reverse_iterator rit; - //uint32_t popLimit = 0; - //for(rit = popMap.rbegin(); ((rit != popMap.rend()) && (i < popCount)); ++rit, ++i) ; - //if (rit != popMap.rend()) { - // popLimit = rit->first; - //} - for (rit = popMap.rbegin(); rit != popMap.rend(); ++rit,++i) { - //if (rit->second.popularity > (int) popLimit) { + for (rit = popMap.rbegin(); rit != popMap.rend(); ++rit,++i) if(i < popCount) popList.append(rit->second); else otherList.append(rit->second); - } /* now we can add them in as a tree! */ ui->groupTreeWidget->fillGroupItems(mYourGroups, adminList); + mYourGroups->setText(2, QString::number(mYourGroups->childCount())); ui->groupTreeWidget->fillGroupItems(mSubscribedGroups, subList); mSubscribedGroups->setText(2, QString::number(mSubscribedGroups->childCount())); // 1 COLUMN_UNREAD 2 COLUMN_POPULARITY ui->groupTreeWidget->fillGroupItems(mPopularGroups, popList); @@ -1002,20 +1033,32 @@ void GxsGroupFrameDialog::insertGroupsData(const std::mapgroupTreeWidget->activateId(QString::fromStdString(mGroupId.toStdString()), true)) { + if (!ui->groupTreeWidget->activateId(QString::fromStdString(mGroupId.toStdString()), true)) mGroupId.clear(); - } updateMessageSummaryList(RsGxsGroupId()); } void GxsGroupFrameDialog::updateMessageSummaryList(RsGxsGroupId groupId) +{ + // groupId.isNull() means that we need to update all groups so we clear up the list of groups to update. + + if(!groupId.isNull()) + mGroupIdsSummaryToUpdate.insert(groupId); + else + mGroupIdsSummaryToUpdate.clear(); + + mShouldUpdateMessageSummaryList = true; +} + +void GxsGroupFrameDialog::updateMessageSummaryListReal(RsGxsGroupId groupId) { if (!mInitialized) { return; } - if (groupId.isNull()) { + if (groupId.isNull()) + { QTreeWidgetItem *items[2] = { mYourGroups, mSubscribedGroups }; for (int item = 0; item < 2; ++item) { int child; @@ -1023,197 +1066,137 @@ void GxsGroupFrameDialog::updateMessageSummaryList(RsGxsGroupId groupId) for (child = 0; child < childCount; ++child) { QTreeWidgetItem *childItem = items[item]->child(child); QString childId = ui->groupTreeWidget->itemId(childItem); - if (childId.isEmpty()) { + if (childId.isEmpty()) continue; - } - requestGroupStatistics(RsGxsGroupId(childId.toLatin1().constData())); + updateGroupStatistics(RsGxsGroupId(childId.toLatin1().constData())); } } - } else { - requestGroupStatistics(groupId); } + else + updateGroupStatistics(groupId); } /*********************** **** **** **** ***********************/ /** Request / Response of Data ********************************/ /*********************** **** **** **** ***********************/ -void GxsGroupFrameDialog::requestGroupSummary() +void GxsGroupFrameDialog::updateGroupSummary() { - mStateHelper->setLoading(TOKEN_TYPE_GROUP_SUMMARY, true); - -#ifdef DEBUG_GROUPFRAMEDIALOG - std::cerr << "GxsGroupFrameDialog::requestGroupSummary()"; - std::cerr << std::endl; -#endif - - mTokenQueue->cancelActiveRequestTokens(TOKEN_TYPE_GROUP_SUMMARY); - - RsTokReqOptions opts; - opts.mReqType = requestGroupSummaryType(); - - uint32_t token; - mTokenQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_SUMMARY, opts, TOKEN_TYPE_GROUP_SUMMARY); -} - -void GxsGroupFrameDialog::loadGroupSummaryToken(const uint32_t &token, std::list &groupInfo, RsUserdata *&/*userdata*/) -{ - /* Default implementation for request type GXS_REQUEST_TYPE_GROUP_META */ - mInterface->getGroupSummary(token, groupInfo); -} - -void GxsGroupFrameDialog::loadGroupSummary(const uint32_t &token) -{ -#ifdef DEBUG_GROUPFRAMEDIALOG - std::cerr << "GxsGroupFrameDialog::loadGroupSummary()"; - std::cerr << std::endl; -#endif - - std::list groupInfo; - RsUserdata *userdata = NULL; - loadGroupSummaryToken(token, groupInfo, userdata); - - mCachedGroupMetas.clear(); - for(auto it(groupInfo.begin());it!=groupInfo.end();++it) - mCachedGroupMetas[(*it).mGroupId] = *it; - - insertGroupsData(mCachedGroupMetas, userdata); - updateSearchResults(); - - mStateHelper->setLoading(TOKEN_TYPE_GROUP_SUMMARY, false); - - if (userdata) { - delete(userdata); - } - - if (!mNavigatePendingGroupId.isNull()) { - /* Navigate pending */ - navigate(mNavigatePendingGroupId, mNavigatePendingMsgId); - - mNavigatePendingGroupId.clear(); - mNavigatePendingMsgId.clear(); - } -} - -/*********************** **** **** **** ***********************/ -/*********************** **** **** **** ***********************/ - -//void GxsGroupFrameDialog::acknowledgeSubscribeChange(const uint32_t &token) -//{ -//#ifdef DEBUG_GROUPFRAMEDIALOG -// std::cerr << "GxsGroupFrameDialog::acknowledgeSubscribeChange()"; -// std::cerr << std::endl; -//#endif - -// RsGxsGroupId groupId; -// mInterface->acknowledgeGrp(token, groupId); - -// fillComplete(); -//} - -/*********************** **** **** **** ***********************/ -/*********************** **** **** **** ***********************/ - -//void GxsGroupFrameDialog::requestGroupSummary_CurrentGroup(const RsGxsGroupId &groupId) -//{ -// RsTokReqOptions opts; -// opts.mReqType = GXS_REQUEST_TYPE_GROUP_META; - -// std::list grpIds; -// grpIds.push_back(groupId); - -// std::cerr << "GxsGroupFrameDialog::requestGroupSummary_CurrentGroup(" << groupId << ")"; -// std::cerr << std::endl; - -// uint32_t token; -// mInteface->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_SUMMARY, opts, grpIds, TOKEN_TYPE_CURRENTGROUP); -//} - -//void GxsGroupFrameDialog::loadGroupSummary_CurrentGroup(const uint32_t &token) -//{ -// std::cerr << "GxsGroupFrameDialog::loadGroupSummary_CurrentGroup()"; -// std::cerr << std::endl; - -// std::list groupInfo; -// rsGxsForums->getGroupSummary(token, groupInfo); - -// if (groupInfo.size() == 1) -// { -// RsGroupMetaData fi = groupInfo.front(); -// mSubscribeFlags = fi.mSubscribeFlags; -// } -// else -// { -// resetData(); -// std::cerr << "GxsGroupFrameDialog::loadGroupSummary_CurrentGroup() ERROR Invalid Number of Groups..."; -// std::cerr << std::endl; -// } - -// setValid(true); -//} - -/*********************** **** **** **** ***********************/ -/*********************** **** **** **** ***********************/ - -void GxsGroupFrameDialog::requestGroupStatistics(const RsGxsGroupId &groupId) -{ - uint32_t token; - mTokenService->requestGroupStatistic(token, groupId); - mTokenQueue->queueRequest(token, 0, RS_TOKREQ_ANSTYPE_ACK, TOKEN_TYPE_STATISTICS); -} - -void GxsGroupFrameDialog::loadGroupStatistics(const uint32_t &token) -{ - GxsGroupStatistic stats; - mInterface->getGroupStatistic(token, stats); - - QTreeWidgetItem *item = ui->groupTreeWidget->getItemFromId(QString::fromStdString(stats.mGrpId.toStdString())); - if (!item) { - return; - } - - ui->groupTreeWidget->setUnreadCount(item, mCountChildMsgs ? (stats.mNumThreadMsgsUnread + stats.mNumChildMsgsUnread) : stats.mNumThreadMsgsUnread); - - getUserNotify()->updateIcon(); -} - -/*********************** **** **** **** ***********************/ -/*********************** **** **** **** ***********************/ - -void GxsGroupFrameDialog::loadRequest(const TokenQueue *queue, const TokenRequest &req) -{ -#ifdef DEBUG_GROUPFRAMEDIALOG - std::cerr << "GxsGroupFrameDialog::loadRequest() UserType: " << req.mUserType; - std::cerr << std::endl; -#endif - - if (queue == mTokenQueue) + RsThread::async([this]() { - /* now switch on req */ - switch(req.mUserType) + auto groupInfo = new std::list() ; + + if(!getGroupData(*groupInfo)) { - case TOKEN_TYPE_GROUP_SUMMARY: - loadGroupSummary(req.mToken); - break; - -// case TOKEN_TYPE_SUBSCRIBE_CHANGE: -// acknowledgeSubscribeChange(req.mToken); -// break; - -// case TOKEN_TYPE_CURRENTGROUP: -// loadGroupSummary_CurrentGroup(req.mToken); -// break; - - case TOKEN_TYPE_STATISTICS: - loadGroupStatistics(req.mToken); - break; - - default: - std::cerr << "GxsGroupFrameDialog::loadRequest() ERROR: INVALID TYPE"; - std::cerr << std::endl; + std::cerr << __PRETTY_FUNCTION__ << " failed to collect group info." << std::endl; + delete groupInfo; + return; } - } + if(groupInfo->empty()) + { + std::cerr << __PRETTY_FUNCTION__ << " no group info collected." << std::endl; + delete groupInfo; + return; + } + + RsQThreadUtils::postToObject( [this,groupInfo]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete, note that + * Qt::QueuedConnection is important! + */ + + insertGroupsData(*groupInfo); + updateSearchResults(); + + mStateHelper->setLoading(TOKEN_TYPE_GROUP_SUMMARY, false); + + if (!mNavigatePendingGroupId.isNull()) { + /* Navigate pending */ + navigate(mNavigatePendingGroupId, mNavigatePendingMsgId); + + mNavigatePendingGroupId.clear(); + mNavigatePendingMsgId.clear(); + } + + // update the local cache in order to avoid re-asking the data when the UI wants it (this happens on ::show() for instance) + + mCachedGroupMetas.clear(); + + // now delete the data that is not used anymore + + for(auto& g:*groupInfo) + { + mCachedGroupMetas[g->mMeta.mGroupId] = g->mMeta; + delete g; + } + + delete groupInfo; + + }, this ); + }); +} + +/*********************** **** **** **** ***********************/ +/*********************** **** **** **** ***********************/ + +void GxsGroupFrameDialog::updateGroupStatistics(const RsGxsGroupId &groupId) +{ + mGroupStatisticsToUpdate.insert(groupId); + mShouldUpdateGroupStatistics = true; +} + +void GxsGroupFrameDialog::updateGroupStatisticsReal(const RsGxsGroupId &groupId) +{ + RsThread::async([this,groupId]() + { + GxsGroupStatistic stats; + + if(! getGroupStatistics(groupId, stats)) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to collect group statistics for group " << groupId << std::endl; + return; + } + + RsQThreadUtils::postToObject( [this,stats, groupId]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete, note that + * Qt::QueuedConnection is important! + */ + + QTreeWidgetItem *item = ui->groupTreeWidget->getItemFromId(QString::fromStdString(stats.mGrpId.toStdString())); + if (!item) + return; + + ui->groupTreeWidget->setUnreadCount(item, mCountChildMsgs ? (stats.mNumThreadMsgsUnread + stats.mNumChildMsgsUnread) : stats.mNumThreadMsgsUnread); + mCachedGroupStats[groupId] = stats; + + getUserNotify()->updateIcon(); + + }, this ); + }); +} + +void GxsGroupFrameDialog::getServiceStatistics(GxsServiceStatistic& stats) const +{ + stats = GxsServiceStatistic(); // clears everything + + for(auto it: mCachedGroupStats) + { + const GxsGroupStatistic& s(it.second); + + stats.mNumMsgs += s.mNumMsgs; + stats.mNumGrps += 1; + stats.mSizeOfMsgs += s.mTotalSizeOfMsgs; + stats.mNumThreadMsgsNew += s.mNumThreadMsgsNew; + stats.mNumThreadMsgsUnread += s.mNumThreadMsgsUnread; + stats.mNumChildMsgsNew += s.mNumChildMsgsNew ; + stats.mNumChildMsgsUnread += s.mNumChildMsgsUnread ; + } } TurtleRequestId GxsGroupFrameDialog::distantSearch(const QString& search_string) // this should be overloaded in the child class @@ -1232,7 +1215,7 @@ void GxsGroupFrameDialog::searchNetwork(const QString& search_string) if(request_id == 0) return ; - mSearchGroupsItems[request_id] = ui->groupTreeWidget->addSearchItem(tr("Search for")+ " \"" + search_string + "\"",(uint32_t)request_id,QIcon(icon(ICON_SEARCH))); + mSearchGroupsItems[request_id] = ui->groupTreeWidget->addSearchItem(tr("Search for")+ " \"" + search_string + "\"",(uint32_t)request_id,FilesDefs::getIconFromQtResourcePath(icon(ICON_SEARCH))); } void GxsGroupFrameDialog::distantRequestGroupData() diff --git a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.h b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.h index 29a22d781..eaf1d0d34 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.h +++ b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.h @@ -21,15 +21,15 @@ #ifndef _GXSGROUPFRAMEDIALOG_H #define _GXSGROUPFRAMEDIALOG_H +#include + #include "gui/gxs/RsGxsUpdateBroadcastPage.h" -#include "RsAutoUpdatePage.h" #include "gui/RetroShareLink.h" #include "gui/settings/rsharesettings.h" #include "util/RsUserdata.h" #include -#include "util/TokenQueue.h" #include "GxsIdTreeWidgetItem.h" #include "GxsGroupDialog.h" @@ -44,7 +44,7 @@ class UIStateHelper; struct RsGxsCommentService; class GxsCommentDialog; -class GxsGroupFrameDialog : public RsGxsUpdateBroadcastPage, public TokenResponse +class GxsGroupFrameDialog : public MainPage { Q_OBJECT @@ -76,15 +76,15 @@ public: bool navigate(const RsGxsGroupId &groupId, const RsGxsMessageId& msgId); - // Callback for all Loads. - virtual void loadRequest(const TokenQueue *queue, const TokenRequest &req); - virtual QString getHelpString() const =0; virtual void getGroupList(std::map &groups) ; + void getServiceStatistics(GxsServiceStatistic& stats) const ; + protected: - virtual void showEvent(QShowEvent *event); + virtual void showEvent(QShowEvent *event) override; + virtual void paintEvent(QPaintEvent *pe) override; virtual void updateDisplay(bool complete); const RsGxsGroupId &groupId() { return mGroupId; } @@ -93,10 +93,21 @@ protected: bool getCurrentGroupName(QString& name); virtual RetroShareLink::enumType getLinkType() = 0; virtual GroupFrameSettings::Type groupFrameSettingsType() { return GroupFrameSettings::Nothing; } - virtual void groupInfoToGroupItemInfo(const RsGroupMetaData &groupInfo, GroupItemInfo &groupItemInfo, const RsUserdata *userdata); + virtual void groupInfoToGroupItemInfo(const RsGxsGenericGroupData *groupInfo, GroupItemInfo &groupItemInfo); virtual void checkRequestGroup(const RsGxsGroupId& /* grpId */) {} // overload this one in order to retrieve full group data when the group is browsed void updateMessageSummaryList(RsGxsGroupId groupId); + void updateGroupStatistics(const RsGxsGroupId &groupId); + + virtual const std::set getSearchRequests() const { return std::set(); } // overload this for subclasses that provide distant search + + // These two need to be overloaded by subsclasses, possibly calling the blocking API, since they are used asynchroneously. + + virtual bool getGroupData(std::list& groupInfo) =0; + virtual bool getGroupStatistics(const RsGxsGroupId& groupId,GxsGroupStatistic& stat) =0; + + void updateGroupStatisticsReal(const RsGxsGroupId &groupId); + void updateMessageSummaryListReal(RsGxsGroupId groupId); private slots: void todo(); @@ -144,14 +155,16 @@ private: virtual QString settingsGroupName() = 0; virtual TurtleRequestId distantSearch(const QString& search_string) ; - virtual GxsGroupDialog *createNewGroupDialog(TokenQueue *tokenQueue) = 0; - virtual GxsGroupDialog *createGroupDialog(TokenQueue *tokenQueue, RsTokenService *tokenService, GxsGroupDialog::Mode mode, RsGxsGroupId groupId) = 0; + virtual GxsGroupDialog *createNewGroupDialog() = 0; + virtual GxsGroupDialog *createGroupDialog(GxsGroupDialog::Mode mode, RsGxsGroupId groupId) = 0; virtual int shareKeyType() = 0; virtual GxsMessageFrameWidget *createMessageFrameWidget(const RsGxsGroupId &groupId) = 0; virtual void groupTreeCustomActions(RsGxsGroupId /*grpId*/, int /*subscribeFlags*/, QList &/*actions*/) {} virtual RsGxsCommentService *getCommentService() { return NULL; } virtual QWidget *createCommentHeaderWidget(const RsGxsGroupId &/*grpId*/, const RsGxsMessageId &/*msgId*/) { return NULL; } - virtual bool getDistantSearchResults(TurtleRequestId /* id */, std::map& /* group_infos */){ return false ;} + virtual bool getDistantSearchResults(TurtleRequestId /* id */, std::map& /* group_infos */){ return false ;} + virtual void clearDistantSearchResults(TurtleRequestId /* id */) {} + virtual RsGxsGenericGroupData *getDistantSearchResultGroupData(const RsGxsGroupId& group_id){ return nullptr ;} void initUi(); @@ -161,42 +174,37 @@ private: void processSettings(bool load); // New Request/Response Loading Functions. - void insertGroupsData(const std::map &groupList, const RsUserdata *userdata); + void insertGroupsData(const std::list &groupList); + + //void requestGroupSummary(); + void updateGroupSummary(); + void loadGroupSummary(const std::list &groupInfo); - void requestGroupSummary(); - void loadGroupSummary(const uint32_t &token); virtual uint32_t requestGroupSummaryType() { return GXS_REQUEST_TYPE_GROUP_META; } // request only meta data - virtual void loadGroupSummaryToken(const uint32_t &token, std::list &groupInfo, RsUserdata* &userdata); // use with requestGroupSummaryType - - void requestGroupStatistics(const RsGxsGroupId &groupId); - void loadGroupStatistics(const uint32_t &token); // subscribe/unsubscribe ack. -// void acknowledgeSubscribeChange(const uint32_t &token); - GxsMessageFrameWidget *messageWidget(const RsGxsGroupId &groupId, bool ownTab); + GxsMessageFrameWidget *messageWidget(const RsGxsGroupId &groupId); GxsMessageFrameWidget *createMessageWidget(const RsGxsGroupId &groupId); GxsCommentDialog *commentWidget(const RsGxsMessageId &msgId); -// void requestGroupSummary_CurrentGroup(const RsGxsGroupId &groupId); -// void loadGroupSummary_CurrentGroup(const uint32_t &token); - protected: - void updateSearchResults(); + void updateSearchResults(const TurtleRequestId &sid); + void updateSearchResults(); // update all searches bool mCountChildMsgs; // Count unread child messages? private: + GxsMessageFrameWidget *currentWidget() const; + bool useTabs(); + bool mInitialized; bool mInFill; bool mDistSyncAllowed; QString mSettingsName; RsGxsGroupId mGroupId; RsGxsIfaceHelper *mInterface; - RsTokenService *mTokenService; - TokenQueue *mTokenQueue; - GxsMessageFrameWidget *mMessageWidget; QTreeWidgetItem *mYourGroups; QTreeWidgetItem *mSubscribedGroups; @@ -206,12 +214,23 @@ private: RsGxsGroupId mNavigatePendingGroupId; RsGxsMessageId mNavigatePendingMsgId; + // Message summary list update + + bool mShouldUpdateMessageSummaryList ; // whether we should update the counting for groups. This takes some CPU so we only do it when needed. + std::set mGroupIdsSummaryToUpdate; + + // GroupStatistics update + bool mShouldUpdateGroupStatistics; + rstime_t mLastGroupStatisticsUpdateTs; + std::set mGroupStatisticsToUpdate; + UIStateHelper *mStateHelper; /** Qt Designer generated object */ Ui::GxsGroupFrameDialog *ui; std::map mCachedGroupMetas; + std::map mCachedGroupStats; std::map mSearchGroupsItems ; std::map > mKnownGroups; diff --git a/retroshare-gui/src/gui/gxs/GxsGroupShareKey.cpp b/retroshare-gui/src/gui/gxs/GxsGroupShareKey.cpp index 7b3ac7fee..dfcafaaf2 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupShareKey.cpp +++ b/retroshare-gui/src/gui/gxs/GxsGroupShareKey.cpp @@ -29,6 +29,7 @@ #include #include "gui/common/PeerDefs.h" +#include "gui/common/FilesDefs.h" GroupShareKey::GroupShareKey(QWidget *parent, const RsGxsGroupId &grpId, int grpType) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint), mGrpId(grpId), mGrpType(grpType) @@ -36,7 +37,7 @@ GroupShareKey::GroupShareKey(QWidget *parent, const RsGxsGroupId &grpId, int grp ui = new Ui::ShareKey(); ui->setupUi(this); - ui->headerFrame->setHeaderImage(QPixmap(":/images/user/agt_forum64.png")); + ui->headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/images/user/agt_forum64.png")); ui->headerFrame->setHeaderText(tr("Share")); connect( ui->buttonBox, SIGNAL(accepted()), this, SLOT(shareKey())); @@ -76,14 +77,14 @@ void GroupShareKey::setTyp() if (!rsGxsChannels) return; - ui->headerFrame->setHeaderImage(QPixmap(":/images/channels.png")); + ui->headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/channels.png")); ui->headerFrame->setHeaderText(tr("Share channel publish permissions")); ui->sharekeyinfo_label->setText(tr("You can allow your friends to publish in your channel, or send the publish permissions to another Retroshare instance of yours. Select the friends which you want to be allowed to publish in this channel. Note: it is currently not possible to revoke channel publish permissions.")); } else if(mGrpType == FORUM_KEY_SHARE) { - ui->headerFrame->setHeaderImage(QPixmap(":/images/konversation64.png")); + ui->headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/forums.png")); ui->headerFrame->setHeaderText(tr("Share forum admin permissions")); ui->sharekeyinfo_label->setText(tr("You can let your friends know about your forum by sharing it with them. Select the friends with which you want to share your forum.")); @@ -93,9 +94,9 @@ void GroupShareKey::setTyp() if (!rsPosted) return; - ui->headerFrame->setHeaderImage(QPixmap(":/images/posted_64.png")); - ui->headerFrame->setHeaderText(tr("Share topic admin permissions")); - ui->sharekeyinfo_label->setText(tr("You can allow your friends to edit the topic. Select them in the list below. Note: it is not possible to revoke Posted admin permissions.")); + ui->headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/posted.png")); + ui->headerFrame->setHeaderText(tr("Share board admin permissions")); + ui->sharekeyinfo_label->setText(tr("You can allow your friends to edit the board. Select them in the list below. Note: it is not possible to revoke Board admin permissions.")); } else diff --git a/retroshare-gui/src/gui/gxs/GxsIdChooser.cpp b/retroshare-gui/src/gui/gxs/GxsIdChooser.cpp index e4e20dfd8..6235c025f 100644 --- a/retroshare-gui/src/gui/gxs/GxsIdChooser.cpp +++ b/retroshare-gui/src/gui/gxs/GxsIdChooser.cpp @@ -22,6 +22,7 @@ #include "GxsIdDetails.h" #include "RsGxsUpdateBroadcastBase.h" #include "gui/Identity/IdEditDialog.h" +#include "gui/common/FilesDefs.h" #include "util/misc.h" #include @@ -51,8 +52,8 @@ GxsIdChooser::GxsIdChooser(QWidget *parent) : QComboBox(parent), mFlags(IDCHOOSER_ANON_DEFAULT) { - mBase = new RsGxsUpdateBroadcastBase(rsIdentity, this); - connect(mBase, SIGNAL(fillDisplay(bool)), this, SLOT(fillDisplay(bool))); +// mBase = new RsGxsUpdateBroadcastBase(rsIdentity, this); +// connect(mBase, SIGNAL(fillDisplay(bool)), this, SLOT(fillDisplay(bool))); /* Initialize ui */ setSizeAdjustPolicy(QComboBox::AdjustToContents); @@ -94,7 +95,8 @@ void GxsIdChooser::fillDisplay(bool complete) void GxsIdChooser::showEvent(QShowEvent *event) { - mBase->showEvent(event); +// mBase->showEvent(event); + updateDisplay(true); QComboBox::showEvent(event); } @@ -161,7 +163,7 @@ static void loadPrivateIdsCallback(GxsIdDetailsType type, const RsIdentityDetail break; case GXS_ID_DETAILS_TYPE_BANNED: - icons.push_back(QIcon(BANNED_ICON)) ; + icons.push_back(FilesDefs::getIconFromQtResourcePath(BANNED_ICON)) ; break; } @@ -220,50 +222,11 @@ bool GxsIdChooser::isInConstraintSet(const RsGxsId& id) const void GxsIdChooser::setEntryEnabled(int indx,bool /*enabled*/) { removeItem(indx) ; - -#ifdef TO_REMOVE -// bool disable = !enabled ; -// -// QSortFilterProxyModel* model = qobject_cast(QComboBox::model()); -// //QStandardItem* item = model->item(index); -// -// QModelIndex ii = model->index(indx,0); -// -// // visually disable by greying out - works only if combobox has been painted already and palette returns the wanted color -// //model->setFlags(ii,disable ? (model->flags(ii) & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)) : (Qt::ItemIsSelectable|Qt::ItemIsEnabled)); -// -// uint32_t v = enabled?(1|32):(0); -// -// std::cerr << "GxsIdChooser::setEnabledEntry: i=" << indx << ", v=" << v << std::endl; -// -// // clear item data in order to use default color -// //model->setData(ii,disable ? (QComboBox::palette().color(QPalette::Disabled, QPalette::Text)) : QVariant(), Qt::TextColorRole); -// model->setData(ii,QVariant(v),Qt::UserRole-1) ; -// -// std::cerr << "model data after operation: " << model->data(ii,Qt::UserRole-1).toUInt() << std::endl; -#endif } uint32_t GxsIdChooser::countEnabledEntries() const { return count() ; - -#ifdef TO_REMOVE -// uint32_t res = 0 ; -// QSortFilterProxyModel* model = qobject_cast(QComboBox::model()); -// -// for(uint32_t i=0;irowCount();++i) -// { -// QModelIndex ii = model->index(i,0); -// uint32_t v = model->data(ii,Qt::UserRole-1).toUInt() ; -// -// std::cerr << "GxsIdChooser::countEnabledEntries(): i=" << i << ", v=" << v << std::endl; -// if(v > 0) -// ++res ; -// } -// -// return res ; -#endif } void GxsIdChooser::loadPrivateIds() @@ -323,7 +286,7 @@ void GxsIdChooser::loadPrivateIds() QString str = tr("Create new Identity"); QString id = ""; - addItem(QIcon(":/images/identity/identity_create_32.png"), str, id); + addItem(FilesDefs::getIconFromQtResourcePath(":/icons/png/add-identity.png"), str, id); setItemData(count() - 1, QString("%1_%2").arg(TYPE_CREATE_ID).arg(str), ROLE_SORT); setItemData(count() - 1, TYPE_CREATE_ID, ROLE_TYPE); diff --git a/retroshare-gui/src/gui/gxs/GxsIdChooser.h b/retroshare-gui/src/gui/gxs/GxsIdChooser.h index dbd9fb91f..3cee770eb 100644 --- a/retroshare-gui/src/gui/gxs/GxsIdChooser.h +++ b/retroshare-gui/src/gui/gxs/GxsIdChooser.h @@ -86,7 +86,7 @@ private: uint32_t mAllowedCount ; std::set mConstraintIdsSet ; // leave empty if all allowed - RsGxsUpdateBroadcastBase *mBase; +// RsGxsUpdateBroadcastBase *mBase; }; #endif diff --git a/retroshare-gui/src/gui/gxs/GxsIdDetails.cpp b/retroshare-gui/src/gui/gxs/GxsIdDetails.cpp index a7627a967..a7d050dd5 100644 --- a/retroshare-gui/src/gui/gxs/GxsIdDetails.cpp +++ b/retroshare-gui/src/gui/gxs/GxsIdDetails.cpp @@ -18,27 +18,30 @@ * * *******************************************************************************/ -#include -#include -#include -#include - -#include -#include -#include "gui/common/AvatarDialog.h" #include "GxsIdDetails.h" + +#include "gui/common/AvatarDialog.h" +#include "gui/common/FilesDefs.h" #include "retroshare-gui/RsAutoUpdatePage.h" #include +#include + +#include +#include +#include +#include +#include +#include #include -#include +#include /* Images for tag icons */ #define IMAGE_LOADING ":/images/folder-draft.png" -#define IMAGE_PGPKNOWN ":/images/contact.png" +#define IMAGE_PGPKNOWN ":/icons/png/profile.png" #define IMAGE_PGPUNKNOWN ":/images/tags/pgp-unknown.png" -#define IMAGE_ANON ":/images/tags/anon.png" +#define IMAGE_ANON ":/icons/png/anonymous.png" #define IMAGE_BANNED ":/icons/biohazard_red.png" #define IMAGE_DEV_AMBASSADOR ":/images/tags/dev-ambassador.png" @@ -373,10 +376,10 @@ static bool findTagIcon(int tag_class, int /*tag_type*/, QIcon &icon) { default: case 0: - icon = QIcon(IMAGE_DEV_AMBASSADOR); + icon = FilesDefs::getIconFromQtResourcePath(IMAGE_DEV_AMBASSADOR); break; case 1: - icon = QIcon(IMAGE_DEV_CONTRIBUTOR); + icon = FilesDefs::getIconFromQtResourcePath(IMAGE_DEV_CONTRIBUTOR); break; } return true; @@ -935,7 +938,7 @@ QPixmap GxsIdDetails::drawIdentIcon( QString hash, quint16 width, bool rotate) QString GxsIdDetails::getLoadingText(const RsGxsId &id) { - return QString("%1... %2").arg(QApplication::translate("GxsIdDetails", "Loading"), QString::fromStdString(id.toStdString().substr(0, 5))); + return QString("%1... %2").arg(QApplication::translate("GxsIdDetails", "Loading..."), QString::fromStdString(id.toStdString().substr(0, 5))); } QString GxsIdDetails::getFailedText(const RsGxsId &id) @@ -972,7 +975,7 @@ QString GxsIdDetails::getNameForType(GxsIdDetailsType type, const RsIdentityDeta QIcon GxsIdDetails::getLoadingIcon(const RsGxsId &/*id*/) { - return QIcon(IMAGE_LOADING); + return FilesDefs::getIconFromQtResourcePath(IMAGE_LOADING); } bool GxsIdDetails::MakeIdDesc(const RsGxsId &id, bool doIcons, QString &str, QList &icons, QString& comment,uint32_t icon_types) @@ -1003,9 +1006,9 @@ bool GxsIdDetails::MakeIdDesc(const RsGxsId &id, bool doIcons, QString &str, QLi // Cyril: I disabled these three which I believe to have been put for testing purposes. // -// icons.push_back(QIcon(IMAGE_ANON)); -// icons.push_back(QIcon(IMAGE_ANON)); -// icons.push_back(QIcon(IMAGE_ANON)); +// icons.push_back(FilesDefs::getIconFromQtResourcePath(IMAGE_ANON)); +// icons.push_back(FilesDefs::getIconFromQtResourcePath(IMAGE_ANON)); +// icons.push_back(FilesDefs::getIconFromQtResourcePath(IMAGE_ANON)); // std::cerr << "GxsIdTreeWidget::MakeIdDesc() ID Ok. Comment: " << comment.toStdString() ; // std::cerr << std::endl; @@ -1081,20 +1084,20 @@ QIcon GxsIdDetails::getReputationIcon( RsReputationLevel icon_index, uint32_t min_reputation ) { if( static_cast(icon_index) >= min_reputation ) - return QIcon(REPUTATION_VOID); + return FilesDefs::getIconFromQtResourcePath(REPUTATION_VOID); switch(icon_index) { case RsReputationLevel::LOCALLY_NEGATIVE: - return QIcon(REPUTATION_LOCALLY_NEGATIVE_ICON); + return FilesDefs::getIconFromQtResourcePath(REPUTATION_LOCALLY_NEGATIVE_ICON); case RsReputationLevel::LOCALLY_POSITIVE: - return QIcon(REPUTATION_LOCALLY_POSITIVE_ICON); + return FilesDefs::getIconFromQtResourcePath(REPUTATION_LOCALLY_POSITIVE_ICON); case RsReputationLevel::REMOTELY_POSITIVE: - return QIcon(REPUTATION_REMOTELY_POSITIVE_ICON); + return FilesDefs::getIconFromQtResourcePath(REPUTATION_REMOTELY_POSITIVE_ICON); case RsReputationLevel::REMOTELY_NEGATIVE: - return QIcon(REPUTATION_REMOTELY_NEGATIVE_ICON); + return FilesDefs::getIconFromQtResourcePath(REPUTATION_REMOTELY_NEGATIVE_ICON); case RsReputationLevel::NEUTRAL: - return QIcon(REPUTATION_NEUTRAL_ICON); + return FilesDefs::getIconFromQtResourcePath(REPUTATION_NEUTRAL_ICON); default: std::cerr << "Asked for unidentified icon index " << static_cast(icon_index) << std::endl; @@ -1110,7 +1113,7 @@ void GxsIdDetails::getIcons(const RsIdentityDetails &details, QList &icon RsReputationLevel::LOCALLY_NEGATIVE ) { icons.clear() ; - icons.push_back(QIcon(IMAGE_BANNED)) ; + icons.push_back(FilesDefs::getIconFromQtResourcePath(IMAGE_BANNED)) ; return ; } @@ -1139,12 +1142,12 @@ void GxsIdDetails::getIcons(const RsIdentityDetails &details, QList &icon if (details.mFlags & RS_IDENTITY_FLAGS_PGP_LINKED) { if (details.mFlags & RS_IDENTITY_FLAGS_PGP_KNOWN) - baseIcon = QIcon(IMAGE_PGPKNOWN); + baseIcon = FilesDefs::getIconFromQtResourcePath(IMAGE_PGPKNOWN); else - baseIcon = QIcon(IMAGE_PGPUNKNOWN); + baseIcon = FilesDefs::getIconFromQtResourcePath(IMAGE_PGPUNKNOWN); } else - baseIcon = QIcon(IMAGE_ANON); + baseIcon = FilesDefs::getIconFromQtResourcePath(IMAGE_ANON); icons.push_back(baseIcon); } diff --git a/retroshare-gui/src/gui/gxs/GxsIdLabel.cpp b/retroshare-gui/src/gui/gxs/GxsIdLabel.cpp index 7cfd5565d..6cb741d14 100644 --- a/retroshare-gui/src/gui/gxs/GxsIdLabel.cpp +++ b/retroshare-gui/src/gui/gxs/GxsIdLabel.cpp @@ -22,35 +22,38 @@ #include "GxsIdDetails.h" /** Constructor */ -GxsIdLabel::GxsIdLabel(QWidget *parent) - : QLabel(parent) +GxsIdLabel::GxsIdLabel(bool show_tooltip,QWidget *parent) + : QLabel(parent),mShowTooltip(show_tooltip) { } static void fillLabelCallback(GxsIdDetailsType type, const RsIdentityDetails &details, QObject *object, const QVariant &/*data*/) { - QLabel *label = dynamic_cast(object); + GxsIdLabel *label = dynamic_cast(object); if (!label) { return; } label->setText(GxsIdDetails::getNameForType(type, details)); - QString toolTip; + if(label->showTooltip()) + { + QString toolTip; - switch (type) { - case GXS_ID_DETAILS_TYPE_EMPTY: - case GXS_ID_DETAILS_TYPE_LOADING: - case GXS_ID_DETAILS_TYPE_FAILED: - case GXS_ID_DETAILS_TYPE_BANNED: - break; + switch (type) { + case GXS_ID_DETAILS_TYPE_EMPTY: + case GXS_ID_DETAILS_TYPE_LOADING: + case GXS_ID_DETAILS_TYPE_FAILED: + case GXS_ID_DETAILS_TYPE_BANNED: + break; - case GXS_ID_DETAILS_TYPE_DONE: - toolTip = GxsIdDetails::getComment(details); - break; + case GXS_ID_DETAILS_TYPE_DONE: + toolTip = GxsIdDetails::getComment(details); + break; + } + + label->setToolTip(toolTip); } - - label->setToolTip(toolTip); } void GxsIdLabel::setId(const RsGxsId &id) diff --git a/retroshare-gui/src/gui/gxs/GxsIdLabel.h b/retroshare-gui/src/gui/gxs/GxsIdLabel.h index 03d68f6e7..4cf7c9e44 100644 --- a/retroshare-gui/src/gui/gxs/GxsIdLabel.h +++ b/retroshare-gui/src/gui/gxs/GxsIdLabel.h @@ -29,13 +29,16 @@ class GxsIdLabel : public QLabel Q_OBJECT public: - GxsIdLabel(QWidget *parent = NULL); + GxsIdLabel(bool show_tooltip=true,QWidget *parent = NULL); void setId(const RsGxsId &id); bool getId(RsGxsId &id); + bool showTooltip() const { return mShowTooltip; } + private: RsGxsId mId; + bool mShowTooltip; }; #endif diff --git a/retroshare-gui/src/gui/gxs/GxsIdTreeWidgetItem.cpp b/retroshare-gui/src/gui/gxs/GxsIdTreeWidgetItem.cpp index f6709922a..0a8b9271b 100644 --- a/retroshare-gui/src/gui/gxs/GxsIdTreeWidgetItem.cpp +++ b/retroshare-gui/src/gui/gxs/GxsIdTreeWidgetItem.cpp @@ -26,8 +26,8 @@ #define BANNED_IMAGE ":/icons/yellow_biohazard64.png" /** Constructor */ -GxsIdRSTreeWidgetItem::GxsIdRSTreeWidgetItem(const RSTreeWidgetItemCompareRole *compareRole, uint32_t icon_mask,QTreeWidget *parent) - : QObject(NULL), RSTreeWidgetItem(compareRole, parent), mColumn(0), mIconTypeMask(icon_mask) +GxsIdRSTreeWidgetItem::GxsIdRSTreeWidgetItem(const RSTreeWidgetItemCompareRole *compareRole, uint32_t icon_mask,bool auto_tooltip,QTreeWidget *parent) + : QObject(NULL), RSTreeWidgetItem(compareRole, parent), mColumn(0), mAutoTooltip(auto_tooltip), mIconTypeMask(icon_mask) { init(); } @@ -35,8 +35,8 @@ GxsIdRSTreeWidgetItem::GxsIdRSTreeWidgetItem(const RSTreeWidgetItemCompareRole * void GxsIdRSTreeWidgetItem::init() { mIdFound = false; + mBannedState = false ; mRetryWhenFailed = false; - mBannedState = false ; } static void fillGxsIdRSTreeWidgetItemCallback(GxsIdDetailsType type, const RsIdentityDetails &details, QObject *object, const QVariant &/*data*/) @@ -67,12 +67,14 @@ static void fillGxsIdRSTreeWidgetItemCallback(GxsIdDetailsType type, const RsIde break; case GXS_ID_DETAILS_TYPE_BANNED: - icons.push_back(QIcon("BANNED_IMAGE")) ; + icons.push_back(FilesDefs::getIconFromQtResourcePath("BANNED_IMAGE")) ; break ; } int column = item->idColumn(); - item->setToolTip(column, GxsIdDetails::getComment(details)); + + if(item->autoTooltip()) + item->setToolTip(column, GxsIdDetails::getComment(details)); item->setText(column, GxsIdDetails::getNameForType(type, details)); item->setData(column, Qt::UserRole, QString::fromStdString(details.mId.toStdString())); @@ -159,7 +161,7 @@ QVariant GxsIdRSTreeWidgetItem::data(int column, int role) const if(mId.isNull()) return RSTreeWidgetItem::data(column, role); else if( rsReputations->overallReputationLevel(mId) == RsReputationLevel::LOCALLY_NEGATIVE ) - pix = QPixmap(BANNED_IMAGE); + pix = FilesDefs::getPixmapFromQtResourcePath(BANNED_IMAGE); else if ( mAvatar.mSize == 0 || !GxsIdDetails::loadPixmapFromData(mAvatar.mData, mAvatar.mSize, pix,GxsIdDetails::LARGE) ) pix = GxsIdDetails::makeDefaultIcon(mId,GxsIdDetails::LARGE); @@ -176,3 +178,42 @@ QVariant GxsIdRSTreeWidgetItem::data(int column, int role) const return RSTreeWidgetItem::data(column, role); } + +void GxsIdTreeItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex& index) const +{ + if(!index.isValid()) + { + RsErr() << __PRETTY_FUNCTION__ << " attempt to draw an invalid index." << std::endl; + return ; + } + + QStyleOptionViewItem ownOption (option); + initStyleOption(&ownOption, index); + + RsGxsId id(index.data(Qt::UserRole).toString().toStdString()); + QString cmt; + + if(id.isNull()) + { + if (ownOption.icon.isNull()) + ownOption.icon = FilesDefs::getIconFromQtResourcePath(":/icons/notification.png"); + } + else + { + if(! computeNameIconAndComment(id,ownOption.text,ownOption.icon,cmt)) + { + if(mReloadPeriod > 3) + { + ownOption.text = tr("[Unknown]"); + ownOption.icon = FilesDefs::getIconFromQtResourcePath(":/icons/png/anonymous.png"); + } + else + { + ownOption.icon = GxsIdDetails::getLoadingIcon(id); + launchAsyncLoading(); + } + } + } + + RSElidedItemDelegate::paint(painter,ownOption,index); +} diff --git a/retroshare-gui/src/gui/gxs/GxsIdTreeWidgetItem.h b/retroshare-gui/src/gui/gxs/GxsIdTreeWidgetItem.h index 6e910b63b..15bdfeb87 100644 --- a/retroshare-gui/src/gui/gxs/GxsIdTreeWidgetItem.h +++ b/retroshare-gui/src/gui/gxs/GxsIdTreeWidgetItem.h @@ -24,10 +24,13 @@ #include #include #include -#include -#include +#include "retroshare/rsidentity.h" +#include "retroshare/rspeers.h" +#include "gui/common/FilesDefs.h" #include "gui/common/RSTreeWidgetItem.h" +#include "gui/common/RSElidedItemDelegate.h" + #include "gui/gxs/GxsIdDetails.h" /***** @@ -41,7 +44,7 @@ class GxsIdRSTreeWidgetItem : public QObject, public RSTreeWidgetItem Q_OBJECT public: - GxsIdRSTreeWidgetItem(const RSTreeWidgetItemCompareRole *compareRole, uint32_t icon_mask,QTreeWidget *parent = NULL); + GxsIdRSTreeWidgetItem(const RSTreeWidgetItemCompareRole *compareRole, uint32_t icon_mask,bool auto_tooltip=true,QTreeWidget *parent = NULL); void setId(const RsGxsId &id, int column, bool retryWhenFailed); bool getId(RsGxsId &id); @@ -57,6 +60,7 @@ public: void setBannedState(bool b) { mBannedState = b; } // does not actually change the state, but used instead by callbacks to leave a trace void updateBannedState() ; // checks reputation, and update is needed + bool autoTooltip() const { return mAutoTooltip; } private slots: void startProcess(); @@ -66,8 +70,9 @@ private: RsGxsId mId; int mColumn; bool mIdFound; - bool mBannedState ; + bool mBannedState ; bool mRetryWhenFailed; + bool mAutoTooltip; RsReputationLevel mReputationLevel; uint32_t mIconTypeMask; RsGxsImage mAvatar; @@ -75,101 +80,19 @@ private: // This class is responsible of rendering authors of type RsGxsId in tree views. Used in forums, messages, etc. -class GxsIdTreeItemDelegate: public QStyledItemDelegate +class GxsIdTreeItemDelegate: public RSElidedItemDelegate { Q_OBJECT public: - GxsIdTreeItemDelegate() - { - mLoading = false; - mReloadPeriod = 0; - } - - QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override - { - QStyleOptionViewItemV4 opt = option; - initStyleOption(&opt, index); - - // disable default icon - opt.icon = QIcon(); - const QRect r = option.rect; - - RsGxsId id(index.data(Qt::UserRole).toString().toStdString()); - QString str; - QList icons; - QString comment; - - QFontMetricsF fm(option.font); - float f = fm.height(); - - QIcon icon ; - - if(!GxsIdDetails::MakeIdDesc(id, true, str, icons, comment,GxsIdDetails::ICON_TYPE_AVATAR)) - { - icon = GxsIdDetails::getLoadingIcon(id); - launchAsyncLoading(); - } - else - icon = *icons.begin(); - - QPixmap pix = icon.pixmap(r.size()); - - return QSize(1.2*(pix.width() + fm.width(str)),std::max(1.1*pix.height(),1.4*fm.height())); - } - - virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex& index) const override + GxsIdTreeItemDelegate(QObject *parent = nullptr) + :RSElidedItemDelegate(parent), mLoading(false), mReloadPeriod(0) { - if(!index.isValid()) - { - std::cerr << "(EE) attempt to draw an invalid index." << std::endl; - return ; - } - - QStyleOptionViewItemV4 opt = option; - initStyleOption(&opt, index); - - // disable default icon - opt.icon = QIcon(); - // draw default item - QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter, 0); - - const QRect r = option.rect; - - RsGxsId id(index.data(Qt::UserRole).toString().toStdString()); - QString str; - QString comment; - - QFontMetricsF fm(painter->font()); - float f = fm.height(); - - QIcon icon ; - - if(id.isNull()) - { - str = tr("[Notification]"); - icon = QIcon(":/icons/logo_128.png"); - } - else if(! computeNameIconAndComment(id,str,icon,comment)) - if(mReloadPeriod > 3) - { - str = tr("[Unknown]"); - icon = QIcon(); - } - else - { - icon = GxsIdDetails::getLoadingIcon(id); - launchAsyncLoading(); - } - - QPixmap pix = icon.pixmap(r.size()); - const QPoint p = QPoint(r.height()/2.0, (r.height() - pix.height())/2); - - // draw pixmap at center of item - painter->drawPixmap(r.topLeft() + p, pix); - painter->drawText(r.topLeft() + QPoint(r.height()+ f/2.0 + f/2.0,f*1.0), str); + //setPaintRoudedRect(false); } + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex& index) const override; + void launchAsyncLoading() const { if(mLoading) @@ -194,22 +117,27 @@ public: return true; } - static bool computeNameIconAndComment(const RsGxsId& id,QString& name,QIcon& icon,QString& comment) - { - QList icons; + static bool computeNameIconAndComment(const RsGxsId& id,QString& name,QIcon& icon,QString& comment) + { + QList icons; + bool exist = false; - if(rsPeers->isFriend(RsPeerId(id))) // horrible trick because some widgets still use locations as IDs (e.g. messages) - { + + if(rsPeers->isFriend(RsPeerId(id))) // horrible trick because some widgets still use locations as IDs (e.g. messages) + { name = QString::fromUtf8(rsPeers->getPeerName(RsPeerId(id)).c_str()) ; - icon = QIcon(":/icons/avatar_128.png"); - } - else if(!GxsIdDetails::MakeIdDesc(id, true, name, icons, comment,GxsIdDetails::ICON_TYPE_AVATAR)) - return false; + icon = FilesDefs::getIconFromQtResourcePath(":/icons/avatar_128.png"); + } else - icon = *icons.begin(); + if(!GxsIdDetails::MakeIdDesc(id, true, name, icons, comment,GxsIdDetails::ICON_TYPE_AVATAR)) + return false; + else + icon = *icons.begin(); - return true; - } + FilesDefs::getIconFromGxsIdCache(id,icon,exist); + + return true; + } private slots: void reload() { mLoading=false; emit commitData(NULL) ; } diff --git a/retroshare-gui/src/gui/gxs/GxsMessageFramePostWidget.cpp b/retroshare-gui/src/gui/gxs/GxsMessageFramePostWidget.cpp index bff646b01..1eb52356a 100644 --- a/retroshare-gui/src/gui/gxs/GxsMessageFramePostWidget.cpp +++ b/retroshare-gui/src/gui/gxs/GxsMessageFramePostWidget.cpp @@ -25,6 +25,7 @@ #include "gui/common/UIStateHelper.h" #include "retroshare/rsgxsifacehelper.h" +#include "util/qtthreadsutils.h" //#define ENABLE_DEBUG 1 @@ -50,12 +51,12 @@ GxsMessageFramePostWidget::~GxsMessageFramePostWidget() void GxsMessageFramePostWidget::groupIdChanged() { - mGroupName = groupId().isNull () ? "" : tr("Loading"); + mGroupName = groupId().isNull () ? "" : tr("Loading..."); groupNameChanged(mGroupName); emit groupChanged(this); - fillComplete(); + updateDisplay(true); } QString GxsMessageFramePostWidget::groupName(bool /*withUnreadCount*/) @@ -98,41 +99,14 @@ void GxsMessageFramePostWidget::updateDisplay(bool complete) { if (complete) { /* Fill complete */ - requestGroupData(); - requestAllPosts(); + loadGroupData(); + loadAllPosts(); return; } if (groupId().isNull()) { return; } - - bool updateGroup = false; - const std::set &grpIdsMeta = getGrpIdsMeta(); - - if(grpIdsMeta.find(groupId())!=grpIdsMeta.end()) - updateGroup = true; - - const std::set &grpIds = getGrpIds(); - if (!groupId().isNull() && grpIds.find(groupId())!=grpIds.end()) - { - updateGroup = true; - /* Do we need to fill all posts? */ - requestAllPosts(); - } else { - std::map > msgs; - getAllMsgIds(msgs); - if (!msgs.empty()) { - auto mit = msgs.find(groupId()); - if (mit != msgs.end()) { - requestPosts(mit->second); - } - } - } - - if (updateGroup) { - requestGroupData(); - } } void GxsMessageFramePostWidget::fillThreadAddPost(const QVariant &post, bool related, int current, int count) @@ -189,17 +163,10 @@ void GxsMessageFramePostWidget::fillThreadFinished() /** Request / Response of Data ********************************/ /**************************************************************/ -void GxsMessageFramePostWidget::requestGroupData() +void GxsMessageFramePostWidget::loadGroupData() { -#ifdef ENABLE_DEBUG - std::cerr << "GxsMessageFramePostWidget::requestGroupData()"; - std::cerr << std::endl; -#endif - mSubscribeFlags = 0; - mTokenQueue->cancelActiveRequestTokens(mTokenTypeGroupData); - if (groupId().isNull()) { mStateHelper->setActive(mTokenTypeGroupData, false); mStateHelper->setLoading(mTokenTypeGroupData, false); @@ -216,61 +183,59 @@ void GxsMessageFramePostWidget::requestGroupData() mStateHelper->setLoading(mTokenTypeGroupData, true); emit groupChanged(this); - std::list groupIds; - groupIds.push_back(groupId()); + RsThread::async([this]() + { + RsGxsGenericGroupData *data = nullptr; + getGroupData(data); - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; + if(!data) + { + std::cerr << "Cannot get group data for group " << groupId() << ". Maybe database is busy" << std::endl; + return; + } + + RsQThreadUtils::postToObject( [data,this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ + + bool ok = insertGroupData(data); + + mStateHelper->setLoading(mTokenTypeGroupData, false); + + if (ok) { + mSubscribeFlags = data->mMeta.mSubscribeFlags; + + mGroupName = QString::fromUtf8(data->mMeta.mGroupName.c_str()); + groupNameChanged(mGroupName); + } else { + std::cerr << "GxsMessageFramePostWidget::loadGroupData() ERROR Not just one Group"; + std::cerr << std::endl; + + mStateHelper->clear(mTokenTypeGroupData); + + mGroupName.clear(); + groupNameChanged(mGroupName); + } + + mStateHelper->setActive(mTokenTypeGroupData, ok); + + emit groupChanged(this); + + delete data; + + }, this ); + }); - uint32_t token; - mTokenQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, groupIds, mTokenTypeGroupData); } -void GxsMessageFramePostWidget::loadGroupData(const uint32_t &token) +void GxsMessageFramePostWidget::loadAllPosts() { -#ifdef ENABLE_DEBUG - std::cerr << "GxsMessageFramePostWidget::loadGroupData()"; - std::cerr << std::endl; -#endif - - RsGroupMetaData metaData; - bool ok = insertGroupData(token, metaData); - - mStateHelper->setLoading(mTokenTypeGroupData, false); - - if (ok) { - mSubscribeFlags = metaData.mSubscribeFlags; - - mGroupName = QString::fromUtf8(metaData.mGroupName.c_str()); - groupNameChanged(mGroupName); - } else { - std::cerr << "GxsMessageFramePostWidget::loadGroupData() ERROR Not just one Group"; - std::cerr << std::endl; - - mStateHelper->clear(mTokenTypeGroupData); - - mGroupName.clear(); - groupNameChanged(mGroupName); - } - - mStateHelper->setActive(mTokenTypeGroupData, ok); - - emit groupChanged(this); -} - -void GxsMessageFramePostWidget::requestAllPosts() -{ -#ifdef ENABLE_DEBUG - std::cerr << "GxsMessageFramePostWidget::requestAllPosts()"; - std::cerr << std::endl; -#endif - mNavigatePendingMsgId.clear(); /* Request all posts */ - mTokenQueue->cancelActiveRequestTokens(mTokenTypeAllPosts); - if (mFillThread) { /* Stop current fill thread */ GxsMessageFramePostThread *thread = mFillThread; @@ -293,66 +258,66 @@ void GxsMessageFramePostWidget::requestAllPosts() mStateHelper->setLoading(mTokenTypeAllPosts, true); emit groupChanged(this); - std::list groupIds; - groupIds.push_back(groupId()); + RsThread::async([this]() + { + std::vector posts; + getAllMsgData(posts); - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; + RsQThreadUtils::postToObject( [posts,this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ + + mStateHelper->setActive(mTokenTypeAllPosts, true); + + if (useThread()) { + /* Create fill thread */ + mFillThread = new GxsMessageFramePostThread(posts,this); + + // connect thread + connect(mFillThread, SIGNAL(finished()), this, SLOT(fillThreadFinished()), Qt::BlockingQueuedConnection); + connect(mFillThread, SIGNAL(addPost(QVariant,bool,int,int)), this, SLOT(fillThreadAddPost(QVariant,bool,int,int)), Qt::BlockingQueuedConnection); + +#ifdef ENABLE_DEBUG + std::cerr << "GxsMessageFramePostWidget::loadAllPosts() Start fill thread" << std::endl; +#endif + + /* Start thread */ + mFillThread->start(); + } + else + { + insertAllPosts(posts, NULL); + + mStateHelper->setLoading(mTokenTypeAllPosts, false); + + if (!mNavigatePendingMsgId.isNull()) + { + navigate(mNavigatePendingMsgId); + + mNavigatePendingMsgId.clear(); + } + + // don't forget to delete the posts + + for(auto& ppost:posts) + delete ppost; + } + + emit groupChanged(this); + + }, this ); + }); - uint32_t token; - mTokenQueue->requestMsgInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, groupIds, mTokenTypeAllPosts); } -void GxsMessageFramePostWidget::loadAllPosts(const uint32_t &token) +void GxsMessageFramePostWidget::loadPosts(const std::set& msgIds) { -#ifdef ENABLE_DEBUG - std::cerr << "GxsMessageFramePostWidget::loadAllPosts()"; - std::cerr << std::endl; -#endif - - mStateHelper->setActive(mTokenTypeAllPosts, true); - - if (useThread()) { - /* Create fill thread */ - mFillThread = new GxsMessageFramePostThread(token, this); - - // connect thread - connect(mFillThread, SIGNAL(finished()), this, SLOT(fillThreadFinished()), Qt::BlockingQueuedConnection); - connect(mFillThread, SIGNAL(addPost(QVariant,bool,int,int)), this, SLOT(fillThreadAddPost(QVariant,bool,int,int)), Qt::BlockingQueuedConnection); - -#ifdef ENABLE_DEBUG - std::cerr << "GxsMessageFramePostWidget::loadAllPosts() Start fill thread" << std::endl; -#endif - - /* Start thread */ - mFillThread->start(); - } else { - insertAllPosts(token, NULL); - - mStateHelper->setLoading(mTokenTypeAllPosts, false); - - if (!mNavigatePendingMsgId.isNull()) { - navigate(mNavigatePendingMsgId); - - mNavigatePendingMsgId.clear(); - } - } - - emit groupChanged(this); -} - -void GxsMessageFramePostWidget::requestPosts(const std::set &msgIds) -{ -#ifdef ENABLE_DEBUG - std::cerr << "GxsMessageFramePostWidget::requestPosts()"; - std::cerr << std::endl; -#endif - mNavigatePendingMsgId.clear(); - mTokenQueue->cancelActiveRequestTokens(mTokenTypePosts); - - if (groupId().isNull()) { + if (groupId().isNull()) + { mStateHelper->setActive(mTokenTypePosts, false); mStateHelper->setLoading(mTokenTypePosts, false); mStateHelper->clear(mTokenTypePosts); @@ -360,77 +325,53 @@ void GxsMessageFramePostWidget::requestPosts(const std::set &msg return; } - if (msgIds.empty()) { + if (msgIds.empty()) return; - } mStateHelper->setLoading(mTokenTypePosts, true); emit groupChanged(this); - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; - - uint32_t token; - GxsMsgReq requestMsgIds; - requestMsgIds[groupId()] = msgIds; - mTokenQueue->requestMsgInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, requestMsgIds, mTokenTypePosts); -} - -void GxsMessageFramePostWidget::loadPosts(const uint32_t &token) -{ -#ifdef ENABLE_DEBUG - std::cerr << "GxsMessageFramePostWidget::loadPosts()"; - std::cerr << std::endl; -#endif - - mStateHelper->setActive(mTokenTypePosts, true); - - insertPosts(token); - - mStateHelper->setLoading(mTokenTypePosts, false); - emit groupChanged(this); - - if (!mNavigatePendingMsgId.isNull()) { - navigate(mNavigatePendingMsgId); - - mNavigatePendingMsgId.clear(); - } -} - -void GxsMessageFramePostWidget::loadRequest(const TokenQueue *queue, const TokenRequest &req) -{ -#ifdef ENABLE_DEBUG - std::cerr << "GxsMessageFramePostWidget::loadRequest() UserType: " << req.mUserType; - std::cerr << std::endl; -#endif - - if (queue == mTokenQueue) + RsThread::async([this,msgIds]() { - if (req.mUserType == mTokenTypeGroupData) { - loadGroupData(req.mToken); - return; - } + std::vector posts; - if (req.mUserType == mTokenTypeAllPosts) { - loadAllPosts(req.mToken); - return; - } + getMsgData(msgIds,posts); - if (req.mUserType == mTokenTypePosts) { - loadPosts(req.mToken); - return; - } - } + RsQThreadUtils::postToObject( [posts,this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ - GxsMessageFrameWidget::loadRequest(queue, req); + mStateHelper->setActive(mTokenTypePosts, true); + + insertPosts(posts); + + mStateHelper->setLoading(mTokenTypePosts, false); + emit groupChanged(this); + + if (!mNavigatePendingMsgId.isNull()) + { + navigate(mNavigatePendingMsgId); + + mNavigatePendingMsgId.clear(); + } + + // don't forget to delete posts. + + for(auto& post:posts) + delete post; + + }, this ); + }); } /**************************************************************/ /** GxsMessageFramePostThread *********************************/ /**************************************************************/ -GxsMessageFramePostThread::GxsMessageFramePostThread(uint32_t token, GxsMessageFramePostWidget *parent) - : QThread(parent), mToken(token), mParent(parent) +GxsMessageFramePostThread::GxsMessageFramePostThread(const std::vector& posts,GxsMessageFramePostWidget *parent) + : mPosts(posts),QThread(parent), mParent(parent) { mStopped = false; } @@ -444,9 +385,9 @@ GxsMessageFramePostThread::~GxsMessageFramePostThread() void GxsMessageFramePostThread::stop(bool waitForStop) { - if (waitForStop) { + if (waitForStop) disconnect(); - } + mStopped = true; QApplication::processEvents(); @@ -462,7 +403,12 @@ void GxsMessageFramePostThread::run() std::cerr << "GxsMessageFramePostThread::run()" << std::endl; #endif - mParent->insertAllPosts(mToken, this); + mParent->insertAllPosts(mPosts,this); + + for(auto& ppost:mPosts) + delete ppost; + + mPosts.clear(); // dont keep deleted pointers #ifdef ENABLE_DEBUG std::cerr << "GxsMessageFramePostThread::run() stopped: " << (stopped() ? "yes" : "no") << std::endl; diff --git a/retroshare-gui/src/gui/gxs/GxsMessageFramePostWidget.h b/retroshare-gui/src/gui/gxs/GxsMessageFramePostWidget.h index 231134d13..f47b9f3c9 100644 --- a/retroshare-gui/src/gui/gxs/GxsMessageFramePostWidget.h +++ b/retroshare-gui/src/gui/gxs/GxsMessageFramePostWidget.h @@ -40,12 +40,15 @@ public: /* GxsMessageFrameWidget */ virtual void groupIdChanged(); virtual QString groupName(bool withUnreadCount); -// virtual QIcon groupIcon() = 0; virtual bool navigate(const RsGxsMessageId& msgId); virtual bool isLoading(); + // These should be derived in subclasses + virtual bool getGroupData(RsGxsGenericGroupData *& data) =0; + virtual void getMsgData(const std::set& msgIds,std::vector& posts) =0; + virtual void getAllMsgData(std::vector& posts) =0; + /* GXS functions */ - virtual void loadRequest(const TokenQueue *queue, const TokenRequest &req); int subscribeFlags() { return mSubscribeFlags; } @@ -64,17 +67,15 @@ protected: virtual void fillThreadCreatePost(const QVariant &/*post*/, bool /*related*/, int /*current*/, int /*count*/) {} /* GXS functions */ - void requestGroupData(); - void loadGroupData(const uint32_t &token); - virtual bool insertGroupData(const uint32_t &token, RsGroupMetaData &metaData) = 0; + void loadGroupData(); + void loadAllPosts(); + void loadPosts(const std::set& msgIds); - void requestAllPosts(); - void loadAllPosts(const uint32_t &token); - virtual void insertAllPosts(const uint32_t &token, GxsMessageFramePostThread *thread) = 0; + // In the following 3 methods, the memory ownership is kept by GxsMessageFramePostWidget - void requestPosts(const std::set &msgIds); - void loadPosts(const uint32_t &token); - virtual void insertPosts(const uint32_t &token) = 0; + virtual bool insertGroupData(const RsGxsGenericGroupData *data) =0; + virtual void insertPosts(const std::vector& posts) =0; + virtual void insertAllPosts(const std::vector& posts, GxsMessageFramePostThread *thread) =0; private slots: void fillThreadFinished(); @@ -97,7 +98,7 @@ class GxsMessageFramePostThread : public QThread Q_OBJECT public: - GxsMessageFramePostThread(uint32_t token, GxsMessageFramePostWidget *parent); + GxsMessageFramePostThread(const std::vector& posts,GxsMessageFramePostWidget *parent); ~GxsMessageFramePostThread(); void run(); @@ -110,7 +111,7 @@ signals: void addPost(const QVariant &post, bool related, int current, int count); private: - uint32_t mToken; + std::vector mPosts; GxsMessageFramePostWidget *mParent; volatile bool mStopped; }; diff --git a/retroshare-gui/src/gui/gxs/GxsMessageFrameWidget.cpp b/retroshare-gui/src/gui/gxs/GxsMessageFrameWidget.cpp index 4e8780435..8d9a89a47 100644 --- a/retroshare-gui/src/gui/gxs/GxsMessageFrameWidget.cpp +++ b/retroshare-gui/src/gui/gxs/GxsMessageFrameWidget.cpp @@ -24,7 +24,7 @@ #include GxsMessageFrameWidget::GxsMessageFrameWidget(RsGxsIfaceHelper *ifaceImpl, QWidget *parent) - : RsGxsUpdateBroadcastWidget(ifaceImpl, parent) + : QWidget(parent) { mNextTokenType = 0; diff --git a/retroshare-gui/src/gui/gxs/GxsMessageFrameWidget.h b/retroshare-gui/src/gui/gxs/GxsMessageFrameWidget.h index 41b98dc6b..36c2f4f67 100644 --- a/retroshare-gui/src/gui/gxs/GxsMessageFrameWidget.h +++ b/retroshare-gui/src/gui/gxs/GxsMessageFrameWidget.h @@ -27,7 +27,7 @@ struct RsGxsIfaceHelper; class UIStateHelper; -class GxsMessageFrameWidget : public RsGxsUpdateBroadcastWidget, public TokenResponse +class GxsMessageFrameWidget : public QWidget, public TokenResponse { Q_OBJECT @@ -55,6 +55,7 @@ signals: void groupChanged(QWidget *widget); void waitingChanged(QWidget *widget); void loadComment(const RsGxsGroupId &groupId, const QVector& msg_versions,const RsGxsMessageId &msgId, const QString &title); + void groupDataLoaded(); protected: virtual void setAllMessagesReadDo(bool read, uint32_t &token) = 0; diff --git a/retroshare-gui/src/gui/gxs/GxsUserNotify.cpp b/retroshare-gui/src/gui/gxs/GxsUserNotify.cpp index ec2e0b09a..bc1afa1c1 100644 --- a/retroshare-gui/src/gui/gxs/GxsUserNotify.cpp +++ b/retroshare-gui/src/gui/gxs/GxsUserNotify.cpp @@ -19,63 +19,38 @@ *******************************************************************************/ #include "GxsUserNotify.h" -#include "gui/gxs/RsGxsUpdateBroadcastBase.h" +//#include "gui/gxs/RsGxsUpdateBroadcastBase.h" #include "retroshare/rsgxsifacehelper.h" +#include "util/qtthreadsutils.h" #define TOKEN_TYPE_STATISTICS 1 -GxsUserNotify::GxsUserNotify(RsGxsIfaceHelper *ifaceImpl, QObject *parent) : - UserNotify(parent), TokenResponse() +GxsUserNotify::GxsUserNotify(RsGxsIfaceHelper *ifaceImpl, const GxsGroupFrameDialog *g,QObject *parent) : UserNotify(parent), mGroupFrameDialog(g) { mNewThreadMessageCount = 0; mNewChildMessageCount = 0; mCountChildMsgs = false; - - mInterface = ifaceImpl; - mTokenService = mInterface->getTokenService(); - mTokenQueue = new TokenQueue(mInterface->getTokenService(), this); - - mBase = new RsGxsUpdateBroadcastBase(ifaceImpl); - connect(mBase, SIGNAL(fillDisplay(bool)), this, SLOT(updateIcon())); } -GxsUserNotify::~GxsUserNotify() -{ - if (mTokenQueue) { - delete(mTokenQueue); - } - if (mBase) { - delete(mBase); - } -} +GxsUserNotify::~GxsUserNotify() {} void GxsUserNotify::startUpdate() { mNewThreadMessageCount = 0; mNewChildMessageCount = 0; - uint32_t token; - mTokenService->requestServiceStatistic(token); - mTokenQueue->queueRequest(token, 0, RS_TOKREQ_ANSTYPE_ACK, TOKEN_TYPE_STATISTICS); + + GxsServiceStatistic stats; + mGroupFrameDialog->getServiceStatistics(stats); + + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ + + mNewThreadMessageCount = stats.mNumThreadMsgsNew; + mNewChildMessageCount = stats.mNumChildMsgsNew; + + update(); } -void GxsUserNotify::loadRequest(const TokenQueue *queue, const TokenRequest &req) -{ - if (queue == mTokenQueue) { - /* now switch on req */ - switch(req.mUserType) { - case TOKEN_TYPE_STATISTICS: - { - GxsServiceStatistic stats; - mInterface->getServiceStatistic(req.mToken, stats); - - mNewThreadMessageCount = stats.mNumThreadMsgsNew; - mNewChildMessageCount = stats.mNumChildMsgsNew; - - update(); - } - break; - } - } -} diff --git a/retroshare-gui/src/gui/gxs/GxsUserNotify.h b/retroshare-gui/src/gui/gxs/GxsUserNotify.h index c4dca9995..f9c5cd616 100644 --- a/retroshare-gui/src/gui/gxs/GxsUserNotify.h +++ b/retroshare-gui/src/gui/gxs/GxsUserNotify.h @@ -23,22 +23,20 @@ #include #include "gui/common/UserNotify.h" +#include "gui/gxs/GxsGroupFrameDialog.h" #include "util/TokenQueue.h" struct RsGxsIfaceHelper; class RsGxsUpdateBroadcastBase; -class GxsUserNotify : public UserNotify, public TokenResponse +class GxsUserNotify : public UserNotify { Q_OBJECT public: - GxsUserNotify(RsGxsIfaceHelper *ifaceImpl, QObject *parent = 0); + GxsUserNotify(RsGxsIfaceHelper *ifaceImpl, const GxsGroupFrameDialog *g, QObject *parent = 0); virtual ~GxsUserNotify(); - /* TokenResponse */ - virtual void loadRequest(const TokenQueue *queue, const TokenRequest &req); - protected: virtual void startUpdate(); @@ -49,10 +47,9 @@ protected: bool mCountChildMsgs; // Count new child messages? private: - RsGxsIfaceHelper *mInterface; - RsTokenService *mTokenService; - TokenQueue *mTokenQueue; RsGxsUpdateBroadcastBase *mBase; + const GxsGroupFrameDialog *mGroupFrameDialog; + unsigned int mNewThreadMessageCount; unsigned int mNewChildMessageCount; }; diff --git a/retroshare-gui/src/gui/gxs/RsGxsUpdateBroadcastBase.h b/retroshare-gui/src/gui/gxs/RsGxsUpdateBroadcastBase.h index ef19a1967..8c603ae84 100644 --- a/retroshare-gui/src/gui/gxs/RsGxsUpdateBroadcastBase.h +++ b/retroshare-gui/src/gui/gxs/RsGxsUpdateBroadcastBase.h @@ -47,7 +47,7 @@ public: const std::map > &getMsgIds() { return mMsgIds; } const std::map > &getMsgIdsMeta() { return mMsgIdsMeta; } void getAllMsgIds(std::map > &msgIds); - const std::set& getSearchResults() { return mTurtleResults ; } + const std::set& getSearchRequests() { return mTurtleResults ; } protected: void fillComplete(); diff --git a/retroshare-gui/src/gui/gxs/RsGxsUpdateBroadcastPage.h b/retroshare-gui/src/gui/gxs/RsGxsUpdateBroadcastPage.h index ebf1000af..2a983bf8d 100644 --- a/retroshare-gui/src/gui/gxs/RsGxsUpdateBroadcastPage.h +++ b/retroshare-gui/src/gui/gxs/RsGxsUpdateBroadcastPage.h @@ -20,7 +20,7 @@ #pragma once -#include "mainpage.h" +#include #include // This class implement a basic RS functionality which is that widgets displaying info @@ -52,7 +52,7 @@ public: const std::map > &getMsgIdsMeta(); void getAllMsgIds(std::map > &msgIds); - virtual const std::set getSearchResults() const { return std::set(); } // overload this for subclasses that provide distant search + virtual const std::set getSearchRequests() const { return std::set(); } // overload this for subclasses that provide distant search protected: virtual void showEvent(QShowEvent *event); diff --git a/retroshare-gui/src/gui/gxs/RsGxsUpdateBroadcastWidget.cpp b/retroshare-gui/src/gui/gxs/RsGxsUpdateBroadcastWidget.cpp index 98c6354e1..f6eb21909 100644 --- a/retroshare-gui/src/gui/gxs/RsGxsUpdateBroadcastWidget.cpp +++ b/retroshare-gui/src/gui/gxs/RsGxsUpdateBroadcastWidget.cpp @@ -49,10 +49,6 @@ const std::set &RsGxsUpdateBroadcastWidget::getGrpIds() return mBase->getGrpIds(); } -const std::set& RsGxsUpdateBroadcastWidget::getSearchResults() -{ - return mBase->getSearchResults(); -} const std::set &RsGxsUpdateBroadcastWidget::getGrpIdsMeta() { return mBase->getGrpIdsMeta(); diff --git a/retroshare-gui/src/gui/gxs/RsGxsUpdateBroadcastWidget.h b/retroshare-gui/src/gui/gxs/RsGxsUpdateBroadcastWidget.h index d1404b656..016c58214 100644 --- a/retroshare-gui/src/gui/gxs/RsGxsUpdateBroadcastWidget.h +++ b/retroshare-gui/src/gui/gxs/RsGxsUpdateBroadcastWidget.h @@ -51,7 +51,7 @@ public: const std::map > &getMsgIds(); const std::map > &getMsgIdsMeta(); void getAllMsgIds(std::map > &msgIds); - const std::set& getSearchResults() ; + const std::set& getSearchquests() ; RsGxsIfaceHelper *interfaceHelper() { return mInterfaceHelper; } diff --git a/retroshare-gui/src/gui/gxs/WikiGroupDialog.cpp b/retroshare-gui/src/gui/gxs/WikiGroupDialog.cpp index fab246dd6..1935f171e 100644 --- a/retroshare-gui/src/gui/gxs/WikiGroupDialog.cpp +++ b/retroshare-gui/src/gui/gxs/WikiGroupDialog.cpp @@ -24,43 +24,43 @@ #include const uint32_t WikiCreateEnabledFlags = ( - GXS_GROUP_FLAGS_NAME | - // GXS_GROUP_FLAGS_ICON | - GXS_GROUP_FLAGS_DESCRIPTION | - GXS_GROUP_FLAGS_DISTRIBUTION | - // GXS_GROUP_FLAGS_PUBLISHSIGN | - GXS_GROUP_FLAGS_SHAREKEYS | - // GXS_GROUP_FLAGS_PERSONALSIGN | - // GXS_GROUP_FLAGS_COMMENTS | - 0); + GXS_GROUP_FLAGS_NAME | + // GXS_GROUP_FLAGS_ICON | + GXS_GROUP_FLAGS_DESCRIPTION | + GXS_GROUP_FLAGS_DISTRIBUTION | + // GXS_GROUP_FLAGS_PUBLISHSIGN | + GXS_GROUP_FLAGS_SHAREKEYS | + // GXS_GROUP_FLAGS_PERSONALSIGN | + // GXS_GROUP_FLAGS_COMMENTS | + 0); uint32_t WikiCreateDefaultsFlags = ( GXS_GROUP_DEFAULTS_DISTRIB_PUBLIC | - //GXS_GROUP_DEFAULTS_DISTRIB_GROUP | - //GXS_GROUP_DEFAULTS_DISTRIB_LOCAL | + //GXS_GROUP_DEFAULTS_DISTRIB_GROUP | + //GXS_GROUP_DEFAULTS_DISTRIB_LOCAL | - GXS_GROUP_DEFAULTS_PUBLISH_OPEN | - //GXS_GROUP_DEFAULTS_PUBLISH_THREADS | - //GXS_GROUP_DEFAULTS_PUBLISH_REQUIRED | - //GXS_GROUP_DEFAULTS_PUBLISH_ENCRYPTED | + GXS_GROUP_DEFAULTS_PUBLISH_OPEN | + //GXS_GROUP_DEFAULTS_PUBLISH_THREADS | + //GXS_GROUP_DEFAULTS_PUBLISH_REQUIRED | + //GXS_GROUP_DEFAULTS_PUBLISH_ENCRYPTED | - //GXS_GROUP_DEFAULTS_PERSONAL_GPG | - GXS_GROUP_DEFAULTS_PERSONAL_REQUIRED | - //GXS_GROUP_DEFAULTS_PERSONAL_IFNOPUB | + //GXS_GROUP_DEFAULTS_PERSONAL_GPG | + GXS_GROUP_DEFAULTS_PERSONAL_REQUIRED | + //GXS_GROUP_DEFAULTS_PERSONAL_IFNOPUB | - //GXS_GROUP_DEFAULTS_COMMENTS_YES | - GXS_GROUP_DEFAULTS_COMMENTS_NO | - 0); + //GXS_GROUP_DEFAULTS_COMMENTS_YES | + GXS_GROUP_DEFAULTS_COMMENTS_NO | + 0); uint32_t WikiEditDefaultsFlags = WikiCreateDefaultsFlags; uint32_t WikiEditEnabledFlags = WikiCreateEnabledFlags; -WikiGroupDialog::WikiGroupDialog(TokenQueue *tokenQueue, QWidget *parent) - :GxsGroupDialog(tokenQueue, WikiCreateEnabledFlags, WikiCreateDefaultsFlags, parent) +WikiGroupDialog::WikiGroupDialog(QWidget *parent) + :GxsGroupDialog(WikiCreateEnabledFlags, WikiCreateDefaultsFlags, parent) { } -WikiGroupDialog::WikiGroupDialog(TokenQueue *tokenExternalQueue, RsTokenService *tokenService, Mode mode, RsGxsGroupId groupId, QWidget *parent) -:GxsGroupDialog(tokenExternalQueue, tokenService, mode, groupId, WikiEditEnabledFlags, WikiEditDefaultsFlags, parent) +WikiGroupDialog::WikiGroupDialog(Mode mode, RsGxsGroupId groupId, QWidget *parent) +:GxsGroupDialog(mode, groupId, WikiEditEnabledFlags, WikiEditDefaultsFlags, parent) { } @@ -87,43 +87,65 @@ void WikiGroupDialog::initUi() QPixmap WikiGroupDialog::serviceImage() { - return QPixmap(":/images/resource-group-new_64.png"); + return FilesDefs::getPixmapFromQtResourcePath(":/icons/png/wiki.png"); } -bool WikiGroupDialog::service_CreateGroup(uint32_t &token, const RsGroupMetaData &meta) + +bool WikiGroupDialog::service_createGroup(RsGroupMetaData &meta) { - // Specific Function. RsWikiCollection grp; grp.mMeta = meta; grp.mDescription = getDescription().toStdString(); - std::cerr << "WikiGroupDialog::service_CreateGroup() storing to Queue"; + + std::cerr << "WikiGroupDialog::service_CreateGroup()"; std::cerr << std::endl; - rsWiki->submitCollection(token, grp); - - return true; + bool success = rsWiki->createCollection(grp); + // createCollection should refresh groupId or data + return success; } -bool WikiGroupDialog::service_EditGroup(uint32_t &token, RsGroupMetaData &editedMeta) +bool WikiGroupDialog::service_updateGroup(const RsGroupMetaData &editedMeta) { RsWikiCollection grp; grp.mMeta = editedMeta; grp.mDescription = getDescription().toStdString(); - std::cerr << "WikiGroupDialog::service_EditGroup() submitting changes."; + std::cerr << "WikiGroupDialog::service_updateGroup() submitting changes."; std::cerr << std::endl; - rsWiki->updateCollection(token, grp); + bool success = rsWiki->updateCollection(grp); + // updateCollection should refresh groupId or data + return success; +} + +bool WikiGroupDialog::service_loadGroup(const RsGxsGenericGroupData *data, Mode mode, QString &description) +{ + std::cerr << "WikiGroupDialog::service_loadGroup()"; + std::cerr << std::endl; + + const RsWikiCollection *pgroup = dynamic_cast(data); + if (pgroup == nullptr) + { + std::cerr << "WikiGroupDialog::service_loadGroup() Error not a RsWikiCollection"; + std::cerr << std::endl; + return false; + } + + const RsWikiCollection &group = *pgroup; + description = QString::fromUtf8(group.mDescription.c_str()); + return true; } -bool WikiGroupDialog::service_loadGroup(uint32_t token, Mode /*mode*/, RsGroupMetaData& groupMetaData, QString &description) +bool WikiGroupDialog::service_getGroupData(const RsGxsGroupId &groupId, RsGxsGenericGroupData *&data) { - std::cerr << "WikiGroupDialog::service_loadGroup(" << token << ")"; + std::cerr << "WikiGroupDialog::service_getGroupData(" << groupId << ")"; std::cerr << std::endl; + std::list groupIds({groupId}); std::vector groups; - if (!rsWiki->getCollections(token, groups)) + if (!rsWiki->getCollections(groupIds, groups)) { std::cerr << "WikiGroupDialog::service_loadGroup() Error getting GroupData"; std::cerr << std::endl; @@ -137,11 +159,7 @@ bool WikiGroupDialog::service_loadGroup(uint32_t token, Mode /*mode*/, RsGroupMe return false; } - std::cerr << "WikisGroupDialog::service_loadGroup() Unfinished Loading"; - std::cerr << std::endl; - - groupMetaData = groups[0].mMeta; - description = QString::fromUtf8(groups[0].mDescription.c_str()); - + data = new RsWikiCollection(groups[0]); return true; } + diff --git a/retroshare-gui/src/gui/gxs/WikiGroupDialog.h b/retroshare-gui/src/gui/gxs/WikiGroupDialog.h index 7d2687eb0..1270d2ae3 100644 --- a/retroshare-gui/src/gui/gxs/WikiGroupDialog.h +++ b/retroshare-gui/src/gui/gxs/WikiGroupDialog.h @@ -29,15 +29,16 @@ class WikiGroupDialog : public GxsGroupDialog Q_OBJECT public: - WikiGroupDialog(TokenQueue *tokenQueue, QWidget *parent); - WikiGroupDialog(TokenQueue *tokenExternalQueue, RsTokenService *tokenService, Mode mode, RsGxsGroupId groupId, QWidget *parent = NULL); + WikiGroupDialog(QWidget *parent); + WikiGroupDialog(Mode mode, RsGxsGroupId groupId, QWidget *parent = NULL); protected: - virtual void initUi(); - virtual QPixmap serviceImage(); - virtual bool service_CreateGroup(uint32_t &token, const RsGroupMetaData &meta); - virtual bool service_loadGroup(uint32_t token, Mode mode, RsGroupMetaData& groupMetaData, QString &description); - virtual bool service_EditGroup(uint32_t &token, RsGroupMetaData& groupMetaData); + virtual void initUi() override; + virtual QPixmap serviceImage() override; + virtual bool service_createGroup(RsGroupMetaData &meta) override; + virtual bool service_updateGroup(const RsGroupMetaData &editedMeta) override; + virtual bool service_loadGroup(const RsGxsGenericGroupData *data, Mode mode, QString &description) override; + virtual bool service_getGroupData(const RsGxsGroupId &groupId, RsGxsGenericGroupData *&data) override; private: diff --git a/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.cpp b/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.cpp index 85f89f3dc..087e29300 100644 --- a/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.cpp +++ b/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.cpp @@ -33,6 +33,7 @@ #include "util/HandleRichText.h" #include "util/misc.h" #include "util/rsdir.h" +#include "util/qtthreadsutils.h" #include "util/RichTextEdit.h" #include @@ -54,9 +55,8 @@ CreateGxsChannelMsg::CreateGxsChannelMsg(const RsGxsGroupId &cId, RsGxsMessageId /* Invoke the Qt Designer generated object setup routine */ setupUi(this); Settings->loadWidgetInformation(this); - mChannelQueue = new TokenQueue(rsGxsChannels->getTokenService(), this); - headerFrame->setHeaderImage(QPixmap(":/icons/png/channel.png")); + headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/channel.png")); if(!existing_post.isNull()) headerFrame->setHeaderText(tr("Edit Channel Post")); @@ -72,15 +72,30 @@ CreateGxsChannelMsg::CreateGxsChannelMsg(const RsGxsGroupId &cId, RsGxsMessageId connect(addFileButton, SIGNAL(clicked() ), this , SLOT(addExtraFile())); connect(addfilepushButton, SIGNAL(clicked() ), this , SLOT(addExtraFile())); - + connect(subjectEdit,SIGNAL(textChanged(const QString&)),this,SLOT(updatePreviewText(const QString&))); + connect(addThumbnailButton, SIGNAL(clicked() ), this , SLOT(addThumbnail())); connect(thumbNailCb, SIGNAL(toggled(bool)), this, SLOT(allowAutoMediaThumbNail(bool))); connect(stackedWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenu(QPoint))); connect(generateCheckBox, SIGNAL(toggled(bool)), generateSpinBox, SLOT(setEnabled(bool))); + connect(aspectRatio_CB,SIGNAL(currentIndexChanged(int)),this,SLOT(changeAspectRatio(int))); + + channelpostButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/comment.png")); + attachmentsButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/attachements.png")); + addThumbnailButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/add-image.png")); + addfilepushButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/add-file.png")); + + aspectRatio_CB->setItemIcon(0,FilesDefs::getIconFromQtResourcePath(":/icons/svg/ratio-auto.svg")); + aspectRatio_CB->setItemIcon(1,FilesDefs::getIconFromQtResourcePath(":/icons/svg/ratio-1-1.svg")); + aspectRatio_CB->setItemIcon(2,FilesDefs::getIconFromQtResourcePath(":/icons/svg/ratio-3-4.svg")); + aspectRatio_CB->setItemIcon(3,FilesDefs::getIconFromQtResourcePath(":/icons/svg/ratio-16-9.svg")); generateSpinBox->setEnabled(false); - thumbNailCb->setVisible(false); + preview_W->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ChannelPostThumbnailView::CHAN_DEFAULT_IMAGE),true); + preview_W->setText("[Text preview]"); + + thumbNailCb->setVisible(false); thumbNailCb->setEnabled(false); #ifdef CHANNELS_FRAME_CATCHER fCatcher = new framecatcher(); @@ -96,6 +111,9 @@ CreateGxsChannelMsg::CreateGxsChannelMsg(const RsGxsGroupId &cId, RsGxsMessageId generateCheckBox->hide(); generateSpinBox->hide(); #endif + + /* load settings */ + processSettings(true); } CreateGxsChannelMsg::~CreateGxsChannelMsg() @@ -105,9 +123,43 @@ CreateGxsChannelMsg::~CreateGxsChannelMsg() delete fCatcher; #endif - delete(mChannelQueue); + // save settings + processSettings(false); } +void CreateGxsChannelMsg::processSettings(bool load) +{ + Settings->beginGroup(QString("CreateGxsChannelMsg")); + + if (load) { + // load settings + + // state of Ratio combobox + int index = Settings->value("aspectRatio_CB", 0).toInt(); + aspectRatio_CB->setCurrentIndex(index); + } else { + // save settings + + // state of Ratio combobox + Settings->setValue("aspectRatio_CB", aspectRatio_CB->currentIndex()); + } + + Settings->endGroup(); +} + +void CreateGxsChannelMsg::changeAspectRatio(int s) +{ + switch(s) + { + case 0: break; + case 1: preview_W->setAspectRatio(ChannelPostThumbnailView::ASPECT_RATIO_1_1); + break; + case 2: preview_W->setAspectRatio(ChannelPostThumbnailView::ASPECT_RATIO_2_3); + break; + case 3: preview_W->setAspectRatio(ChannelPostThumbnailView::ASPECT_RATIO_16_9); + break; + } +} void CreateGxsChannelMsg::contextMenu(QPoint /*point*/) { QList links ; @@ -123,9 +175,9 @@ void CreateGxsChannelMsg::contextMenu(QPoint /*point*/) QAction *action ; if(n_file > 1) - action = contextMnu.addAction(QIcon(":/images/pasterslink.png"), tr("Paste RetroShare Links"), this, SLOT(pasteLink())); + action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/pasterslink.png"), tr("Paste RetroShare Links"), this, SLOT(pasteLink())); else - action = contextMnu.addAction(QIcon(":/images/pasterslink.png"), tr("Paste RetroShare Link"), this, SLOT(pasteLink())); + action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/pasterslink.png"), tr("Paste RetroShare Link"), this, SLOT(pasteLink())); action->setDisabled(n_file < 1) ; contextMnu.exec(QCursor::pos()); @@ -591,26 +643,10 @@ void CreateGxsChannelMsg::newChannelMsg() /* request Data */ { - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_GROUP_META; - - std::list groupIds; - groupIds.push_back(mChannelId); - - std::cerr << "CreateGxsChannelMsg::newChannelMsg() Req Group Summary(" << mChannelId << ")"; - std::cerr << std::endl; - - uint32_t token; - mChannelQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_SUMMARY, opts, groupIds, CREATEMSG_CHANNELINFO); + loadChannelInfo(); if(!mOrigPostId.isNull()) - { - GxsMsgReq message_ids; - - opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; - message_ids[mChannelId].insert(mOrigPostId); - mChannelQueue->requestMsgInfo(token, RS_TOKREQ_ANSTYPE_SUMMARY, opts, message_ids, CREATEMSG_CHANNEL_POST_INFO); - } + loadOriginalChannelPostInfo(); } } @@ -623,6 +659,11 @@ void CreateGxsChannelMsg::saveChannelInfo(const RsGroupMetaData &meta) subjectEdit->setFocus(); } +void CreateGxsChannelMsg::updatePreviewText(const QString& s) +{ + preview_W->setText(s); +} + void CreateGxsChannelMsg::sendMsg() { #ifdef DEBUG_CREATE_GXS_MSG @@ -700,7 +741,7 @@ void CreateGxsChannelMsg::sendMessage(const std::string &subject, const std::str // send chan image buffer.open(QIODevice::WriteOnly); - picture.save(&buffer, "PNG"); // writes image into ba in PNG format + preview_W->getCroppedScaledPicture().save(&buffer, "PNG"); // writes image into ba in PNG format post.mThumbnail.copy((uint8_t *) ba.data(), ba.size()); } @@ -735,7 +776,7 @@ void CreateGxsChannelMsg::sendMessage(const std::string &subject, const std::str void CreateGxsChannelMsg::addThumbnail() { - QPixmap img = misc::getOpenThumbnailedPicture(this, tr("Load thumbnail picture"), 156, 107); + QPixmap img = misc::getOpenThumbnailedPicture(this, tr("Load thumbnail picture"), 0,0); // 0,0 means: no scale. if (img.isNull()) return; @@ -743,92 +784,97 @@ void CreateGxsChannelMsg::addThumbnail() picture = img; // to show the selected - thumbnail_label->setPixmap(picture); + preview_W->setPixmap(picture, aspectRatio_CB->currentIndex()==0); + } -void CreateGxsChannelMsg::loadChannelPostInfo(const uint32_t &token) +void CreateGxsChannelMsg::loadOriginalChannelPostInfo() { #ifdef DEBUG_CREATE_GXS_MSG std::cerr << "CreateGxsChannelMsg::loadChannelPostInfo()"; std::cerr << std::endl; #endif - - std::vector posts; - rsGxsChannels->getPostData(token, posts); - - if (posts.size() != 1) + RsThread::async([this]() { - std::cerr << "CreateGxsChannelMsg::loadChannelPostInfo() ERROR INVALID Number of posts in request" << std::endl; - return ; - } + std::vector posts; + std::vector comments; + std::vector votes; - // now populate the widget with the channel post data. - const RsGxsChannelPost& post = posts[0]; + if( !rsGxsChannels->getChannelContent(mChannelId,std::set({mOrigPostId}),posts,comments,votes) || posts.size() != 1) + { + std::cerr << "Cannot get channel post data for channel " << mChannelId << " and post " << mOrigPostId << std::endl; + return; + } - if(post.mMeta.mGroupId != mChannelId || post.mMeta.mMsgId != mOrigPostId) - { - std::cerr << "CreateGxsChannelMsg::loadChannelPostInfo() ERROR INVALID post ID or channel ID" << std::endl; - return ; - } + RsQThreadUtils::postToObject( [posts,this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ - subjectEdit->setText(QString::fromUtf8(post.mMeta.mMsgName.c_str())) ; - RichTextEditWidget->setText(QString::fromUtf8(post.mMsg.c_str())); + const RsGxsChannelPost& post(posts[0]); - for(std::list::const_iterator it(post.mFiles.begin());it!=post.mFiles.end();++it) - addAttachment(it->mHash,it->mName,it->mSize,true,RsPeerId(),true); + if(post.mMeta.mGroupId != mChannelId || post.mMeta.mMsgId != mOrigPostId) + { + std::cerr << "CreateGxsChannelMsg::loadChannelPostInfo() ERROR INVALID post ID or channel ID" << std::endl; + return ; + } + + subjectEdit->setText(QString::fromUtf8(post.mMeta.mMsgName.c_str())) ; + RichTextEditWidget->setText(QString::fromUtf8(post.mMsg.c_str())); + + for(std::list::const_iterator it(post.mFiles.begin());it!=post.mFiles.end();++it) + addAttachment(it->mHash,it->mName,it->mSize,true,RsPeerId(),true); + + if(post.mThumbnail.mData != NULL) + { + GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData,post.mThumbnail.mSize,picture,GxsIdDetails::ORIGINAL); + preview_W->setPixmap(picture,true); + } + + + }, this ); + }); - if(post.mThumbnail.mData != NULL) - { - GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData,post.mThumbnail.mSize,picture,GxsIdDetails::ORIGINAL); - thumbnail_label->setPixmap(picture); - } } -void CreateGxsChannelMsg::loadChannelInfo(const uint32_t &token) +void CreateGxsChannelMsg::loadChannelInfo() { #ifdef DEBUG_CREATE_GXS_MSG std::cerr << "CreateGxsChannelMsg::loadChannelInfo()"; std::cerr << std::endl; #endif - std::list groupInfo; - rsGxsChannels->getGroupSummary(token, groupInfo); - if (groupInfo.size() == 1) + RsThread::async([this]() { - RsGroupMetaData fi = groupInfo.front(); - saveChannelInfo(fi); - } - else - { - std::cerr << "CreateGxsChannelMsg::loadForumInfo() ERROR INVALID Number of Forums"; - std::cerr << std::endl; - } -} + std::vector groups; -void CreateGxsChannelMsg::loadRequest(const TokenQueue *queue, const TokenRequest &req) -{ -#ifdef DEBUG_CREATE_GXS_MSG - std::cerr << "CreateGxsChannelMsg::loadRequest() UserType: " << req.mUserType; - std::cerr << std::endl; -#endif + if( !rsGxsChannels->getChannelsInfo(std::list({mChannelId}),groups) || groups.size() != 1) + { + std::cerr << "Cannot get channel group data for channel " << mChannelId << std::endl; + return; + } - if (queue == mChannelQueue) - { - /* now switch on req */ - switch(req.mUserType) + RsQThreadUtils::postToObject( [groups,this]() { - case CREATEMSG_CHANNELINFO: - loadChannelInfo(req.mToken); - break; - case CREATEMSG_CHANNEL_POST_INFO: - loadChannelPostInfo(req.mToken); - break; - default: - std::cerr << "CreateGxsChannelMsg::loadRequest() UNKNOWN UserType "; + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ + + if (groups.size() == 1) + { + const RsGroupMetaData& fi = groups.front().mMeta; + saveChannelInfo(fi); + } + else + { + std::cerr << "CreateGxsChannelMsg::loadForumInfo() ERROR INVALID Number of Forums"; std::cerr << std::endl; - } - } + } + + }, this ); + }); } void CreateGxsChannelMsg::on_channelpostButton_clicked() diff --git a/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.h b/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.h index 1347a96c2..8f8b6e41c 100644 --- a/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.h +++ b/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.h @@ -22,7 +22,6 @@ #define _CREATE_GXSCHANNEL_MSG_H #include "ui_CreateGxsChannelMsg.h" -#include "util/TokenQueue.h" #include #ifdef CHANNELS_FRAME_CATCHER @@ -31,7 +30,7 @@ class SubFileItem; -class CreateGxsChannelMsg : public QDialog, public TokenResponse, private Ui::CreateGxsChannelMsg +class CreateGxsChannelMsg : public QDialog, private Ui::CreateGxsChannelMsg { Q_OBJECT @@ -51,9 +50,6 @@ public: QPixmap picture; - // overload from TokenResponse - virtual void loadRequest(const TokenQueue*, const TokenRequest&); - protected: virtual void dragEnterEvent(QDragEnterEvent *event); virtual void dropEvent(QDropEvent *event); @@ -62,11 +58,13 @@ private slots: void addExtraFile(); void checkAttachmentReady(); void deleteAttachment(); + void updatePreviewText(const QString &); void cancelMsg(); void sendMsg(); void pasteLink() ; void contextMenu(QPoint) ; + void changeAspectRatio(int s); void addThumbnail(); void allowAutoMediaThumbNail(bool); @@ -74,8 +72,9 @@ private slots: void on_channelpostButton_clicked(); void on_attachmentsButton_clicked(); private: - void loadChannelInfo(const uint32_t &token); - void loadChannelPostInfo(const uint32_t &token); + void processSettings(bool load); + void loadChannelInfo(); + void loadOriginalChannelPostInfo(); void saveChannelInfo(const RsGroupMetaData &group); void parseRsFileListAttachments(const std::string &attachList); @@ -93,8 +92,6 @@ private: bool mCheckAttachment; bool mAutoMediaThumbNail; - TokenQueue *mChannelQueue; - #ifdef CHANNELS_FRAME_CATCHER framecatcher* fCatcher; #endif diff --git a/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.ui b/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.ui index 5086f4acb..9d296666c 100644 --- a/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.ui +++ b/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.ui @@ -6,8 +6,8 @@ 0 0 - 671 - 513 + 881 + 328
@@ -20,7 +20,7 @@ :/images/logo/logo_16.png:/images/logo/logo_16.png - + 0 @@ -33,16 +33,12 @@ 0 - + + 6 + + 0 - - - - QFrame::Plain - - - @@ -52,32 +48,20 @@ QFrame::Raised - - - - Channel Post - - - - :/icons/png/comment.png:/icons/png/comment.png - - - - 24 - 24 - - - - + + 3 + + + 6 + + + 0 + Attachments - - - :/icons/png/attachements.png:/icons/png/attachements.png - 24 @@ -86,19 +70,6 @@ - - - - Qt::Horizontal - - - - 486 - 20 - - - - @@ -114,129 +85,45 @@ 0 - + 0 - - 0 - 0 - - 0 - - - - - - - - 156 - 107 - - - - - 0 - 0 - - - - :/images/thumb-default-video.png + + + + + + + 0 + 0 + - - - - <!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:'Ubuntu'; font-size:11pt; 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:'MS Shell Dlg 2'; font-size:10pt; font-weight:600;">Attachments:</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><img src=":/images/feedback_arrow.png" /><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;"> Use Drag and Drop / Add Files button, to Hash new files.</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><img src=":/images/feedback_arrow.png" /><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;"> Copy/Paste RetroShare links from your shares</span></p></body></html> - - - - - - - Add Channel Thumbnail - - - - :/icons/png/add-image.png:/icons/png/add-image.png - - - - 24 - 24 - - - - - - - - Add File to Attach - - - - :/icons/png/add-file.png:/icons/png/add-file.png - - - - 24 - 24 - - - - - - + + - Qt::Horizontal - - - QSizePolicy::Expanding + Qt::Vertical - 40 - 20 + 20 + 1 - - - - Message - - - - 0 - - - - - Title - - - - - - - - - - + + + 6 + @@ -268,6 +155,119 @@ p, li { white-space: pre-wrap; } + + + + <!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:'Ubuntu'; font-size:11pt; 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:'MS Shell Dlg 2'; font-size:10pt; font-weight:600;">Attachments:</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><img src=":/images/feedback_arrow.png" /><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;"> Use Drag and Drop / Add Files button, to Hash new files.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><img src=":/images/feedback_arrow.png" /><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;"> Copy/Paste RetroShare links from your shares</span></p></body></html> + + + + + + + + + Add Channel Thumbnail + + + + 24 + 24 + + + + + + + + + 24 + 24 + + + + + Auto + + + + + 1:1 + + + + + 3:4 + + + + + 16:9 + + + + + + + + Add File to Attach + + + + 24 + 24 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 40 + 20 + + + + + + + + + + 9 + + + + + Title + + + + + + + + + + 0 + 0 + + + + @@ -276,7 +276,7 @@ p, li { white-space: pre-wrap; } 0 - 0 + 6 0 @@ -285,48 +285,6 @@ p, li { white-space: pre-wrap; } 0 - - - - - - :/images/attachment.png - - - - - - - - 16777215 - 26 - - - - - 75 - true - - - - Attachments - - - - - - - Qt::Horizontal - - - - 334 - 26 - - - - - @@ -340,11 +298,14 @@ p, li { white-space: pre-wrap; } 34 + + <html><head/><body><p>Add File</p></body></html> + - + :/icons/png/add-file.png:/icons/png/add-file.png @@ -355,16 +316,6 @@ p, li { white-space: pre-wrap; } - - - - Allow channels to get frame for message thumbnail from movie media attachments or not - - - Auto Thumbnail - - - @@ -378,7 +329,7 @@ p, li { white-space: pre-wrap; } 0 0 - 81 + 842 24 @@ -425,10 +376,59 @@ p, li { white-space: pre-wrap; } + + + + Qt::Horizontal + + + + 334 + 26 + + + + + + + + Allow channels to get frame for message thumbnail from movie media attachments or not + + + Auto Thumbnail + + +
+ + + + Qt::Horizontal + + + + 486 + 20 + + + + + + + + Channel Post + + + + 24 + 24 + + + + @@ -466,6 +466,13 @@ p, li { white-space: pre-wrap; } + + + + QFrame::Plain + + + @@ -481,9 +488,14 @@ p, li { white-space: pre-wrap; }
util/RichTextEdit.h
1 + + ChannelPostThumbnailView + QWidget +
gui/gxschannels/GxsChannelPostThumbnail.h
+ 1 +
- diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.cpp index e8534e8f7..09dec15c7 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.cpp @@ -26,7 +26,7 @@ #include "GxsChannelDialog.h" #include "GxsChannelGroupDialog.h" -#include "GxsChannelPostsWidget.h" +#include "GxsChannelPostsWidgetWithModel.h" #include "CreateGxsChannelMsg.h" #include "GxsChannelUserNotify.h" #include "gui/gxs/GxsGroupShareKey.h" @@ -36,49 +36,38 @@ #include "gui/common/GroupTreeWidget.h" #include "util/qtthreadsutils.h" -class GxsChannelGroupInfoData : public RsUserdata -{ -public: - GxsChannelGroupInfoData() : RsUserdata() {} - -public: - QMap mIcon; - QMap mDescription; -}; +// class GxsChannelGroupInfoData : public RsUserdata +// { +// public: +// GxsChannelGroupInfoData() : RsUserdata() {} +// +// public: +// QMap mIcon; +// QMap mDescription; +// }; /** Constructor */ -GxsChannelDialog::GxsChannelDialog(QWidget *parent) - : GxsGroupFrameDialog(rsGxsChannels, parent,true) +GxsChannelDialog::GxsChannelDialog(QWidget *parent): + GxsGroupFrameDialog(rsGxsChannels, parent, true), mEventHandlerId(0) { - mEventHandlerId = 0; - // Needs to be asynced because this function is likely to be called by another thread! - rsEvents->registerEventsHandler(RsEventType::GXS_CHANNELS, [this](std::shared_ptr event) { RsQThreadUtils::postToObject( [=]() { handleEvent_main_thread(event); }, this ); }, mEventHandlerId ); + // Needs to be asynced because this function is called by another thread! + rsEvents->registerEventsHandler( + [this](std::shared_ptr event) + { RsQThreadUtils::postToObject([=]() { handleEvent_main_thread(event); }, this ); }, + mEventHandlerId, RsEventType::GXS_CHANNELS ); } void GxsChannelDialog::handleEvent_main_thread(std::shared_ptr event) { const RsGxsChannelEvent *e = dynamic_cast(event.get()); - if(!e) - return; - + if(e) switch(e->mChannelEventCode) { - case RsChannelEventCode::NEW_MESSAGE: - updateMessageSummaryList(e->mChannelGroupId); - break; - - case RsChannelEventCode::UPDATED_MESSAGE: // [[fallthrough]]; - updateDisplay(false); - break; - - case RsChannelEventCode::READ_STATUS_CHANGED: - updateMessageSummaryList(e->mChannelGroupId); - break; - - case RsChannelEventCode::RECEIVED_DISTANT_SEARCH_RESULT: - mSearchResults.insert(e->mDistantSearchRequestId); - updateSearchResults(); + case RsChannelEventCode::NEW_MESSAGE: // [[fallthrough]]; + case RsChannelEventCode::UPDATED_MESSAGE: // [[fallthrough]]; + case RsChannelEventCode::READ_STATUS_CHANGED: // [[fallthrough]]; + updateGroupStatisticsReal(e->mChannelGroupId); // update the list immediately break; case RsChannelEventCode::NEW_CHANNEL: // [[fallthrough]]; @@ -86,9 +75,20 @@ void GxsChannelDialog::handleEvent_main_thread(std::shared_ptr ev updateDisplay(true); break; + case RsChannelEventCode::STATISTICS_CHANGED: + updateGroupStatistics(e->mChannelGroupId); + break; + default: break; } + + + const RsGxsChannelSearchResultEvent*f = dynamic_cast(event.get()); + + if(nullptr != f) + for(auto it:f->mSearchResultsMap) + updateSearchResults(it.first); } GxsChannelDialog::~GxsChannelDialog() @@ -108,6 +108,7 @@ QString GxsChannelDialog::getHelpString() const

Channels can be made anonymous, or attached to a Retroshare identity so that readers can contact you if needed.\ Enable \"Allow Comments\" if you want to let users comment on your posts.

\

Channel posts are kept for %1 days, and sync-ed over the last %2 days, unless you change this.

\ +

UI Tip: use Control + mouse wheel to control image size in the thumbnail view.

\ ").arg(QString::number(rsGxsChannels->getDefaultStoragePeriod()/86400)).arg(QString::number(rsGxsChannels->getDefaultSyncPeriod()/86400)); return hlp_str ; @@ -115,7 +116,7 @@ QString GxsChannelDialog::getHelpString() const UserNotify *GxsChannelDialog::createUserNotify(QObject *parent) { - return new GxsChannelUserNotify(rsGxsChannels, parent); + return new GxsChannelUserNotify(rsGxsChannels,this, parent); } void GxsChannelDialog::shareOnChannel(const RsGxsGroupId& channel_id,const QList& file_links) @@ -171,30 +172,30 @@ QString GxsChannelDialog::icon(IconType type) case ICON_NEW: return ":/icons/png/add.png"; case ICON_YOUR_GROUP: - return ":/icons/png/channel.png"; + return ""; case ICON_SUBSCRIBED_GROUP: - return ":/icons/png/channel-subscribed.png"; + return ""; case ICON_POPULAR_GROUP: - return ":/icons/png/channel-popular.png"; + return ""; case ICON_OTHER_GROUP: - return ":/icons/png/channel-other.png"; + return ""; case ICON_SEARCH: return ":/images/find.png"; case ICON_DEFAULT: - return ":/images/channels.png"; + return ":/icons/png/channels.png"; } return ""; } -GxsGroupDialog *GxsChannelDialog::createNewGroupDialog(TokenQueue *tokenQueue) +GxsGroupDialog *GxsChannelDialog::createNewGroupDialog() { - return new GxsChannelGroupDialog(tokenQueue, this); + return new GxsChannelGroupDialog(this); } -GxsGroupDialog *GxsChannelDialog::createGroupDialog(TokenQueue *tokenQueue, RsTokenService *tokenService, GxsGroupDialog::Mode mode, RsGxsGroupId groupId) +GxsGroupDialog *GxsChannelDialog::createGroupDialog(GxsGroupDialog::Mode mode, RsGxsGroupId groupId) { - return new GxsChannelGroupDialog(tokenQueue, tokenService, mode, groupId, this); + return new GxsChannelGroupDialog(mode, groupId, this); } int GxsChannelDialog::shareKeyType() @@ -204,7 +205,7 @@ int GxsChannelDialog::shareKeyType() GxsMessageFrameWidget *GxsChannelDialog::createMessageFrameWidget(const RsGxsGroupId &groupId) { - return new GxsChannelPostsWidget(groupId); + return new GxsChannelPostsWidgetWithModel(groupId,this); } void GxsChannelDialog::setDefaultDirectory() @@ -251,8 +252,8 @@ void GxsChannelDialog::groupTreeCustomActions(RsGxsGroupId grpId, int subscribeF if (isSubscribed) { - QAction *action = autoDownload ? (new QAction(QIcon(":/images/redled.png"), tr("Disable Auto-Download"), this)) - : (new QAction(QIcon(":/images/start.png"),tr("Enable Auto-Download"), this)); + QAction *action = autoDownload ? (new QAction(FilesDefs::getIconFromQtResourcePath(":/images/redled.png"), tr("Disable Auto-Download"), this)) + : (new QAction(FilesDefs::getIconFromQtResourcePath(":/images/start.png"),tr("Enable Auto-Download"), this)); connect(action, SIGNAL(triggered()), this, SLOT(toggleAutoDownload())); actions.append(action); @@ -263,7 +264,7 @@ void GxsChannelDialog::groupTreeCustomActions(RsGxsGroupId grpId, int subscribeF QMenu *mnu = new QMenu(tr("Set download directory")) ; if(dl_directory.empty()) - mnu->addAction(QIcon(":/images/start.png"),tr("[Default directory]"), this, SLOT(setDefaultDirectory())) ; + mnu->addAction(FilesDefs::getIconFromQtResourcePath(":/images/start.png"),tr("[Default directory]"), this, SLOT(setDefaultDirectory())) ; else mnu->addAction(tr("[Default directory]"), this, SLOT(setDefaultDirectory())) ; @@ -277,7 +278,7 @@ void GxsChannelDialog::groupTreeCustomActions(RsGxsGroupId grpId, int subscribeF if(dl_directory == it->filename) { - action = new QAction(QIcon(":/images/start.png"),QString::fromUtf8(it->filename.c_str()),NULL) ; + action = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/start.png"),QString::fromUtf8(it->filename.c_str()),NULL) ; found = true ; } else @@ -291,7 +292,7 @@ void GxsChannelDialog::groupTreeCustomActions(RsGxsGroupId grpId, int subscribeF if(!found && !dl_directory.empty()) { - QAction *action = new QAction(QIcon(":/images/start.png"),QString::fromUtf8(dl_directory.c_str()),NULL) ; + QAction *action = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/start.png"),QString::fromUtf8(dl_directory.c_str()),NULL) ; connect(action,SIGNAL(triggered()),this,SLOT(setDownloadDirectory())) ; action->setData(QString::fromUtf8(dl_directory.c_str())) ; @@ -350,52 +351,55 @@ void GxsChannelDialog::toggleAutoDownload() }); } -void GxsChannelDialog::loadGroupSummaryToken(const uint32_t &token, std::list &groupInfo, RsUserdata *&userdata) +bool GxsChannelDialog::getGroupStatistics(const RsGxsGroupId& groupId,GxsGroupStatistic& stat) { - std::vector groups; - rsGxsChannels->getGroupData(token, groups); - - /* Save groups to fill icons and description */ - GxsChannelGroupInfoData *channelData = new GxsChannelGroupInfoData; - userdata = channelData; - - std::vector::iterator groupIt; - for (groupIt = groups.begin(); groupIt != groups.end(); ++groupIt) { - RsGxsChannelGroup &group = *groupIt; - groupInfo.push_back(group.mMeta); - - if (group.mImage.mData != NULL) { - QPixmap image; - GxsIdDetails::loadPixmapFromData(group.mImage.mData, group.mImage.mSize, image,GxsIdDetails::ORIGINAL); - channelData->mIcon[group.mMeta.mGroupId] = image; - } - - if (!group.mDescription.empty()) { - channelData->mDescription[group.mMeta.mGroupId] = QString::fromUtf8(group.mDescription.c_str()); - } - } + return rsGxsChannels->getChannelStatistics(groupId,stat); } -void GxsChannelDialog::groupInfoToGroupItemInfo(const RsGroupMetaData &groupInfo, GroupItemInfo &groupItemInfo, const RsUserdata *userdata) +bool GxsChannelDialog::getGroupData(std::list& groupInfo) { - GxsGroupFrameDialog::groupInfoToGroupItemInfo(groupInfo, groupItemInfo, userdata); + std::vector groups; - const GxsChannelGroupInfoData *channelData = dynamic_cast(userdata); - if (!channelData) { - std::cerr << "GxsChannelDialog::groupInfoToGroupItemInfo() Failed to cast data to GxsChannelGroupInfoData"; - std::cerr << std::endl; + // request all group infos at once + + if(! rsGxsChannels->getChannelsInfo(std::list(),groups)) + return false; + + /* Save groups to fill icons and description */ + + for (auto& group: groups) + groupInfo.push_back(new RsGxsChannelGroup(group)); + + return true; +} + +void GxsChannelDialog::groupInfoToGroupItemInfo(const RsGxsGenericGroupData *groupData, GroupItemInfo &groupItemInfo) +{ + GxsGroupFrameDialog::groupInfoToGroupItemInfo(groupData, groupItemInfo); + + const RsGxsChannelGroup *channelGroupData = dynamic_cast(groupData); + + if (!channelGroupData) + { + std::cerr << "GxsChannelDialog::groupInfoToGroupItemInfo() Failed to cast data to GxsChannelGroupInfoData"<< std::endl; return; } - QMap::const_iterator descriptionIt = channelData->mDescription.find(groupInfo.mGroupId); - if (descriptionIt != channelData->mDescription.end()) { - groupItemInfo.description = descriptionIt.value(); + if(channelGroupData->mImage.mSize > 0) + { + QPixmap image; + GxsIdDetails::loadPixmapFromData(channelGroupData->mImage.mData, channelGroupData->mImage.mSize, image,GxsIdDetails::ORIGINAL); + groupItemInfo.icon = image; } + else + groupItemInfo.icon = FilesDefs::getIconFromQtResourcePath(":icons/png/channel.png"); - QMap::const_iterator iconIt = channelData->mIcon.find(groupInfo.mGroupId); - if (iconIt != channelData->mIcon.end()) { - groupItemInfo.icon = iconIt.value(); - } + groupItemInfo.description = QString::fromUtf8(channelGroupData->mDescription.c_str()); +} + +void GxsChannelDialog::clearDistantSearchResults(TurtleRequestId id) +{ + rsGxsChannels->clearDistantSearchResults(id); } TurtleRequestId GxsChannelDialog::distantSearch(const QString& search_string) @@ -403,16 +407,26 @@ TurtleRequestId GxsChannelDialog::distantSearch(const QString& search_string) return rsGxsChannels->turtleSearchRequest(search_string.toStdString()) ; } -bool GxsChannelDialog::getDistantSearchResults(TurtleRequestId id, std::map& group_infos) +bool GxsChannelDialog::getDistantSearchResults(TurtleRequestId id, std::map& group_infos) { return rsGxsChannels->retrieveDistantSearchResults(id,group_infos); } +RsGxsGenericGroupData *GxsChannelDialog::getDistantSearchResultGroupData(const RsGxsGroupId& group_id) +{ + RsGxsChannelGroup channel_group; + + if(rsGxsChannels->getDistantSearchResultGroupData(group_id,channel_group)) + return new RsGxsGenericGroupData(channel_group); + else + return nullptr; +} + void GxsChannelDialog::checkRequestGroup(const RsGxsGroupId& grpId) { RsGxsChannelGroup distant_group; - if( rsGxsChannels->retrieveDistantGroup(grpId,distant_group)) // normally we should also check that the group meta is not already here. + if( rsGxsChannels->getDistantSearchResultGroupData(grpId,distant_group)) // normally we should also check that the group meta is not already here. { std::cerr << "GxsChannelDialog::checkRequestGroup() sending turtle request for group data for group " << grpId << std::endl; rsGxsChannels->turtleGroupRequest(grpId); diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.h b/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.h index 1ab59ca1e..7dab607be 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.h @@ -35,25 +35,32 @@ public: /** Default Destructor */ ~GxsChannelDialog(); - virtual QIcon iconPixmap() const { return QIcon(IMAGE_GXSCHANNELS) ; } //MainPage - virtual QString pageName() const { return tr("Channels") ; } //MainPage - virtual QString helpText() const { return ""; } //MainPage + virtual QIcon iconPixmap() const override { return QIcon(IMAGE_GXSCHANNELS) ; } //MainPage + virtual QString pageName() const override { return tr("Channels") ; } //MainPage + virtual QString helpText() const override { return ""; } //MainPage void shareOnChannel(const RsGxsGroupId& channel_id, const QList& file_link) ; protected: /* GxsGroupFrameDialog */ - virtual RetroShareLink::enumType getLinkType() { return RetroShareLink::TYPE_CHANNEL; } - virtual GroupFrameSettings::Type groupFrameSettingsType() { return GroupFrameSettings::Channel; } - virtual QString getHelpString() const ; - virtual void groupInfoToGroupItemInfo(const RsGroupMetaData &groupInfo, GroupItemInfo &groupItemInfo, const RsUserdata *userdata); - virtual bool getDistantSearchResults(TurtleRequestId id, std::map& group_infos); - virtual const std::set getSearchResults() const override { return mSearchResults ; } + virtual bool getDistantSearchResults(TurtleRequestId id, std::map &group_infos) override; + virtual RsGxsGenericGroupData *getDistantSearchResultGroupData(const RsGxsGroupId& group_id) override; - virtual TurtleRequestId distantSearch(const QString& search_string) ; - virtual void checkRequestGroup(const RsGxsGroupId& grpId) ; + virtual TurtleRequestId distantSearch(const QString& search_string) override; + virtual void checkRequestGroup(const RsGxsGroupId& grpId) override ; + virtual void clearDistantSearchResults(TurtleRequestId id) override; + + // Implementation of some abstract methods in GxsGroupFrameDialog + + virtual QString getHelpString() const override; + GroupFrameSettings::Type groupFrameSettingsType() override { return GroupFrameSettings::Channel; } + RetroShareLink::enumType getLinkType() override { return RetroShareLink::TYPE_CHANNEL; } + void groupInfoToGroupItemInfo(const RsGxsGenericGroupData *groupData, GroupItemInfo &groupItemInfo) override; + const std::set getSearchRequests() const override { return mSearchResults ; } + UserNotify *createUserNotify(QObject *parent) override; + bool getGroupData(std::list& groupInfo) override; + bool getGroupStatistics(const RsGxsGroupId& groupId,GxsGroupStatistic& stat) override; - virtual UserNotify *createUserNotify(QObject *parent) override; private slots: void toggleAutoDownload(); void setDefaultDirectory(); @@ -62,18 +69,17 @@ private slots: private: /* GxsGroupFrameDialog */ - virtual QString text(TextType type); - virtual QString icon(IconType type); - virtual QString settingsGroupName() { return "ChannelDialog"; } - virtual GxsGroupDialog *createNewGroupDialog(TokenQueue *tokenQueue); - virtual GxsGroupDialog *createGroupDialog(TokenQueue *tokenQueue, RsTokenService *tokenService, GxsGroupDialog::Mode mode, RsGxsGroupId groupId); - virtual int shareKeyType(); - virtual GxsMessageFrameWidget *createMessageFrameWidget(const RsGxsGroupId &groupId); - virtual void groupTreeCustomActions(RsGxsGroupId grpId, int subscribeFlags, QList &actions); - virtual RsGxsCommentService *getCommentService(); - virtual QWidget *createCommentHeaderWidget(const RsGxsGroupId &grpId, const RsGxsMessageId &msgId); - virtual uint32_t requestGroupSummaryType() { return GXS_REQUEST_TYPE_GROUP_DATA; } // request complete group data - virtual void loadGroupSummaryToken(const uint32_t &token, std::list &groupInfo, RsUserdata* &userdata); + virtual QString text(TextType type) override; + virtual QString icon(IconType type) override; + virtual QString settingsGroupName() override { return "ChannelDialog"; } + virtual GxsGroupDialog *createNewGroupDialog() override; + virtual GxsGroupDialog *createGroupDialog(GxsGroupDialog::Mode mode, RsGxsGroupId groupId) override; + virtual int shareKeyType() override; + virtual GxsMessageFrameWidget *createMessageFrameWidget(const RsGxsGroupId &groupId) override; + virtual void groupTreeCustomActions(RsGxsGroupId grpId, int subscribeFlags, QList &actions) override; + virtual RsGxsCommentService *getCommentService() override; + virtual QWidget *createCommentHeaderWidget(const RsGxsGroupId &grpId, const RsGxsMessageId &msgId) override; + virtual uint32_t requestGroupSummaryType() override { return GXS_REQUEST_TYPE_GROUP_DATA; } // request complete group data void handleEvent_main_thread(std::shared_ptr event); diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.cpp index 6dfe8a673..bc019e426 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.cpp @@ -18,6 +18,7 @@ * * *******************************************************************************/ +#include #include #include #include @@ -27,11 +28,13 @@ #include "GxsChannelFilesStatusWidget.h" #include "ui_GxsChannelFilesStatusWidget.h" #include "gui/common/RsUrlHandler.h" +#include "gui/common/FilesDefs.h" +#include "util/misc.h" #include "retroshare/rsfiles.h" -GxsChannelFilesStatusWidget::GxsChannelFilesStatusWidget(const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, const RsGxsFile &file, QWidget *parent) : - QWidget(parent), mGroupId(groupId), mMessageId(messageId), mFile(file), ui(new Ui::GxsChannelFilesStatusWidget) +GxsChannelFilesStatusWidget::GxsChannelFilesStatusWidget(const RsGxsFile &file, QWidget *parent) : + QWidget(parent), mFile(file), ui(new Ui::GxsChannelFilesStatusWidget) { ui->setupUi(this); @@ -40,11 +43,21 @@ GxsChannelFilesStatusWidget::GxsChannelFilesStatusWidget(const RsGxsGroupId &gro setSize(mFile.mSize); /* Connect signals */ - connect(ui->downloadToolButton, SIGNAL(clicked()), this, SLOT(download())); + connect(ui->downloadPushButton, SIGNAL(clicked()), this, SLOT(download())); connect(ui->resumeToolButton, SIGNAL(clicked()), this, SLOT(resume())); connect(ui->pauseToolButton, SIGNAL(clicked()), this, SLOT(pause())); connect(ui->cancelToolButton, SIGNAL(clicked()), this, SLOT(cancel())); - connect(ui->openFolderToolButton, SIGNAL(clicked()), this, SLOT(openFolder())); + connect(ui->openFilePushButton, SIGNAL(clicked()), this, SLOT(openFile())); + + ui->downloadPushButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/download.png")); + ui->openFolderToolButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/arrow.png")); + + QAction *openfolder = new QAction(tr("Open folder"), this); + connect(openfolder, SIGNAL(triggered()), this, SLOT(openFolder())); + + QMenu *menu = new QMenu(); + menu->addAction(openfolder); + ui->openFolderToolButton->setMenu(menu); check(); } @@ -80,6 +93,17 @@ void GxsChannelFilesStatusWidget::check() if (rsFiles->alreadyHaveFile(mFile.mHash, fileInfo)) { mState = STATE_LOCAL; setSize(fileInfo.size); + + /* check if the file is a media file */ + if (!misc::isPreviewable(QFileInfo(QString::fromUtf8(fileInfo.path.c_str())).suffix())) + { + /* check if the file is not a media file and change text */ + ui->openFilePushButton->setText(tr("Open file")); + } else { + ui->openFilePushButton->setText(tr("Play")); + ui->openFilePushButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/play.png")); + } + } else { FileInfo fileInfo; bool detailsOk = rsFiles->FileDetails(mFile.mHash, RS_FILE_HINTS_DOWNLOAD | RS_FILE_HINTS_SPEC_ONLY, fileInfo); @@ -126,11 +150,12 @@ void GxsChannelFilesStatusWidget::check() case STATE_ERROR: repeat = 0; - ui->downloadToolButton->hide(); + ui->downloadPushButton->hide(); ui->resumeToolButton->hide(); ui->pauseToolButton->hide(); ui->cancelToolButton->hide(); ui->progressBar->hide(); + ui->openFilePushButton->hide(); ui->openFolderToolButton->hide(); statusText = tr("Error"); @@ -140,11 +165,12 @@ void GxsChannelFilesStatusWidget::check() case STATE_REMOTE: repeat = 30000; - ui->downloadToolButton->show(); + ui->downloadPushButton->show(); ui->resumeToolButton->hide(); ui->pauseToolButton->hide(); ui->cancelToolButton->hide(); ui->progressBar->hide(); + ui->openFilePushButton->hide(); ui->openFolderToolButton->hide(); break; @@ -152,11 +178,12 @@ void GxsChannelFilesStatusWidget::check() case STATE_DOWNLOAD: repeat = 1000; - ui->downloadToolButton->hide(); + ui->downloadPushButton->hide(); ui->resumeToolButton->hide(); ui->pauseToolButton->show(); ui->cancelToolButton->show(); ui->progressBar->show(); + ui->openFilePushButton->hide(); ui->openFolderToolButton->hide(); break; @@ -164,11 +191,12 @@ void GxsChannelFilesStatusWidget::check() case STATE_PAUSED: repeat = 1000; - ui->downloadToolButton->hide(); + ui->downloadPushButton->hide(); ui->resumeToolButton->show(); ui->pauseToolButton->hide(); ui->cancelToolButton->show(); ui->progressBar->hide(); + ui->openFilePushButton->hide(); ui->openFolderToolButton->hide(); statusText = tr("Paused"); @@ -178,11 +206,12 @@ void GxsChannelFilesStatusWidget::check() case STATE_WAITING: repeat = 1000; - ui->downloadToolButton->hide(); + ui->downloadPushButton->hide(); ui->resumeToolButton->hide(); ui->pauseToolButton->show(); ui->cancelToolButton->show(); ui->progressBar->hide(); + ui->openFilePushButton->hide(); ui->openFolderToolButton->hide(); statusText = tr("Waiting"); @@ -192,11 +221,12 @@ void GxsChannelFilesStatusWidget::check() case STATE_CHECKING: repeat = 1000; - ui->downloadToolButton->hide(); + ui->downloadPushButton->hide(); ui->resumeToolButton->hide(); ui->pauseToolButton->hide(); ui->cancelToolButton->show(); ui->progressBar->hide(); + ui->openFilePushButton->hide(); ui->openFolderToolButton->hide(); statusText = tr("Checking"); @@ -206,11 +236,12 @@ void GxsChannelFilesStatusWidget::check() case STATE_LOCAL: repeat = 60000; - ui->downloadToolButton->hide(); + ui->downloadPushButton->hide(); ui->resumeToolButton->hide(); ui->pauseToolButton->hide(); ui->cancelToolButton->hide(); ui->progressBar->hide(); + ui->openFilePushButton->show(); ui->openFolderToolButton->show(); break; @@ -296,3 +327,24 @@ void GxsChannelFilesStatusWidget::openFolder() } } } + +void GxsChannelFilesStatusWidget::openFile() +{ + FileInfo fileInfo; + if (!rsFiles->alreadyHaveFile(mFile.mHash, fileInfo)) { + return; + } + + /* open file with a suitable application */ + QFileInfo qinfo; + qinfo.setFile(QString::fromUtf8(fileInfo.path.c_str())); + if (qinfo.exists()) { + if (!RsUrlHandler::openUrl(QUrl::fromLocalFile(qinfo.absoluteFilePath()))) { + std::cerr << "GxsChannelFilesStatusWidget(): can't open file " << fileInfo.path << std::endl; + } + }else{ + QMessageBox::information(this, tr("Play File"), + tr("File %1 does not exist at location.").arg(fileInfo.path.c_str())); + return; + } +} diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.h b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.h index 1fc24437f..1effe0549 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.h @@ -34,7 +34,7 @@ class GxsChannelFilesStatusWidget : public QWidget Q_OBJECT public: - explicit GxsChannelFilesStatusWidget(const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, const RsGxsFile &file, QWidget *parent = 0); + explicit GxsChannelFilesStatusWidget(const RsGxsFile &file, QWidget *parent = 0); ~GxsChannelFilesStatusWidget(); private slots: @@ -44,6 +44,7 @@ private slots: void pause(); void resume(); void openFolder(); + void openFile(); private: void setSize(uint64_t size); diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.ui index 59fa846da..c1b02ab83 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.ui @@ -6,8 +6,8 @@ 0 0 - 367 - 27 + 421 + 29 @@ -48,7 +48,7 @@ 2 - + 0 @@ -126,18 +126,31 @@ - + 0 0 + + Play + + + + + + + + 0 + 0 + + Qt::NoFocus - - Open folder + + QToolButton::InstantPopup @@ -148,6 +161,7 @@ + diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesWidget.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesWidget.cpp deleted file mode 100644 index 9931811f8..000000000 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesWidget.cpp +++ /dev/null @@ -1,249 +0,0 @@ -/******************************************************************************* - * retroshare-gui/src/gui/gxschannels/GxsChannelFilesWidget.cpp * - * * - * Copyright 2014 by Retroshare Team * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Affero General Public License as * - * published by the Free Software Foundation, either version 3 of the * - * License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Affero General Public License for more details. * - * * - * You should have received a copy of the GNU Affero General Public License * - * along with this program. If not, see . * - * * - *******************************************************************************/ - -#include - -#include "GxsChannelFilesWidget.h" -#include "ui_GxsChannelFilesWidget.h" -#include "GxsChannelFilesStatusWidget.h" -#include "GxsChannelPostsWidget.h" -#include "gui/feeds/GxsChannelPostItem.h" -#include "gui/common/RSTreeWidgetItem.h" -#include "util/misc.h" -#include "util/DateTime.h" -#include "gui/gxs/GxsFeedItem.h" - -#include "retroshare/rsgxschannels.h" - -#define COLUMN_FILENAME 0 -#define COLUMN_SIZE 1 -#define COLUMN_STATUS 2 -#define COLUMN_TITLE 3 -#define COLUMN_PUBLISHED 4 -#define COLUMN_COUNT 5 -#define COLUMN_DATA 0 - -#define ROLE_SORT Qt::UserRole -#define ROLE_GROUP_ID Qt::UserRole + 1 -#define ROLE_MESSAGE_ID Qt::UserRole + 2 -#define ROLE_FILE_HASH Qt::UserRole + 3 -#define ROLE_MSG Qt::UserRole + 4 - -GxsChannelFilesWidget::GxsChannelFilesWidget(QWidget *parent) : - QWidget(parent), ui(new Ui::GxsChannelFilesWidget) -{ - ui->setupUi(this); - - mFeedItem = NULL; - - /* Connect signals */ - connect(ui->treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*))); - - /* Sort */ - mCompareRole = new RSTreeWidgetItemCompareRole; - mCompareRole->setRole(COLUMN_SIZE, ROLE_SORT); - mCompareRole->setRole(COLUMN_PUBLISHED, ROLE_SORT); - - /* Filter */ - mFilterType = 0; - - /* Initialize file tree */ - ui->treeWidget->setColumnCount(COLUMN_COUNT); - QTreeWidgetItem *headerItem = ui->treeWidget->headerItem(); - headerItem->setText(COLUMN_FILENAME, tr("Filename")); - headerItem->setText(COLUMN_SIZE, tr("Size")); - headerItem->setText(COLUMN_TITLE, tr("Title")); - headerItem->setText(COLUMN_PUBLISHED, tr("Published")); - headerItem->setText(COLUMN_STATUS, tr("Status")); - - ui->treeWidget->setColumnWidth(COLUMN_FILENAME, 400); - ui->treeWidget->setColumnWidth(COLUMN_SIZE, 80); - ui->treeWidget->setColumnWidth(COLUMN_PUBLISHED, 150); -} - -GxsChannelFilesWidget::~GxsChannelFilesWidget() -{ - delete(mCompareRole); - delete ui; -} - -void GxsChannelFilesWidget::addFiles(const RsGxsChannelPost &post, bool related) -{ - if (related) { - removeItems(post.mMeta.mGroupId, post.mMeta.mMsgId); - } - - std::list::const_iterator fileIt; - for (fileIt = post.mFiles.begin(); fileIt != post.mFiles.end(); ++fileIt) { - const RsGxsFile &file = *fileIt; - - QTreeWidgetItem *treeItem = new RSTreeWidgetItem(mCompareRole); - - treeItem->setText(COLUMN_FILENAME, QString::fromUtf8(file.mName.c_str())); - treeItem->setText(COLUMN_SIZE, misc::friendlyUnit(file.mSize)); - treeItem->setData(COLUMN_SIZE, ROLE_SORT, (qulonglong)file.mSize); - treeItem->setText(COLUMN_TITLE, QString::fromUtf8(post.mMeta.mMsgName.c_str())); - treeItem->setText(COLUMN_PUBLISHED, DateTime::formatDateTime(post.mMeta.mPublishTs)); - treeItem->setData(COLUMN_PUBLISHED, ROLE_SORT, QDateTime::fromTime_t(post.mMeta.mPublishTs)); - - treeItem->setData(COLUMN_DATA, ROLE_GROUP_ID, qVariantFromValue(post.mMeta.mGroupId)); - treeItem->setData(COLUMN_DATA, ROLE_MESSAGE_ID, qVariantFromValue(post.mMeta.mMsgId)); - treeItem->setData(COLUMN_DATA, ROLE_FILE_HASH, qVariantFromValue(file.mHash)); - treeItem->setData(COLUMN_DATA, ROLE_MSG, QString::fromUtf8(post.mMsg.c_str())); - treeItem->setTextAlignment(COLUMN_SIZE, Qt::AlignRight) ; - - ui->treeWidget->addTopLevelItem(treeItem); - - QWidget *statusWidget = new GxsChannelFilesStatusWidget(post.mMeta.mGroupId, post.mMeta.mMsgId, file); - ui->treeWidget->setItemWidget(treeItem, COLUMN_STATUS, statusWidget); - - filterItem(treeItem); - } -} - -void GxsChannelFilesWidget::clear() -{ - ui->treeWidget->clear(); - closeFeedItem(); -} - -void GxsChannelFilesWidget::setFilter(const QString &text, int type) -{ - if (mFilterText == text && mFilterType == type) { - return; - } - - mFilterText = text; - mFilterType = type; - - filterItems(); -} - -void GxsChannelFilesWidget::setFilterText(const QString &text) -{ - setFilter(text, mFilterType); -} - -void GxsChannelFilesWidget::setFilterType(int type) -{ - setFilter(mFilterText, type); -} - -void GxsChannelFilesWidget::filterItems() -{ - QTreeWidgetItemIterator it(ui->treeWidget); - QTreeWidgetItem *treeItem; - while ((treeItem = *it) != NULL) { - ++it; - filterItem(treeItem); - } -} - -void GxsChannelFilesWidget::filterItem(QTreeWidgetItem *treeItem) -{ - bool visible = mFilterText.isEmpty(); - - switch (mFilterType) { - case GxsChannelPostsWidget::FILTER_TITLE: - visible = treeItem->text(COLUMN_TITLE).contains(mFilterText, Qt::CaseInsensitive); - break; - case GxsChannelPostsWidget::FILTER_MSG: - visible = treeItem->data(COLUMN_DATA, ROLE_MSG).toString().contains(mFilterText, Qt::CaseInsensitive); - break; - case GxsChannelPostsWidget::FILTER_FILE_NAME: - visible = treeItem->text(COLUMN_FILENAME).contains(mFilterText, Qt::CaseInsensitive); - break; - } - - treeItem->setHidden(!visible); -} - -//QTreeWidgetItem *GxsChannelFilesWidget::findFile(const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, const RsFileHash &fileHash) -//{ -// QTreeWidgetItemIterator it(ui->treeWidget); -// QTreeWidgetItem *treeItem; -// while ((treeItem = *it) != NULL) { -// ++it; - -// if (fileHash != treeItem->data(COLUMN_DATA, ROLE_FILE_HASH).value()) { -// continue; -// } -// if (messageId != treeItem->data(COLUMN_DATA, ROLE_MESSAGE_ID).value()) { -// continue; -// } -// if (groupId != treeItem->data(COLUMN_DATA, ROLE_GROUP_ID).value()) { -// continue; -// } - -// return treeItem; -// } - -// return NULL; -//} - -void GxsChannelFilesWidget::removeItems(const RsGxsGroupId &groupId, const RsGxsMessageId &messageId) -{ - QTreeWidgetItemIterator it(ui->treeWidget); - QTreeWidgetItem *treeItem; - while ((treeItem = *it) != NULL) { - ++it; - - if (messageId != treeItem->data(COLUMN_DATA, ROLE_MESSAGE_ID).value()) { - continue; - } - if (groupId != treeItem->data(COLUMN_DATA, ROLE_GROUP_ID).value()) { - continue; - } - - delete(treeItem); - } -} - -void GxsChannelFilesWidget::closeFeedItem() -{ - if (mFeedItem) { - delete(mFeedItem); - mFeedItem = NULL; - } - - ui->feedItemFrame->hide(); -} - -void GxsChannelFilesWidget::currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem */*previous*/) -{ - if (!current) { - closeFeedItem(); - return; - } - - RsGxsGroupId groupId = current->data(COLUMN_DATA, ROLE_GROUP_ID).value(); - RsGxsMessageId messageId = current->data(COLUMN_DATA, ROLE_MESSAGE_ID).value(); - - if (mFeedItem) { - if (mFeedItem->groupId() == groupId && mFeedItem->messageId() == messageId) { - return; - } - closeFeedItem(); - } - - mFeedItem = new GxsChannelPostItem(NULL, 0, groupId, messageId, true, true); - ui->feedItemFrame->show(); - ui->feedItemFrame->layout()->addWidget(mFeedItem); -} diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesWidget.h b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesWidget.h deleted file mode 100644 index cb8bd8d14..000000000 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesWidget.h +++ /dev/null @@ -1,76 +0,0 @@ -/******************************************************************************* - * retroshare-gui/src/gui/gxschannels/GxsChannelFilesWidget.h * - * * - * Copyright 2014 by Retroshare Team * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Affero General Public License as * - * published by the Free Software Foundation, either version 3 of the * - * License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Affero General Public License for more details. * - * * - * You should have received a copy of the GNU Affero General Public License * - * along with this program. If not, see . * - * * - *******************************************************************************/ - -#ifndef GXSCHANNELFILESWIDGET_H -#define GXSCHANNELFILESWIDGET_H - -#include - -#include "retroshare/rsgxsifacetypes.h" - -struct RsGxsChannelPost; -class RSTreeWidgetItemCompareRole; -class QTreeWidgetItem; -class GxsFeedItem; - -namespace Ui { -class GxsChannelFilesWidget; -} - -class GxsChannelFilesWidget : public QWidget -{ - Q_OBJECT - -public: - explicit GxsChannelFilesWidget(QWidget *parent = 0); - ~GxsChannelFilesWidget(); - - void addFiles(const RsGxsChannelPost &post, bool related); - void clear(); - -public slots: - void setFilter(const QString &text, int type); - void setFilterText(const QString &text); - void setFilterType(int type); - -private: -// QTreeWidgetItem *findFile(const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, const RsFileHash &fileHash); - void removeItems(const RsGxsGroupId &groupId, const RsGxsMessageId &messageId); - void closeFeedItem(); - void filterItems(); - void filterItem(QTreeWidgetItem *treeItem); - -private slots: - void currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); - -private: - /* Sort */ - RSTreeWidgetItemCompareRole *mCompareRole; - - /* Filter */ - QString mFilterText; - int mFilterType; - - GxsFeedItem *mFeedItem; - - Ui::GxsChannelFilesWidget *ui; -}; - -#endif // GXSCHANNELFILESWIDGET_H diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelGroupDialog.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelGroupDialog.cpp index 5732a706a..ef5f03e9a 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelGroupDialog.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelGroupDialog.cpp @@ -21,6 +21,7 @@ #include #include "gui/gxs/GxsIdDetails.h" +#include "gui/common/FilesDefs.h" #include "GxsChannelGroupDialog.h" #include @@ -59,15 +60,15 @@ const uint32_t ChannelCreateDefaultsFlags = ( GXS_GROUP_DEFAULTS_DISTRIB_PUBLIC const uint32_t ChannelEditEnabledFlags = ChannelCreateEnabledFlags; const uint32_t ChannelEditDefaultsFlags = ChannelCreateDefaultsFlags; -GxsChannelGroupDialog::GxsChannelGroupDialog(TokenQueue *tokenQueue, QWidget *parent) - : GxsGroupDialog(tokenQueue, ChannelCreateEnabledFlags, ChannelCreateDefaultsFlags, parent) +GxsChannelGroupDialog::GxsChannelGroupDialog(QWidget *parent) + : GxsGroupDialog(ChannelCreateEnabledFlags, ChannelCreateDefaultsFlags, parent) { ui.commentGroupBox->setEnabled(false); // These are here because comments_allowed are actually not used yet, so the group will not be changed by the setting and when ui.comments_allowed->setChecked(true); // the group info is displayed it will therefore be set to "disabled" in all cases although it is enabled. } -GxsChannelGroupDialog::GxsChannelGroupDialog(TokenQueue *tokenExternalQueue, RsTokenService *tokenService, Mode mode, RsGxsGroupId groupId, QWidget *parent) - : GxsGroupDialog(tokenExternalQueue, tokenService, mode, groupId, ChannelEditEnabledFlags, ChannelEditDefaultsFlags, parent) +GxsChannelGroupDialog::GxsChannelGroupDialog(Mode mode, RsGxsGroupId groupId, QWidget *parent) + : GxsGroupDialog(mode, groupId, ChannelEditEnabledFlags, ChannelEditDefaultsFlags, parent) { ui.commentGroupBox->setEnabled(false); // These are here because comments_allowed are actually not used yet, so the group will not be changed by the setting and when ui.comments_allowed->setChecked(true); // the group info is displayed it will therefore be set to "disabled" in all cases although it is enabled. @@ -99,11 +100,9 @@ QPixmap GxsChannelGroupDialog::serviceImage() switch (mode()) { case MODE_CREATE: - return QPixmap(":/icons/png/channel.png"); case MODE_SHOW: - return QPixmap(":/icons/png/channel.png"); case MODE_EDIT: - return QPixmap(":/icons/png/channel.png"); + return FilesDefs::getPixmapFromQtResourcePath(":/icons/png/channel.png"); } return QPixmap(); @@ -129,17 +128,19 @@ void GxsChannelGroupDialog::prepareChannelGroup(RsGxsChannelGroup &group, const } } -bool GxsChannelGroupDialog::service_CreateGroup(uint32_t &token, const RsGroupMetaData &meta) +bool GxsChannelGroupDialog::service_createGroup(RsGroupMetaData& meta) { // Specific Function. RsGxsChannelGroup grp; prepareChannelGroup(grp, meta); - rsGxsChannels->createGroup(token, grp); + rsGxsChannels->createChannel(grp); + + meta = grp.mMeta; return true; } -bool GxsChannelGroupDialog::service_EditGroup(uint32_t &token, RsGroupMetaData &editedMeta) +bool GxsChannelGroupDialog::service_updateGroup(const RsGroupMetaData& editedMeta) { RsGxsChannelGroup grp; prepareChannelGroup(grp, editedMeta); @@ -147,44 +148,42 @@ bool GxsChannelGroupDialog::service_EditGroup(uint32_t &token, RsGroupMetaData & std::cerr << "GxsChannelGroupDialog::service_EditGroup() submitting changes"; std::cerr << std::endl; - rsGxsChannels->updateGroup(token, grp); - return true; + return rsGxsChannels->editChannel(grp); } -bool GxsChannelGroupDialog::service_loadGroup(uint32_t token, Mode /*mode*/, RsGroupMetaData& groupMetaData, QString &description) +bool GxsChannelGroupDialog::service_loadGroup(const RsGxsGenericGroupData *data, Mode /*mode*/, QString& description) { - std::cerr << "GxsChannelGroupDialog::service_loadGroup(" << token << ")"; - std::cerr << std::endl; + const RsGxsChannelGroup *pgroup = dynamic_cast(data); - std::vector groups; - if (!rsGxsChannels->getGroupData(token, groups)) + if (!pgroup) { - std::cerr << "GxsChannelGroupDialog::service_loadGroup() Error getting GroupData"; - std::cerr << std::endl; + std::cerr << "GxsChannelGroupDialog::service_loadGroup() Error supplied generic group data is not a RsGxsChannelGroup" << std::endl; return false; } - if (groups.size() != 1) - { - std::cerr << "GxsChannelGroupDialog::service_loadGroup() Error Group.size() != 1"; - std::cerr << std::endl; - return false; - } - - std::cerr << "GxsChannelsGroupDialog::service_loadGroup() Unfinished Loading"; - std::cerr << std::endl; - - const RsGxsChannelGroup &group = groups[0]; - groupMetaData = group.mMeta; + const RsGxsChannelGroup& group = *pgroup; description = QString::fromUtf8(group.mDescription.c_str()); if (group.mImage.mData) { QPixmap pixmap; - if (GxsIdDetails::loadPixmapFromData(group.mImage.mData, group.mImage.mSize,pixmap,GxsIdDetails::ORIGINAL)) { + if (GxsIdDetails::loadPixmapFromData(group.mImage.mData, group.mImage.mSize,pixmap,GxsIdDetails::ORIGINAL)) setLogo(pixmap); - } } return true; } + +bool GxsChannelGroupDialog::service_getGroupData(const RsGxsGroupId& grpId,RsGxsGenericGroupData *& data) +{ + std::vector forumsInfo ; + + if( rsGxsChannels->getChannelsInfo(std::list({grpId}),forumsInfo) && forumsInfo.size() == 1) + { + data = new RsGxsChannelGroup(forumsInfo[0]); + return true; + } + else + return false; + +} diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelGroupDialog.h b/retroshare-gui/src/gui/gxschannels/GxsChannelGroupDialog.h index c500912df..025d6a47d 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelGroupDialog.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelGroupDialog.h @@ -29,15 +29,16 @@ class GxsChannelGroupDialog : public GxsGroupDialog Q_OBJECT public: - GxsChannelGroupDialog(TokenQueue *tokenQueue, QWidget *parent); - GxsChannelGroupDialog(TokenQueue *tokenExternalQueue, RsTokenService *tokenService, Mode mode, RsGxsGroupId groupId, QWidget *parent = NULL); + GxsChannelGroupDialog(QWidget *parent); + GxsChannelGroupDialog(Mode mode, RsGxsGroupId groupId, QWidget *parent = NULL); protected: - virtual void initUi(); - virtual QPixmap serviceImage(); - virtual bool service_CreateGroup(uint32_t &token, const RsGroupMetaData &meta); - virtual bool service_loadGroup(uint32_t token, Mode mode, RsGroupMetaData& groupMetaData, QString &description); - virtual bool service_EditGroup(uint32_t &token, RsGroupMetaData &editedMeta); + void initUi() override; + QPixmap serviceImage() override; + bool service_createGroup(RsGroupMetaData &meta) override; + bool service_loadGroup(const RsGxsGenericGroupData *data,Mode mode, QString& description) override; + bool service_updateGroup(const RsGroupMetaData& editedMeta) override; + bool service_getGroupData(const RsGxsGroupId& grpId,RsGxsGenericGroupData *& data) override; private: void prepareChannelGroup(RsGxsChannelGroup &group, const RsGroupMetaData &meta); diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp new file mode 100644 index 000000000..dd19f600d --- /dev/null +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp @@ -0,0 +1,476 @@ +/******************************************************************************* + * retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp * + * * + * Copyright 2020 by Cyril Soler * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include +#include +#include +#include + +#include "gui/common/FilesDefs.h" +#include "util/qtthreadsutils.h" +#include "util/HandleRichText.h" +#include "util/DateTime.h" +#include "retroshare/rsgxsflags.h" +#include "retroshare/rsgxschannels.h" +#include "retroshare/rsexpr.h" + +#include "GxsChannelPostFilesModel.h" + +//#define DEBUG_CHANNEL_MODEL + +Q_DECLARE_METATYPE(ChannelPostFileInfo) + +static std::ostream& operator<<(std::ostream& o, const QModelIndex& i);// defined elsewhere + +RsGxsChannelPostFilesModel::RsGxsChannelPostFilesModel(QObject *parent) + : QAbstractItemModel(parent) +{ + initEmptyHierarchy(); + + mTimer = new QTimer; + connect(mTimer,SIGNAL(timeout()),this,SLOT(update())); +} + +void RsGxsChannelPostFilesModel::initEmptyHierarchy() +{ + preMods(); + + mFiles.clear(); + mFilteredFiles.clear(); + + postMods(); +} + +void RsGxsChannelPostFilesModel::preMods() +{ + //emit layoutAboutToBeChanged(); //Generate SIGSEGV when click on button move next/prev. + + beginResetModel(); +} +void RsGxsChannelPostFilesModel::postMods() +{ + endResetModel(); + + emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFilteredFiles.size(),COLUMN_FILES_NB_COLUMNS-1,(void*)NULL)); +} + +void RsGxsChannelPostFilesModel::update() +{ + emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFilteredFiles.size(),COLUMN_FILES_NB_COLUMNS-1,(void*)NULL)); +} + +int RsGxsChannelPostFilesModel::rowCount(const QModelIndex& parent) const +{ + if(parent.column() > 0) + return 0; + + if(mFilteredFiles.empty()) // security. Should never happen. + return 0; + + if(!parent.isValid()) + return mFilteredFiles.size(); // mFilteredPosts always has an item at 0, so size()>=1, and mColumn>=1 + + RsErr() << __PRETTY_FUNCTION__ << " rowCount cannot figure out the porper number of rows." << std::endl; + return 0; +} + +int RsGxsChannelPostFilesModel::columnCount(const QModelIndex &/*parent*/) const +{ + return COLUMN_FILES_NB_COLUMNS ; +} + +bool RsGxsChannelPostFilesModel::getFileData(const QModelIndex& i,ChannelPostFileInfo& fmpe) const +{ + if(!i.isValid()) + return true; + + quintptr ref = i.internalId(); + uint32_t entry = 0; + + if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFiles.size()) + return false ; + + fmpe = mFiles[mFilteredFiles[entry]]; + + return true; + +} + +bool RsGxsChannelPostFilesModel::hasChildren(const QModelIndex &parent) const +{ + if(!parent.isValid()) + return true; + + return false; // by default, no channel post has children +} + +bool RsGxsChannelPostFilesModel::convertTabEntryToRefPointer(uint32_t entry,quintptr& ref) +{ + // the pointer is formed the following way: + // + // [ 32 bits ] + // + // This means that the whole software has the following build-in limitation: + // * 4 B simultaenous posts. Should be enough ! + + ref = (intptr_t)(entry+1); + + return true; +} + +bool RsGxsChannelPostFilesModel::convertRefPointerToTabEntry(quintptr ref, uint32_t& entry) +{ + intptr_t val = (intptr_t)ref; + + if(val > (1<<30)) // make sure the pointer is an int that fits in 32bits and not too big which would look suspicious + { + RsErr() << "(EE) trying to make a ChannelPostsModelIndex out of a number that is larger than 2^32-1 !" << std::endl; + return false ; + } + if(val==0) + { + RsErr() << "(EE) trying to make a ChannelPostsFileModelIndex out of index 0." << std::endl; + return false; + } + entry = val-1; + + return true; +} + +QModelIndex RsGxsChannelPostFilesModel::index(int row, int column, const QModelIndex & parent) const +{ + if(row < 0 || column < 0 || column >= COLUMN_FILES_NB_COLUMNS) + return QModelIndex(); + + quintptr ref = getChildRef(parent.internalId(),row); + +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << "index-3(" << row << "," << column << " parent=" << parent << ") : " << createIndex(row,column,ref) << std::endl; +#endif + return createIndex(row,column,ref) ; +} + +QModelIndex RsGxsChannelPostFilesModel::parent(const QModelIndex& index) const +{ + if(!index.isValid()) + return QModelIndex(); + + return QModelIndex(); // there's no hierarchy here. So nothing to do! +} + +Qt::ItemFlags RsGxsChannelPostFilesModel::flags(const QModelIndex& index) const +{ + if (!index.isValid()) + return 0; + + if(index.column() == COLUMN_FILES_FILE) + return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; + else + return QAbstractItemModel::flags(index); +} + +quintptr RsGxsChannelPostFilesModel::getChildRef(quintptr ref,int index) const +{ + if (index < 0) + return 0; + + if(ref == quintptr(0)) + { + quintptr new_ref; + convertTabEntryToRefPointer(index,new_ref); + return new_ref; + } + else + return 0 ; +} + +quintptr RsGxsChannelPostFilesModel::getParentRow(quintptr ref,int& row) const +{ + ChannelPostFilesModelIndex ref_entry; + + if(!convertRefPointerToTabEntry(ref,ref_entry) || ref_entry >= mFilteredFiles.size()) + return 0 ; + + if(ref_entry == 0) + { + RsErr() << "getParentRow() shouldn't be asked for the parent of NULL" << std::endl; + row = 0; + } + else + row = ref_entry-1; + + return 0; +} + +int RsGxsChannelPostFilesModel::getChildrenCount(quintptr ref) const +{ + uint32_t entry = 0 ; + + if(ref == quintptr(0)) + return rowCount()-1; + + return 0; +} + +QVariant RsGxsChannelPostFilesModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + switch(section) + { + case COLUMN_FILES_FILE: return QString("Status"); + case COLUMN_FILES_SIZE: return QString("Size"); + case COLUMN_FILES_NAME: return QString("File"); + case COLUMN_FILES_DATE: return QString("Published"); + default: + return QString("[No data]"); + } +} + +QVariant RsGxsChannelPostFilesModel::data(const QModelIndex &index, int role) const +{ +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << "calling data(" << index << ") role=" << role << std::endl; +#endif + + if(!index.isValid()) + return QVariant(); + + switch(role) + { + case Qt::SizeHintRole: return sizeHintRole(index.column()) ; + case Qt::StatusTipRole:return QVariant(); + default: break; + } + + quintptr ref = (index.isValid())?index.internalId():0 ; + uint32_t entry = 0; + +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << "data(" << index << ")" ; +#endif + + if(!ref) + { +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << " [empty]" << std::endl; +#endif + return QVariant() ; + } + + if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFilteredFiles.size()) + { +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << "Bad pointer: " << (void*)ref << std::endl; +#endif + return QVariant() ; + } + + const ChannelPostFileInfo& fmpe(mFiles[mFilteredFiles[entry]]); + + switch(role) + { + case Qt::DisplayRole: return displayRole (fmpe,index.column()) ; + case Qt::UserRole: return userRole (fmpe,index.column()) ; + case SortRole: return sortRole (fmpe,index.column()) ; + default: + return QVariant(); + } +} + +void RsGxsChannelPostFilesModel::setFilter(const QStringList& strings, uint32_t& count) +{ + preMods(); + + beginRemoveRows(QModelIndex(),0,rowCount()-1); + endRemoveRows(); + + if(strings.empty()) + { + mFilteredFiles.clear(); + for(uint32_t i=0;if2.mName); + case RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE: return (ord==Qt::AscendingOrder)?(f1.mSizef2.mSize); + case RsGxsChannelPostFilesModel::COLUMN_FILES_DATE: return (ord==Qt::AscendingOrder)?(f1.mPublishTimef2.mPublishTime); + case RsGxsChannelPostFilesModel::COLUMN_FILES_FILE: + { + FileInfo fi1,fi2; + rsFiles->FileDetails(f1.mHash,RS_FILE_HINTS_DOWNLOAD,fi1); + rsFiles->FileDetails(f2.mHash,RS_FILE_HINTS_DOWNLOAD,fi2); + + return (ord==Qt::AscendingOrder)?(fi1.transferedfi2.transfered); + } + } + + } + +private: + int col; + Qt::SortOrder ord; +}; + +void RsGxsChannelPostFilesModel::sort(int column, Qt::SortOrder order) +{ + std::sort(mFiles.begin(),mFiles.end(),compareOperator(column,order)); + + update(); +} + +QVariant RsGxsChannelPostFilesModel::sizeHintRole(int col) const +{ + float factor = QFontMetricsF(QApplication::font()).height()/14.0f ; + + return QVariant( QSize(factor * 170, factor*14 )); +} + +QVariant RsGxsChannelPostFilesModel::sortRole(const ChannelPostFileInfo& fmpe,int column) const +{ + switch(column) + { + case COLUMN_FILES_NAME: return QVariant(QString::fromUtf8(fmpe.mName.c_str())); + case COLUMN_FILES_SIZE: return QVariant(qulonglong(fmpe.mSize)); + case COLUMN_FILES_DATE: return QVariant(qulonglong(fmpe.mPublishTime)); + case COLUMN_FILES_FILE: + { + FileInfo finfo; + if(rsFiles->FileDetails(fmpe.mHash,RS_FILE_HINTS_DOWNLOAD,finfo)) + return qulonglong(finfo.transfered); + + return QVariant(qulonglong(fmpe.mSize)); + } + break; + + default: + return displayRole(fmpe,column); + } +} + +QVariant RsGxsChannelPostFilesModel::displayRole(const ChannelPostFileInfo& fmpe,int col) const +{ + switch(col) + { + case COLUMN_FILES_NAME: return QString::fromUtf8(fmpe.mName.c_str()); + case COLUMN_FILES_SIZE: return QString::number(fmpe.mSize); + case COLUMN_FILES_DATE: return QString::number(fmpe.mPublishTime); + case COLUMN_FILES_FILE: { + FileInfo finfo; + if(rsFiles->FileDetails(fmpe.mHash,RS_FILE_HINTS_DOWNLOAD,finfo)) + return qulonglong(finfo.transfered); + else + return 0; + } + default: + return QString(); + + } + + + return QVariant("[ERROR]"); +} + +QVariant RsGxsChannelPostFilesModel::userRole(const ChannelPostFileInfo& fmpe,int col) const +{ + switch(col) + { + default: + return QVariant::fromValue(fmpe); + } +} + +void RsGxsChannelPostFilesModel::clear() +{ + preMods(); + + initEmptyHierarchy(); + + postMods(); + emit channelLoaded(); +} + +void RsGxsChannelPostFilesModel::setFiles(const std::list &files) +{ + preMods(); + + beginRemoveRows(QModelIndex(),0,mFilteredFiles.size()-1); + endRemoveRows(); + + initEmptyHierarchy(); + + for(auto& file:files) + mFiles.push_back(file); + + for(uint32_t i=0;istart(5000); + else + mTimer->stop(); +} diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h new file mode 100644 index 000000000..d2d4fabf8 --- /dev/null +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h @@ -0,0 +1,173 @@ +/******************************************************************************* + * retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h * + * * + * Copyright 2020 by Cyril Soler * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#pragma once + +#include "retroshare/rsfiles.h" +#include "retroshare/rsgxscommon.h" + +#include +#include + +// This class holds the actual hierarchy of posts, represented by identifiers +// It is responsible for auto-updating when necessary and holds a mutex to allow the Model to +// safely access the data. + +// The model contains a post in place 0 that is the parent of all posts. + +typedef uint32_t ChannelPostFilesModelIndex; + +class QTimer; + +// This class contains the info for a file as well as additional info such as publication date + +struct ChannelPostFileInfo: public RsGxsFile +{ + ChannelPostFileInfo(const RsGxsFile& gxs_file,rstime_t t) + : RsGxsFile(gxs_file),mPublishTime(t) + {} + + ChannelPostFileInfo() : mPublishTime(0) {} + + rstime_t mPublishTime; +}; + +// This class is the item model used by Qt to display the information + +class RsGxsChannelPostFilesModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit RsGxsChannelPostFilesModel(QObject *parent = NULL); + ~RsGxsChannelPostFilesModel(){} + + enum Columns { + COLUMN_FILES_NAME = 0x00, + COLUMN_FILES_SIZE = 0x01, + COLUMN_FILES_FILE = 0x02, + COLUMN_FILES_DATE = 0x03, + COLUMN_FILES_NB_COLUMNS = 0x04 + }; + + enum Roles{ SortRole = Qt::UserRole+1, + FilterRole = Qt::UserRole+2, + }; + +#ifdef TODO + enum SortMode{ SORT_MODE_PUBLISH_TS = 0x00, + SORT_MODE_CHILDREN_PUBLISH_TS = 0x01, + }; +#endif + + QModelIndex root() const{ return createIndex(0,0,(void*)NULL) ;} + + // This method will asynchroneously update the data + void setFiles(const std::list& files); + void setFilter(const QStringList &strings, uint32_t &count) ; + +#ifdef TODO + QModelIndex getIndexOfFile(const RsFileHash& hash) const; + void setSortMode(SortMode mode) ; + + void setTextColorRead (QColor color) { mTextColorRead = color;} + void setTextColorUnread (QColor color) { mTextColorUnread = color;} + void setTextColorUnreadChildren(QColor color) { mTextColorUnreadChildren = color;} + void setTextColorNotSubscribed (QColor color) { mTextColorNotSubscribed = color;} + void setTextColorMissing (QColor color) { mTextColorMissing = color;} + void setAuthorOpinion(const QModelIndex& indx,RsOpinion op); +#endif + + // Helper functions + + void clear() ; + + // AbstractItemModel functions. + + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + bool hasChildren(const QModelIndex &parent = QModelIndex()) const override; + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; + + QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex& child) const override; + Qt::ItemFlags flags(const QModelIndex& index) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + + // Custom item roles + + QVariant sizeHintRole (int col) const; + QVariant displayRole (const ChannelPostFileInfo& fmpe, int col) const; + QVariant toolTipRole (const ChannelPostFileInfo& fmpe, int col) const; + QVariant userRole (const ChannelPostFileInfo& fmpe, int col) const; + QVariant sortRole (const ChannelPostFileInfo& fmpe, int col) const; + QVariant filterRole (const ChannelPostFileInfo& fmpe, int col) const; +#ifdef TODO + QVariant decorationRole(const ForumModelPostEntry& fmpe, int col) const; + QVariant pinnedRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant missingRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant statusRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant authorRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant fontRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant textColorRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant backgroundRole(const ForumModelPostEntry& fmpe, int col) const; +#endif + + /*! + * \brief debug_dump + * Dumps the hierarchy of posts in the terminal, to allow checking whether the internal representation is correct. + */ + void debug_dump(); + +signals: + void channelLoaded(); // emitted after the posts have been set. Can be used to updated the UI. + +private slots: + void update(); + +private: +#ifdef TODO + bool mUseChildTS; + bool mFilteringEnabled; + SortMode mSortMode; +#endif + void preMods() ; + void postMods() ; + + quintptr getParentRow(quintptr ref,int& row) const; + quintptr getChildRef(quintptr ref, int index) const; + int getChildrenCount(quintptr ref) const; + bool getFileData(const QModelIndex& i, ChannelPostFileInfo &fmpe) const; + + static bool convertTabEntryToRefPointer(uint32_t entry, quintptr& ref); + static bool convertRefPointerToTabEntry(quintptr ref,uint32_t& entry); + +#ifdef TODO + static void generateMissingItem(const RsGxsMessageId &msgId,ChannelPostsModelPostEntry& entry); +#endif + void initEmptyHierarchy(); + + std::vector mFilteredFiles ; // store the list of files for the post + std::vector mFiles ; // store the list of files for the post + + QTimer *mTimer; +}; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.cpp new file mode 100644 index 000000000..7c4bc62da --- /dev/null +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.cpp @@ -0,0 +1,324 @@ +/******************************************************************************* + * retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.cpp * + * * + * Copyright 2020 by Retroshare Team * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include +#include + +#include "gui/common/FilesDefs.h" +#include "gui/gxschannels/GxsChannelPostThumbnail.h" + +const float ChannelPostThumbnailView::DEFAULT_SIZE_IN_FONT_HEIGHT = 5.0; +const float ChannelPostThumbnailView::FONT_SCALE_FACTOR = 1.5; + +ChannelPostThumbnailView::ChannelPostThumbnailView(const RsGxsChannelPost& post,uint32_t flags,QWidget *parent) + : QWidget(parent),mPostTitle(nullptr),mFlags(flags), mAspectRatio(ASPECT_RATIO_2_3) +{ + // now fill the data + + init(post); +} + +ChannelPostThumbnailView::ChannelPostThumbnailView(QWidget *parent,uint32_t flags) + : QWidget(parent),mFlags(flags), mAspectRatio(ASPECT_RATIO_2_3) +{ + init(RsGxsChannelPost()); +} + +ChannelPostThumbnailView::~ChannelPostThumbnailView() +{ + delete mPostImage; +} + +void ChannelPostThumbnailView::setText(const QString& s) +{ + if(mPostTitle == NULL) + { + std::cerr << "(EE) calling setText on a ChannelPostThumbnailView without SHOW_TEXT flag!"<< std::endl; + return; + } + + QString ss; + if(s.length() > 30) + ss = s.left(30)+"..."; + else + ss =s; + + mPostTitle->setText(ss); +} + +void ChannelPostThumbnailView::setPixmap(const QPixmap& p, bool guess_aspect_ratio) +{ + mPostImage->setPicture(p); + + if(guess_aspect_ratio)// aspect ratio is automatically guessed. + { + // compute closest aspect ratio + float r = p.width()/(float)p.height(); + + if(r < 0.8) + setAspectRatio(ChannelPostThumbnailView::ASPECT_RATIO_2_3); + else if(r < 1.15) + setAspectRatio(ChannelPostThumbnailView::ASPECT_RATIO_1_1); + else + setAspectRatio(ChannelPostThumbnailView::ASPECT_RATIO_16_9); + } +} + +void ChannelPostThumbnailView::setAspectRatio(AspectRatio r) +{ + mAspectRatio = r; + + QFontMetricsF fm(font()); + int W = THUMBNAIL_OVERSAMPLE_FACTOR * thumbnail_w() * fm.height() ; + int H = THUMBNAIL_OVERSAMPLE_FACTOR * thumbnail_h() * fm.height() ; + + mPostImage->setFixedSize(W,H); + mPostImage->reset(); + mPostImage->updateView(); +} + +void ChannelPostThumbnailView::init(const RsGxsChannelPost& post) +{ + QString msg = QString::fromUtf8(post.mMeta.mMsgName.c_str()); + bool is_msg_new = IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus); + + QPixmap thumbnail; + + if(post.mThumbnail.mSize > 0) + GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData, post.mThumbnail.mSize, thumbnail,GxsIdDetails::ORIGINAL); + else if(post.mMeta.mPublishTs > 0) // this is for testing that the post is not an empty post (happens at the end of the last row) + thumbnail = FilesDefs::getPixmapFromQtResourcePath(CHAN_DEFAULT_IMAGE); + + mPostImage = new ZoomableLabel(this); + mPostImage->setEnableZoom(mFlags & FLAG_ALLOW_PAN); + mPostImage->setScaledContents(true); + mPostImage->setPicture(thumbnail); + + if(mFlags & FLAG_ALLOW_PAN) + mPostImage->setToolTip(tr("Use mouse to center and zoom into the image")); + + QVBoxLayout *layout = new QVBoxLayout(this); + + layout->addWidget(mPostImage); + + QFontMetricsF fm(font()); + int W = THUMBNAIL_OVERSAMPLE_FACTOR * thumbnail_w() * fm.height() ; + int H = THUMBNAIL_OVERSAMPLE_FACTOR * thumbnail_h() * fm.height() ; + + mPostImage->setFixedSize(W,H); + + if(mFlags & FLAG_SHOW_TEXT) + { + mPostTitle = new QLabel(this); + layout->addWidget(mPostTitle); + + QString ss = (msg.length() > 30)? (msg.left(30)+"..."):msg; + + mPostTitle->setText(ss); + + QFont font = mPostTitle->font(); + + if(mFlags & ChannelPostThumbnailView::FLAG_SCALE_FONT) + font.setPointSizeF(FONT_SCALE_FACTOR * DEFAULT_SIZE_IN_FONT_HEIGHT / 5.0 * font.pointSizeF()); + else + font.setPointSizeF(DEFAULT_SIZE_IN_FONT_HEIGHT / 5.0 * font.pointSizeF()); + + if(is_msg_new) + font.setBold(true); + + mPostTitle->setFont(font); + mPostTitle->setMaximumWidth(W); + mPostTitle->setWordWrap(true); + } + setSizePolicy(QSizePolicy::MinimumExpanding,QSizePolicy::MinimumExpanding); + + layout->addStretch(); + + setLayout(layout); + adjustSize(); + update(); +} + +ChannelPostThumbnailView::AspectRatio ChannelPostThumbnailView::bestAspectRatio() +{ + if(mPostImage->originalImage().isNull()) + return ASPECT_RATIO_1_1; + + float as = mPostImage->originalImage().height() / (float)mPostImage->originalImage().width() ; + + if(as <= 0.8) + return ASPECT_RATIO_16_9; + else if(as < 1.15) + return ASPECT_RATIO_1_1; + else + return ASPECT_RATIO_2_3; +} +QSize ChannelPostThumbnailView::actualSize() const +{ + QFontMetricsF fm(font()); + + if(mPostTitle != nullptr) + { + QMargins cm = layout()->contentsMargins(); + + return QSize(width(), + mPostTitle->height() + mPostImage->height() + 0.5*fm.height() + + cm.top() + cm.bottom() + layout()->spacing()); + } + else + return size(); +} + +float ChannelPostThumbnailView::thumbnail_w() const +{ + switch(mAspectRatio) + { + default: + case ASPECT_RATIO_1_1: + case ASPECT_RATIO_UNKNOWN: return DEFAULT_SIZE_IN_FONT_HEIGHT; + + case ASPECT_RATIO_2_3: return DEFAULT_SIZE_IN_FONT_HEIGHT; + case ASPECT_RATIO_16_9: return DEFAULT_SIZE_IN_FONT_HEIGHT; + } +} +float ChannelPostThumbnailView::thumbnail_h() const +{ + switch(mAspectRatio) + { + default: + case ASPECT_RATIO_1_1: + case ASPECT_RATIO_UNKNOWN: return DEFAULT_SIZE_IN_FONT_HEIGHT; + + case ASPECT_RATIO_2_3: return DEFAULT_SIZE_IN_FONT_HEIGHT * 3.0/2.0; + case ASPECT_RATIO_16_9: return DEFAULT_SIZE_IN_FONT_HEIGHT * 9.0/16.0; + } +} + +void ZoomableLabel::reset() +{ + mCenterX = mFullImage.width()/2.0; + mCenterY = mFullImage.height()/2.0; + mZoomFactor = 1.0/std::max(width() / (float)mFullImage.width(), height()/(float)mFullImage.height()); + + updateView(); +} +void ZoomableLabel::mouseMoveEvent(QMouseEvent *me) +{ + if(!mZoomEnabled) + return; + + float new_center_x = mCenterX - (me->x() - mLastX); + float new_center_y = mCenterY - (me->y() - mLastY); + + mLastX = me->x(); + mLastY = me->y(); + + if(new_center_x - 0.5 * width()*mZoomFactor < 0) return; + if(new_center_y - 0.5 *height()*mZoomFactor < 0) return; + + if(new_center_x + 0.5 * width()*mZoomFactor >= mFullImage.width()) return; + if(new_center_y + 0.5 *height()*mZoomFactor >=mFullImage.height()) return; + + mCenterX = new_center_x; + mCenterY = new_center_y; + + updateView(); +} +void ZoomableLabel::mousePressEvent(QMouseEvent *me) +{ + mMoving = true; + mLastX = me->x(); + mLastY = me->y(); +} +void ZoomableLabel::mouseReleaseEvent(QMouseEvent *) +{ + mMoving = false; +} +void ZoomableLabel::wheelEvent(QWheelEvent *me) +{ + if(!mZoomEnabled) + return; + + float new_zoom_factor = (me->delta() > 0)?(mZoomFactor*1.05):(mZoomFactor/1.05); + float new_center_x = mCenterX; + float new_center_y = mCenterY; + + // Try to find centerX and centerY so that the crop does not overlap the original image + + float min_x = 0.5 * width()*new_zoom_factor; + float max_x = mFullImage.width() - 0.5 * width()*new_zoom_factor; + float min_y = 0.5 * height()*new_zoom_factor; + float max_y = mFullImage.height() - 0.5 * height()*new_zoom_factor; + + if(min_x >= max_x) return; + if(min_y >= max_y) return; + + if(new_center_x < min_x) new_center_x = min_x; + if(new_center_y < min_y) new_center_y = min_y; + if(new_center_x > max_x) new_center_x = max_x; + if(new_center_y > max_y) new_center_y = max_y; + + mZoomFactor = new_zoom_factor; + mCenterX = new_center_x; + mCenterY = new_center_y; + + updateView(); +} + +QPixmap ZoomableLabel::extractCroppedScaledPicture() const +{ + QRect rect(mCenterX - 0.5 * width()*mZoomFactor, mCenterY - 0.5 * height()*mZoomFactor, width()*mZoomFactor, height()*mZoomFactor); + QPixmap pix = mFullImage.copy(rect).scaledToHeight(height(),Qt::SmoothTransformation); + + return pix; +} + +void ZoomableLabel::setPicture(const QPixmap& pix) +{ + mFullImage = pix; + + reset(); + updateView(); +} +void ZoomableLabel::resizeEvent(QResizeEvent *e) +{ + QLabel::resizeEvent(e); + + updateView(); +} + +void ZoomableLabel::updateView() +{ + // The new image will be cropped from the original image, using the following rules: + // - first the cropped image size is computed + // - then center is calculated so that + // - the original center is preferred + // - if the crop overlaps the image border, the center is moved. + + QRect rect(mCenterX - 0.5 * width()*mZoomFactor, mCenterY - 0.5 * height()*mZoomFactor, width()*mZoomFactor, height()*mZoomFactor); + QLabel::setPixmap(mFullImage.copy(rect)); +} + + + + + + + diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.h new file mode 100644 index 000000000..b231fbe0a --- /dev/null +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.h @@ -0,0 +1,133 @@ +/******************************************************************************* + * retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.h * + * * + * Copyright 2020 by Retroshare Team * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#pragma once + +#include + +#include +#include +#include + +#include "retroshare/rsgxschannels.h" +#include "retroshare/rsidentity.h" + +#include "gui/gxs/GxsIdDetails.h" +#include "gui/common/FilesDefs.h" + +// Class to provide a label in which the image can be zoomed/moved. The widget size is fixed by the GUI and the user can move/zoom the image +// inside the window formed by the widget. When happy, the view-able part of the image can be extracted. + +class ZoomableLabel: public QLabel +{ +public: + ZoomableLabel(QWidget *parent): QLabel(parent),mZoomFactor(1.0),mCenterX(0.0),mCenterY(0.0),mZoomEnabled(true) {} + + void setPicture(const QPixmap& pix); + void setEnableZoom(bool b) { mZoomEnabled = b; } + void reset(); + QPixmap extractCroppedScaledPicture() const; + void updateView(); + + const QPixmap& originalImage() const { return mFullImage ; } + +protected: + void mousePressEvent(QMouseEvent *ev) override; + void mouseReleaseEvent(QMouseEvent *ev) override; + void mouseMoveEvent(QMouseEvent *ev) override; + void resizeEvent(QResizeEvent *ev) override; + void wheelEvent(QWheelEvent *me) override; + + QPixmap mFullImage; + + float mCenterX; + float mCenterY; + float mZoomFactor; + int mLastX,mLastY; + bool mMoving; + bool mZoomEnabled; +}; + +// Class to paint the thumbnails with title + +class ChannelPostThumbnailView: public QWidget +{ + Q_OBJECT + +public: + typedef enum { + ASPECT_RATIO_UNKNOWN = 0x00, + ASPECT_RATIO_2_3 = 0x01, + ASPECT_RATIO_1_1 = 0x02, + ASPECT_RATIO_16_9 = 0x03, + } AspectRatio; + + // This variable determines the zoom factor on the text below thumbnails. 2.0 is mostly correct for all screen. + static constexpr float THUMBNAIL_OVERSAMPLE_FACTOR = 2.0; + + static constexpr uint32_t FLAG_NONE = 0x00; + static constexpr uint32_t FLAG_SHOW_TEXT = 0x01; + static constexpr uint32_t FLAG_ALLOW_PAN = 0x02; + static constexpr uint32_t FLAG_SCALE_FONT= 0x04; + + // Size of thumbnails as a function of the height of the font. An aspect ratio of 3/4 is good. + + static const int THUMBNAIL_W = 4; + static const int THUMBNAIL_H = 6; + + static constexpr char *CHAN_DEFAULT_IMAGE = ":images/thumb-default-video.png"; + + virtual ~ChannelPostThumbnailView(); + ChannelPostThumbnailView(QWidget *parent=NULL,uint32_t flags=FLAG_ALLOW_PAN | FLAG_SHOW_TEXT | FLAG_SCALE_FONT); + ChannelPostThumbnailView(const RsGxsChannelPost& post,uint32_t flags,QWidget *parent=NULL); + + void init(const RsGxsChannelPost& post); + + void setAspectRatio(AspectRatio r); + void setPixmap(const QPixmap& p,bool guess_aspect_ratio) ; + QPixmap getCroppedScaledPicture() const { return mPostImage->extractCroppedScaledPicture() ; } + + void setText(const QString& s); + + // This is used to allow to render the widget into a pixmap without the white space that Qt adds vertically. There is *no way* apparently + // to get rid of that bloody space. It depends on the aspect ratio of the image and it only shows up when the text label is shown. + // The label however has a correct size. It seems that Qt doesn't like widgets with horizontal aspect ratio and forces the size accordingly. + + QSize actualSize() const ; + + /*! + * \brief bestAspectRatio + * Computes the preferred aspect ratio for the image in the post. The default is 1:1. + * \return the prefered aspect ratio + */ + AspectRatio bestAspectRatio() ; +private: + static const float DEFAULT_SIZE_IN_FONT_HEIGHT ; + static const float FONT_SCALE_FACTOR ; + + float thumbnail_w() const; + float thumbnail_h() const; + + ZoomableLabel *mPostImage; + QLabel *mPostTitle; + uint32_t mFlags; + AspectRatio mAspectRatio; +}; + diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp new file mode 100644 index 000000000..797b2fb6f --- /dev/null +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp @@ -0,0 +1,773 @@ +/******************************************************************************* + * retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp * + * * + * Copyright 2020 by Cyril Soler * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include +#include +#include +#include + +#include "retroshare/rsgxsflags.h" +#include "retroshare/rsgxschannels.h" +#include "retroshare/rsexpr.h" + +#include "gui/common/FilesDefs.h" +#include "util/qtthreadsutils.h" +#include "util/HandleRichText.h" +#include "util/DateTime.h" + +#include "GxsChannelPostsModel.h" +#include "GxsChannelPostFilesModel.h" + +//#define DEBUG_CHANNEL_MODEL + +Q_DECLARE_METATYPE(RsMsgMetaData) + +Q_DECLARE_METATYPE(RsGxsChannelPost) + +std::ostream& operator<<(std::ostream& o, const QModelIndex& i);// defined elsewhere + +RsGxsChannelPostsModel::RsGxsChannelPostsModel(QObject *parent) + : QAbstractItemModel(parent), mTreeMode(RsGxsChannelPostsModel::TREE_MODE_GRID), mColumns(6) +{ + initEmptyHierarchy(); + + mEventHandlerId = 0; + // Needs to be asynced because this function is called by another thread! + + rsEvents->registerEventsHandler( [this](std::shared_ptr event) + { + RsQThreadUtils::postToObject([=](){ handleEvent_main_thread(event); }, this ); + }, mEventHandlerId, RsEventType::GXS_CHANNELS ); +} + +RsGxsChannelPostsModel::~RsGxsChannelPostsModel() +{ + rsEvents->unregisterEventsHandler(mEventHandlerId); +} + +void RsGxsChannelPostsModel::setMode(TreeMode mode) +{ + mTreeMode = mode; + + if(mode == TREE_MODE_LIST) + setNumColumns(2); + + triggerViewUpdate(); +} + +void RsGxsChannelPostsModel::handleEvent_main_thread(std::shared_ptr event) +{ + const RsGxsChannelEvent *e = dynamic_cast(event.get()); + + if(!e) + return; + + switch(e->mChannelEventCode) + { + case RsChannelEventCode::UPDATED_MESSAGE: + case RsChannelEventCode::READ_STATUS_CHANGED: + { + // Normally we should just emit dataChanged() on the index of the data that has changed: + // + // We need to update the data! + + if(e->mChannelGroupId == mChannelGroup.mMeta.mGroupId) + RsThread::async([this, e]() + { + // 1 - get message data from p3GxsChannels + + std::vector posts; + std::vector comments; + std::vector votes; + + if(!rsGxsChannels->getChannelContent(mChannelGroup.mMeta.mGroupId,std::set{ e->mChannelMsgId }, posts,comments,votes)) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve channel message data for channel/msg " << e->mChannelGroupId << "/" << e->mChannelMsgId << std::endl; + return; + } + + // 2 - update the model in the UI thread. + + RsQThreadUtils::postToObject( [posts,comments,votes,this]() + { + for(uint32_t i=0;i& files) +{ + // We use an intermediate map so as to remove duplicates + + std::map files_map; + + for(uint32_t i=0;i 0) + return 0; + + if(mFilteredPosts.empty()) // security. Should never happen. + return 0; + + if(!parent.isValid()) + { + if(mTreeMode == TREE_MODE_GRID) + return (mFilteredPosts.size() + mColumns-1)/mColumns; // mFilteredPosts always has an item at 0, so size()>=1, and mColumn>=1 + else + return mFilteredPosts.size(); + } + + RsErr() << __PRETTY_FUNCTION__ << " rowCount cannot figure out the porper number of rows." << std::endl; + return 0; +} + +int RsGxsChannelPostsModel::columnCount(const QModelIndex &/*parent*/) const +{ + if(mTreeMode == TREE_MODE_GRID) + return std::min((int)mFilteredPosts.size(),(int)mColumns) ; + else + return 2; +} + +bool RsGxsChannelPostsModel::getPostData(const QModelIndex& i,RsGxsChannelPost& fmpe) const +{ + if(!i.isValid()) + return true; + + quintptr ref = i.internalId(); + uint32_t entry = 0; + + if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFilteredPosts.size()) + return false ; + + fmpe = mPosts[mFilteredPosts[entry]]; + + return true; + +} + +bool RsGxsChannelPostsModel::hasChildren(const QModelIndex &parent) const +{ + if(!parent.isValid()) + return true; + + return false; // by default, no channel post has children +} + +bool RsGxsChannelPostsModel::convertTabEntryToRefPointer(uint32_t entry,quintptr& ref) +{ + // the pointer is formed the following way: + // + // [ 32 bits ] + // + // This means that the whole software has the following build-in limitation: + // * 4 B simultaenous posts. Should be enough ! + + ref = (intptr_t)(entry+1); + + return true; +} + +bool RsGxsChannelPostsModel::convertRefPointerToTabEntry(quintptr ref, uint32_t& entry) +{ + intptr_t val = (intptr_t)ref; + + if(val > (1<<30)) // make sure the pointer is an int that fits in 32bits and not too big which would look suspicious + { + RsErr() << "(EE) trying to make a ChannelPostsModelIndex out of a number that is larger than 2^32-1 !" << std::endl; + return false ; + } + if(val==0) + { + RsErr() << "(EE) trying to make a ChannelPostsModelIndex out of index 0." << std::endl; + return false; + } + + entry = val - 1; + + return true; +} + +QModelIndex RsGxsChannelPostsModel::index(int row, int column, const QModelIndex & parent) const +{ + if(row < 0 || column < 0 || column >= (int)mColumns) + return QModelIndex(); + + quintptr ref = getChildRef(parent.internalId(),(mTreeMode == TREE_MODE_GRID)?(column + row*mColumns):row); + +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << "index-3(" << row << "," << column << " parent=" << parent << ") : " << createIndex(row,column,ref) << std::endl; +#endif + return createIndex(row,column,ref) ; +} + +QModelIndex RsGxsChannelPostsModel::parent(const QModelIndex& index) const +{ + if(!index.isValid()) + return QModelIndex(); + + return QModelIndex(); // there's no hierarchy here. So nothing to do! +} + +Qt::ItemFlags RsGxsChannelPostsModel::flags(const QModelIndex& index) const +{ + if (!index.isValid()) + return 0; + + return QAbstractItemModel::flags(index); +} + +void RsGxsChannelPostsModel::setNumColumns(int n) +{ + if(n < 1) + { + RsErr() << __PRETTY_FUNCTION__ << " Attempt to set a number of column of 0. This is wrong." << std::endl; + return; + } + preMods(); + + beginRemoveRows(QModelIndex(),0,rowCount()-1); + endRemoveRows(); + + mColumns = n; + + beginInsertRows(QModelIndex(),0,rowCount()-1); + endInsertRows(); + + postMods(); +} + +quintptr RsGxsChannelPostsModel::getChildRef(quintptr ref,int index) const +{ + if (index < 0) + return 0; + + if(ref == quintptr(0)) + { + quintptr new_ref; + convertTabEntryToRefPointer(index,new_ref); + return new_ref; + } + else + return 0 ; +} + +quintptr RsGxsChannelPostsModel::getParentRow(quintptr ref,int& row) const +{ + ChannelPostsModelIndex ref_entry; + + if(!convertRefPointerToTabEntry(ref,ref_entry) || ref_entry >= mFilteredPosts.size()) + return 0 ; + + if(ref_entry == 0) + { + RsErr() << "getParentRow() shouldn't be asked for the parent of NULL" << std::endl; + row = 0; + } + else + row = ref_entry-1; + + return 0; +} + +int RsGxsChannelPostsModel::getChildrenCount(quintptr ref) const +{ + if(ref == quintptr(0)) + return rowCount()-1; + + return 0; +} + +QVariant RsGxsChannelPostsModel::data(const QModelIndex &index, int role) const +{ +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << "calling data(" << index << ") role=" << role << std::endl; +#endif + + if(!index.isValid()) + return QVariant(); + + switch(role) + { + case Qt::SizeHintRole: return sizeHintRole(index.column()) ; + case Qt::StatusTipRole:return QVariant(); + default: break; + } + + quintptr ref = (index.isValid())?index.internalId():0 ; + uint32_t entry = 0; + +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << "data(" << index << ")" ; +#endif + + if(!ref) + { +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << " [empty]" << std::endl; +#endif + return QVariant() ; + } + + if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFilteredPosts.size()) + { +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << "Bad pointer: " << (void*)ref << std::endl; +#endif + return QVariant() ; + } + + const RsGxsChannelPost& fmpe(mPosts[mFilteredPosts[entry]]); + + switch(role) + { + case Qt::DisplayRole: return displayRole (fmpe,index.column()) ; + case Qt::UserRole: return userRole (fmpe,index.column()) ; + default: + return QVariant(); + } +} + +QVariant RsGxsChannelPostsModel::sizeHintRole(int /* col */) const +{ + float factor = QFontMetricsF(QApplication::font()).height()/14.0f ; + + return QVariant( QSize(factor * 170, factor*14 )); +} + +QVariant RsGxsChannelPostsModel::displayRole(const RsGxsChannelPost& fmpe,int col) const +{ + switch(col) + { + default: + return QString::fromUtf8(fmpe.mMeta.mMsgName.c_str()); + } + + + return QVariant("[ERROR]"); +} + +QVariant RsGxsChannelPostsModel::userRole(const RsGxsChannelPost& fmpe,int col) const +{ + switch(col) + { + default: + return QVariant::fromValue(fmpe); + } +} + +const RsGxsGroupId& RsGxsChannelPostsModel::currentGroupId() const +{ + return mChannelGroup.mMeta.mGroupId; +} + +void RsGxsChannelPostsModel::updateChannel(const RsGxsGroupId& channel_group_id) +{ + if(channel_group_id.isNull()) + return; + + update_posts(channel_group_id); +} + +void RsGxsChannelPostsModel::clear() +{ + preMods(); + + mPosts.clear(); + initEmptyHierarchy(); + + postMods(); + emit channelPostsLoaded(); +} + +bool operator<(const RsGxsChannelPost& p1,const RsGxsChannelPost& p2) +{ + return p1.mMeta.mPublishTs > p2.mMeta.mPublishTs; +} + +void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vector& posts) +{ + preMods(); + + beginRemoveRows(QModelIndex(),0,rowCount()-1); + endRemoveRows(); + + mPosts.clear(); + mChannelGroup = group; + + createPostsArray(posts); + + std::sort(mPosts.begin(),mPosts.end()); + + mFilteredPosts.clear(); + for(uint32_t i=0;i channelIds; + std::vector msg_metas; + std::vector groups; + + channelIds.push_back(group_id); + + if(!rsGxsChannels->getChannelsInfo(channelIds,groups) || groups.size() != 1) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve channel group info for channel " << group_id << std::endl; + return; + } + + RsGxsChannelGroup group = groups[0]; + + // We use the heap because the arrays need to be stored accross async + + std::vector *posts = new std::vector(); + std::vector *comments = new std::vector(); + std::vector *votes = new std::vector(); + + if(!rsGxsChannels->getChannelAllContent(group_id, *posts,*comments,*votes)) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve channel messages for channel " << group_id << std::endl; + return; + } + + // 2 - update the model in the UI thread. + + RsQThreadUtils::postToObject( [group,posts,comments,votes,this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete, note that + * Qt::QueuedConnection is important! + */ + + setPosts(group,*posts) ; + + delete posts; + delete comments; + delete votes; + + }, this ); + + }); +} + +void RsGxsChannelPostsModel::createPostsArray(std::vector& posts) +{ + // collect new versions of posts if any + +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << "Inserting channel posts" << std::endl; +#endif + + std::vector new_versions ; + for (uint32_t i=0;i search_map ; + for (uint32_t i=0;i versions ; + std::map::const_iterator vit ; + + while(search_map.end() != (vit=search_map.find(posts[current_index].mMeta.mOrigMsgId))) + { +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << " post at index " << current_index << " replaces a post at position " << vit->second ; +#endif + + // Now replace the post only if the new versionis more recent. It may happen indeed that the same post has been corrected multiple + // times. In this case, we only need to replace the post with the newest version + + //uint32_t prev_index = current_index ; + current_index = vit->second ; + + if(posts[current_index].mMeta.mMsgId.isNull()) // This handles the branching situation where this post has been already erased. No need to go down further. + { +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << " already erased. Stopping." << std::endl; +#endif + break ; + } + + if(posts[current_index].mMeta.mPublishTs < posts[source_index].mMeta.mPublishTs) + { +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << " and is more recent => following" << std::endl; +#endif + for(std::set::const_iterator itt(posts[current_index].mOlderVersions.begin());itt!=posts[current_index].mOlderVersions.end();++itt) + posts[source_index].mOlderVersions.insert(*itt); + + posts[source_index].mOlderVersions.insert(posts[current_index].mMeta.mMsgId); + posts[current_index].mMeta.mMsgId.clear(); // clear the msg Id so the post will be ignored + } +#ifdef DEBUG_CHANNEL_MODEL + else + std::cerr << " but is older -> Stopping" << std::endl; +#endif + } + } + } + +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << "Now adding " << posts.size() << " posts into array structure..." << std::endl; +#endif + + mPosts.clear(); + + for (std::vector::const_reverse_iterator it = posts.rbegin(); it != posts.rend(); ++it) + { + if(!(*it).mMeta.mMsgId.isNull()) + { +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << " adding post \"" << (*it).mMeta.mMsgName << "\"" << std::endl; +#endif + mPosts.push_back(*it); + } +#ifdef DEBUG_CHANNEL_MODEL + else + std::cerr << " skipped older version post \"" << (*it).mMeta.mMsgName << "\"" << std::endl; +#endif + } +} + +void RsGxsChannelPostsModel::setAllMsgReadStatus(bool read_status) +{ + // No need to call preMods()/postMods() here because we're not changing the model + // All operations below are done async + + // 1 - copy all msg/grp id groups, so that changing the mPosts list while calling the async method will not break the work + + std::vector pairs; + + for(uint32_t i=0;imarkRead(pairs[i],read_status)) + RsErr() << "setAllMsgReadStatus: failed to change status of msg " << pairs[i].first << " in group " << pairs[i].second << " to status " << read_status << std::endl; + }); +} + +void RsGxsChannelPostsModel::setMsgReadStatus(const QModelIndex& i,bool read_status) +{ + if(!i.isValid()) + return ; + + // no need to call preMods()/postMods() here because we'renot changing the model + + quintptr ref = i.internalId(); + uint32_t entry = 0; + + if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFilteredPosts.size()) + return ; + + rsGxsChannels->markRead(RsGxsGrpMsgIdPair(mPosts[mFilteredPosts[entry]].mMeta.mGroupId,mPosts[mFilteredPosts[entry]].mMeta.mMsgId),read_status); +} + +QModelIndex RsGxsChannelPostsModel::getIndexOfMessage(const RsGxsMessageId& mid) const +{ + // Brutal search. This is not so nice, so dont call that in a loop! If too costly, we'll use a map. + + RsGxsMessageId postId = mid; + + for(uint32_t i=0;i * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include "retroshare/rsgxschannels.h" +#include "retroshare/rsgxsifacetypes.h" +#include "retroshare/rsevents.h" + +#include +#include + +struct ChannelPostFileInfo; + +// This class holds the actual hierarchy of posts, represented by identifiers +// It is responsible for auto-updating when necessary and holds a mutex to allow the Model to +// safely access the data. + +// The model contains a post in place 0 that is the parent of all posts. + +typedef uint32_t ChannelPostsModelIndex; + +// struct ChannelPostsModelPostEntry +// { +// ChannelPostsModelPostEntry() : mPublishTs(0),mPostFlags(0),mMsgStatus(0),prow(0) {} +// +// enum { // flags for display of posts. To be used in mPostFlags +// FLAG_POST_IS_PINNED = 0x0001, +// FLAG_POST_IS_MISSING = 0x0002, +// FLAG_POST_IS_REDACTED = 0x0004, +// FLAG_POST_HAS_UNREAD_CHILDREN = 0x0008, +// FLAG_POST_HAS_READ_CHILDREN = 0x0010, +// FLAG_POST_PASSES_FILTER = 0x0020, +// FLAG_POST_CHILDREN_PASSES_FILTER = 0x0040, +// }; +// +// std::string mTitle ; +// RsGxsMessageId mMsgId; +// uint32_t mPublishTs; +// uint32_t mPostFlags; +// int mMsgStatus; +// +// int prow ;// parent row, which basically means position in the array of posts +// }; + +// This class is the item model used by Qt to display the information + +class RsGxsChannelPostsModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit RsGxsChannelPostsModel(QObject *parent = NULL); + virtual ~RsGxsChannelPostsModel() override; + + static const int COLUMN_THREAD_NB_COLUMNS = 0x01; + +#ifdef TODO + enum Columns { + COLUMN_THREAD_TITLE =0x00, + COLUMN_THREAD_READ =0x01, + COLUMN_THREAD_DATE =0x02, + COLUMN_THREAD_DISTRIBUTION =0x03, + COLUMN_THREAD_AUTHOR =0x04, + COLUMN_THREAD_CONTENT =0x05, + COLUMN_THREAD_MSGID =0x06, + COLUMN_THREAD_DATA =0x07, + }; + + enum Roles{ SortRole = Qt::UserRole+1, + ThreadPinnedRole = Qt::UserRole+2, + MissingRole = Qt::UserRole+3, + StatusRole = Qt::UserRole+4, + UnreadChildrenRole = Qt::UserRole+5, + FilterRole = Qt::UserRole+6, + }; +#endif + + enum TreeMode{ TREE_MODE_UNKWN = 0x00, + TREE_MODE_GRID = 0x01, + TREE_MODE_LIST = 0x02, + }; + +#ifdef TODO + enum SortMode{ SORT_MODE_PUBLISH_TS = 0x00, + SORT_MODE_CHILDREN_PUBLISH_TS = 0x01, + }; +#endif + + QModelIndex root() const{ return createIndex(0,0,(void*)NULL) ;} + QModelIndex getIndexOfMessage(const RsGxsMessageId& mid) const; + + std::vector > getPostVersions(const RsGxsMessageId& mid) const; + + uint32_t getNumberOfPosts() { return mPosts.size() ; } + const RsGxsChannelPost& post(uint32_t i) const { return mPosts[i]; } + + // This method will asynchroneously update the data + void updateChannel(const RsGxsGroupId& channel_group_id); + const RsGxsGroupId& currentGroupId() const; + + void triggerViewUpdate(); + + void setNumColumns(int n); + void setMode(TreeMode mode); + TreeMode getMode() const { return mTreeMode; } + + // Retrieve the full list of files for all posts. + + void getFilesList(std::list &files); + +#ifdef TODO + void setSortMode(SortMode mode) ; + + void setTextColorRead (QColor color) { mTextColorRead = color;} + void setTextColorUnread (QColor color) { mTextColorUnread = color;} + void setTextColorUnreadChildren(QColor color) { mTextColorUnreadChildren = color;} + void setTextColorNotSubscribed (QColor color) { mTextColorNotSubscribed = color;} + void setTextColorMissing (QColor color) { mTextColorMissing = color;} +#endif + + void setMsgReadStatus(const QModelIndex &i, bool read_status); + void setAllMsgReadStatus(bool read_status); + + void setFilter(const QStringList &strings, bool only_unread,uint32_t &count) ; + +#ifdef TODO + void setAuthorOpinion(const QModelIndex& indx,RsOpinion op); +#endif + + // Helper functions + + bool getPostData(const QModelIndex& i,RsGxsChannelPost& fmpe) const ; + void clear() ; + + // AbstractItemModel functions. + + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + bool hasChildren(const QModelIndex &parent = QModelIndex()) const override; + + QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex& child) const override; + Qt::ItemFlags flags(const QModelIndex& index) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + // Custom item roles + + QVariant sizeHintRole (int col) const; + QVariant displayRole (const RsGxsChannelPost& fmpe, int col) const; + QVariant toolTipRole (const RsGxsChannelPost& fmpe, int col) const; + QVariant userRole (const RsGxsChannelPost& fmpe, int col) const; +#ifdef TODO + QVariant decorationRole(const ForumModelPostEntry& fmpe, int col) const; + QVariant pinnedRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant missingRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant statusRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant authorRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant sortRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant fontRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant filterRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant textColorRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant backgroundRole(const ForumModelPostEntry& fmpe, int col) const; +#endif + + /*! + * \brief debug_dump + * Dumps the hierarchy of posts in the terminal, to allow checking whether the internal representation is correct. + */ + void debug_dump(); + +signals: + void channelPostsLoaded(); // emitted after the posts have been loaded. + +private: + RsGxsChannelGroup mChannelGroup; + +#ifdef TODO + bool mUseChildTS; + bool mFilteringEnabled; + SortMode mSortMode; +#endif + TreeMode mTreeMode; + + uint32_t mColumns; + + void preMods() ; + void postMods() ; + + quintptr getParentRow(quintptr ref,int& row) const; + quintptr getChildRef(quintptr ref, int index) const; + int getChildrenCount(quintptr ref) const; + + static bool convertTabEntryToRefPointer(uint32_t entry, quintptr &ref); + static bool convertRefPointerToTabEntry(quintptr ref,uint32_t& entry); + static void computeReputationLevel(uint32_t forum_sign_flags, RsGxsChannelPost& entry); + + void update_posts(const RsGxsGroupId& group_id); + +#ifdef TODO + void setForumMessageSummary(const std::vector& messages); +#endif + void recursUpdateReadStatusAndTimes(ChannelPostsModelIndex i,bool& has_unread_below,bool& has_read_below); + uint32_t recursUpdateFilterStatus(ChannelPostsModelIndex i,int column,const QStringList& strings); + void recursSetMsgReadStatus(ChannelPostsModelIndex i,bool read_status,bool with_children); + +#ifdef TODO + static void generateMissingItem(const RsGxsMessageId &msgId,ChannelPostsModelPostEntry& entry); +#endif + //static ChannelModelIndex addEntry(std::vector& posts,const ChannelModelPostEntry& entry,ChannelModelIndex parent); + //static void convertMsgToPostEntry(const RsGxsChannelGroup &mChannelGroup, const RsMsgMetaData &msg, bool useChildTS, ChannelModelPostEntry& fentry); + + //void computeMessagesHierarchy(const RsGxsChannelGroup& forum_group, const std::vector &msgs_array, std::vector &posts, std::map > > &mPostVersions); + void createPostsArray(std::vector &posts); + void setPosts(const RsGxsChannelGroup& group, std::vector &posts); + void initEmptyHierarchy(); + void handleEvent_main_thread(std::shared_ptr event); + + std::vector mFilteredPosts; // stores the list of displayes indices due to filtering. + std::vector mPosts ; // store the list of posts updated from rsForums. + + QColor mTextColorRead ; + QColor mTextColorUnread ; + QColor mTextColorUnreadChildren; + QColor mTextColorNotSubscribed ; + QColor mTextColorMissing ; + + RsEventsHandlerId_t mEventHandlerId ; + friend class const_iterator; +}; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.cpp deleted file mode 100644 index 3286c5f8b..000000000 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.cpp +++ /dev/null @@ -1,821 +0,0 @@ -/******************************************************************************* - * retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.cpp * - * * - * Copyright 2013 by Robert Fernie * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Affero General Public License as * - * published by the Free Software Foundation, either version 3 of the * - * License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Affero General Public License for more details. * - * * - * You should have received a copy of the GNU Affero General Public License * - * along with this program. If not, see . * - * * - *******************************************************************************/ - -#include -#include - -#include "retroshare/rsgxscircles.h" - -#include "GxsChannelPostsWidget.h" -#include "ui_GxsChannelPostsWidget.h" -#include "gui/feeds/GxsChannelPostItem.h" -#include "gui/gxs/GxsIdDetails.h" -#include "gui/gxschannels/CreateGxsChannelMsg.h" -#include "gui/common/UIStateHelper.h" -#include "gui/settings/rsharesettings.h" -#include "gui/feeds/SubFileItem.h" -#include "gui/notifyqt.h" -#include "gui/RetroShareLink.h" -#include "util/HandleRichText.h" -#include "util/DateTime.h" -#include "util/qtthreadsutils.h" - -#include - -#define CHAN_DEFAULT_IMAGE ":/images/channels.png" - -#define ROLE_PUBLISH FEED_TREEWIDGET_SORTROLE - -/**** - * #define DEBUG_CHANNEL - ***/ - -/* View mode */ -#define VIEW_MODE_FEEDS 1 -#define VIEW_MODE_FILES 2 - -/** Constructor */ -GxsChannelPostsWidget::GxsChannelPostsWidget(const RsGxsGroupId &channelId, QWidget *parent) : - GxsMessageFramePostWidget(rsGxsChannels, parent), - ui(new Ui::GxsChannelPostsWidget) -{ - /* Invoke the Qt Designer generated object setup routine */ - ui->setupUi(this); - - - /* Setup UI helper */ - - mStateHelper->addWidget(mTokenTypeAllPosts, ui->progressBar, UISTATE_LOADING_VISIBLE); - mStateHelper->addWidget(mTokenTypeAllPosts, ui->loadingLabel, UISTATE_LOADING_VISIBLE); - mStateHelper->addWidget(mTokenTypeAllPosts, ui->filterLineEdit); - - mStateHelper->addWidget(mTokenTypePosts, ui->loadingLabel, UISTATE_LOADING_VISIBLE); - - mStateHelper->addLoadPlaceholder(mTokenTypeGroupData, ui->nameLabel); - - mStateHelper->addWidget(mTokenTypeGroupData, ui->postButton); - mStateHelper->addWidget(mTokenTypeGroupData, ui->logoLabel); - mStateHelper->addWidget(mTokenTypeGroupData, ui->subscribeToolButton); - - /* Connect signals */ - connect(ui->postButton, SIGNAL(clicked()), this, SLOT(createMsg())); - connect(ui->subscribeToolButton, SIGNAL(subscribe(bool)), this, SLOT(subscribeGroup(bool))); - connect(NotifyQt::getInstance(), SIGNAL(settingsChanged()), this, SLOT(settingsChanged())); - - ui->postButton->setText(tr("Add new post")); - - /* add filter actions */ - ui->filterLineEdit->addFilter(QIcon(), tr("Title"), FILTER_TITLE, tr("Search Title")); - ui->filterLineEdit->addFilter(QIcon(), tr("Message"), FILTER_MSG, tr("Search Message")); - ui->filterLineEdit->addFilter(QIcon(), tr("Filename"), FILTER_FILE_NAME, tr("Search Filename")); - connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), ui->feedWidget, SLOT(setFilterText(QString))); - connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), ui->fileWidget, SLOT(setFilterText(QString))); - connect(ui->filterLineEdit, SIGNAL(filterChanged(int)), this, SLOT(filterChanged(int))); - - /* Initialize view button */ - //setViewMode(VIEW_MODE_FEEDS); see processSettings - ui->infoWidget->hide(); - - QSignalMapper *signalMapper = new QSignalMapper(this); - connect(ui->feedToolButton, SIGNAL(clicked()), signalMapper, SLOT(map())); - connect(ui->fileToolButton, SIGNAL(clicked()), signalMapper, SLOT(map())); - signalMapper->setMapping(ui->feedToolButton, VIEW_MODE_FEEDS); - signalMapper->setMapping(ui->fileToolButton, VIEW_MODE_FILES); - connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(setViewMode(int))); - - /*************** Setup Left Hand Side (List of Channels) ****************/ - - ui->loadingLabel->hide(); - ui->progressBar->hide(); - - ui->nameLabel->setMinimumWidth(20); - - /* Initialize feed widget */ - ui->feedWidget->setSortRole(ROLE_PUBLISH, Qt::DescendingOrder); - ui->feedWidget->setFilterCallback(filterItem); - - /* load settings */ - processSettings(true); - - /* Initialize subscribe button */ - QIcon icon; - icon.addPixmap(QPixmap(":/images/redled.png"), QIcon::Normal, QIcon::On); - icon.addPixmap(QPixmap(":/images/start.png"), QIcon::Normal, QIcon::Off); - mAutoDownloadAction = new QAction(icon, "", this); - mAutoDownloadAction->setCheckable(true); - connect(mAutoDownloadAction, SIGNAL(triggered()), this, SLOT(toggleAutoDownload())); - - ui->subscribeToolButton->addSubscribedAction(mAutoDownloadAction); - - /* Initialize GUI */ - setAutoDownload(false); - settingsChanged(); - setGroupId(channelId); - - mEventHandlerId = 0; - // Needs to be asynced because this function is likely to be called by another thread! - - rsEvents->registerEventsHandler(RsEventType::GXS_CHANNELS, [this](std::shared_ptr event) { RsQThreadUtils::postToObject( [=]() { handleEvent_main_thread(event); }, this ); }, mEventHandlerId ); -} - -void GxsChannelPostsWidget::handleEvent_main_thread(std::shared_ptr event) -{ - const RsGxsChannelEvent *e = dynamic_cast(event.get()); - - if(!e) - return; - - switch(e->mChannelEventCode) - { - case RsChannelEventCode::UPDATED_CHANNEL: - case RsChannelEventCode::NEW_CHANNEL: - case RsChannelEventCode::UPDATED_MESSAGE: - case RsChannelEventCode::NEW_MESSAGE: - if(e->mChannelGroupId == mChannelGroupId) - updateDisplay(true); - break; - default: - break; - } -} - -GxsChannelPostsWidget::~GxsChannelPostsWidget() -{ - rsEvents->unregisterEventsHandler(mEventHandlerId); - // save settings - processSettings(false); - - delete(mAutoDownloadAction); - - delete ui; -} - -void GxsChannelPostsWidget::processSettings(bool load) -{ - Settings->beginGroup(QString("ChannelPostsWidget")); - - if (load) { - // load settings - - /* Filter */ - ui->filterLineEdit->setCurrentFilter(Settings->value("filter", FILTER_TITLE).toInt()); - - /* View mode */ - setViewMode(Settings->value("viewMode", VIEW_MODE_FEEDS).toInt()); - } else { - // save settings - - /* Filter */ - Settings->setValue("filter", ui->filterLineEdit->currentFilter()); - - /* View mode */ - Settings->setValue("viewMode", viewMode()); - } - - Settings->endGroup(); -} - -void GxsChannelPostsWidget::settingsChanged() -{ - mUseThread = Settings->getChannelLoadThread(); - - mStateHelper->setWidgetVisible(ui->progressBar, mUseThread); -} - -void GxsChannelPostsWidget::groupNameChanged(const QString &name) -{ - if (groupId().isNull()) { - ui->nameLabel->setText(tr("No Channel Selected")); - ui->logoLabel->setPixmap(QPixmap(":/images/channels.png")); - } else { - ui->nameLabel->setText(name); - } -} - -QIcon GxsChannelPostsWidget::groupIcon() -{ - if (mStateHelper->isLoading(mTokenTypeGroupData) || mStateHelper->isLoading(mTokenTypeAllPosts)) { - return QIcon(":/images/kalarm.png"); - } - -// if (mNewCount) { -// return QIcon(":/images/message-state-new.png"); -// } - - return QIcon(); -} - -/*************************************************************************************/ -/*************************************************************************************/ -/*************************************************************************************/ - -QScrollArea *GxsChannelPostsWidget::getScrollArea() -{ - return NULL; -} - -void GxsChannelPostsWidget::deleteFeedItem(FeedItem *feedItem, uint32_t /*type*/) -{ - if (!feedItem) - return; - - ui->feedWidget->removeFeedItem(feedItem); -} - -void GxsChannelPostsWidget::openChat(const RsPeerId & /*peerId*/) -{ -} - -// Callback from Widget->FeedHolder->ServiceDialog->CommentContainer->CommentDialog, -void GxsChannelPostsWidget::openComments(uint32_t /*type*/, const RsGxsGroupId &groupId, const QVector& msg_versions,const RsGxsMessageId &msgId, const QString &title) -{ - emit loadComment(groupId, msg_versions,msgId, title); -} - -void GxsChannelPostsWidget::createMsg() -{ - if (groupId().isNull()) { - return; - } - - if (!IS_GROUP_SUBSCRIBED(subscribeFlags())) { - return; - } - - CreateGxsChannelMsg *msgDialog = new CreateGxsChannelMsg(groupId()); - msgDialog->show(); - - /* window will destroy itself! */ -} - -void GxsChannelPostsWidget::insertChannelDetails(const RsGxsChannelGroup &group) -{ - /* IMAGE */ - QPixmap chanImage; - if (group.mImage.mData != NULL) { - GxsIdDetails::loadPixmapFromData(group.mImage.mData, group.mImage.mSize, chanImage,GxsIdDetails::ORIGINAL); - } else { - chanImage = QPixmap(CHAN_DEFAULT_IMAGE); - } - ui->logoLabel->setPixmap(chanImage); - - if (group.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_PUBLISH) - { - mStateHelper->setWidgetEnabled(ui->postButton, true); - } - else - { - mStateHelper->setWidgetEnabled(ui->postButton, false); - } - - ui->subscribeToolButton->setSubscribed(IS_GROUP_SUBSCRIBED(group.mMeta.mSubscribeFlags)); - mStateHelper->setWidgetEnabled(ui->subscribeToolButton, true); - - - bool autoDownload ; - rsGxsChannels->getChannelAutoDownload(group.mMeta.mGroupId,autoDownload); - setAutoDownload(autoDownload); - - RetroShareLink link; - - if (IS_GROUP_SUBSCRIBED(group.mMeta.mSubscribeFlags)) { - ui->feedToolButton->setEnabled(true); - - ui->fileToolButton->setEnabled(true); - ui->infoWidget->hide(); - setViewMode(viewMode()); - - ui->subscribeToolButton->setText(tr("Subscribed") + " " + QString::number(group.mMeta.mPop) ); - - - ui->infoPosts->clear(); - ui->infoDescription->clear(); - } else { - ui->infoPosts->setText(QString::number(group.mMeta.mVisibleMsgCount)); - if(group.mMeta.mLastPost==0) - ui->infoLastPost->setText(tr("Never")); - else - ui->infoLastPost->setText(DateTime::formatLongDateTime(group.mMeta.mLastPost)); - QString formatDescription = QString::fromUtf8(group.mDescription.c_str()); - - unsigned int formatFlag = RSHTML_FORMATTEXT_EMBED_LINKS; - - // embed smileys ? - if (Settings->valueFromGroup(QString("ChannelPostsWidget"), QString::fromUtf8("Emoteicons_ChannelDecription"), true).toBool()) { - formatFlag |= RSHTML_FORMATTEXT_EMBED_SMILEYS; - } - - formatDescription = RsHtml().formatText(NULL, formatDescription, formatFlag); - - ui->infoDescription->setText(formatDescription); - - ui->infoAdministrator->setId(group.mMeta.mAuthorId) ; - - link = RetroShareLink::createMessage(group.mMeta.mAuthorId, ""); - ui->infoAdministrator->setText(link.toHtml()); - - ui->infoCreated->setText(DateTime::formatLongDateTime(group.mMeta.mPublishTs)); - - QString distrib_string ( "[unknown]" ); - - switch(group.mMeta.mCircleType) - { - case GXS_CIRCLE_TYPE_PUBLIC: distrib_string = tr("Public") ; - break ; - case GXS_CIRCLE_TYPE_EXTERNAL: - { - RsGxsCircleDetails det ; - - // !! What we need here is some sort of CircleLabel, which loads the circle and updates the label when done. - - if(rsGxsCircles->getCircleDetails(group.mMeta.mCircleId,det)) - distrib_string = tr("Restricted to members of circle \"")+QString::fromUtf8(det.mCircleName.c_str()) +"\""; - else - distrib_string = tr("Restricted to members of circle ")+QString::fromStdString(group.mMeta.mCircleId.toStdString()) ; - } - break ; - case GXS_CIRCLE_TYPE_YOUR_EYES_ONLY: distrib_string = tr("Your eyes only"); - break ; - case GXS_CIRCLE_TYPE_LOCAL: distrib_string = tr("You and your friend nodes"); - break ; - default: - std::cerr << "(EE) badly initialised group distribution ID = " << group.mMeta.mCircleType << std::endl; - } - - ui->infoDistribution->setText(distrib_string); - - ui->infoWidget->show(); - ui->feedWidget->hide(); - ui->fileWidget->hide(); - - ui->feedToolButton->setEnabled(false); - ui->fileToolButton->setEnabled(false); - - ui->subscribeToolButton->setText(tr("Subscribe ") + " " + QString::number(group.mMeta.mPop) ); - - } -} - -int GxsChannelPostsWidget::viewMode() -{ - if (ui->feedToolButton->isChecked()) { - return VIEW_MODE_FEEDS; - } else if (ui->fileToolButton->isChecked()) { - return VIEW_MODE_FILES; - } - - /* Default */ - return VIEW_MODE_FEEDS; -} - -void GxsChannelPostsWidget::setViewMode(int viewMode) -{ - switch (viewMode) { - case VIEW_MODE_FEEDS: - ui->feedWidget->show(); - ui->fileWidget->hide(); - - ui->feedToolButton->setChecked(true); - ui->fileToolButton->setChecked(false); - - break; - case VIEW_MODE_FILES: - ui->feedWidget->hide(); - ui->fileWidget->show(); - - ui->feedToolButton->setChecked(false); - ui->fileToolButton->setChecked(true); - - break; - default: - setViewMode(VIEW_MODE_FEEDS); - return; - } -} - -void GxsChannelPostsWidget::filterChanged(int filter) -{ - ui->feedWidget->setFilterType(filter); - ui->fileWidget->setFilterType(filter); -} - -/*static*/ bool GxsChannelPostsWidget::filterItem(FeedItem *feedItem, const QString &text, int filter) -{ - GxsChannelPostItem *item = dynamic_cast(feedItem); - if (!item) { - return true; - } - - bool bVisible = text.isEmpty(); - - if (!bVisible) - { - switch(filter) - { - case FILTER_TITLE: - bVisible = item->getTitleLabel().contains(text,Qt::CaseInsensitive); - break; - case FILTER_MSG: - bVisible = item->getMsgLabel().contains(text,Qt::CaseInsensitive); - break; - case FILTER_FILE_NAME: - { - std::list fileItems = item->getFileItems(); - std::list::iterator lit; - for(lit = fileItems.begin(); lit != fileItems.end(); ++lit) - { - SubFileItem *fi = *lit; - QString fileName = QString::fromUtf8(fi->FileName().c_str()); - bVisible = (bVisible || fileName.contains(text,Qt::CaseInsensitive)); - } - break; - } - default: - bVisible = true; - break; - } - } - - return bVisible; -} - -void GxsChannelPostsWidget::createPostItem(const RsGxsChannelPost& post, bool related) -{ - GxsChannelPostItem *item = NULL; - - if(!post.mMeta.mOrigMsgId.isNull()) - { - FeedItem *feedItem = ui->feedWidget->findFeedItem(GxsChannelPostItem::computeIdentifier(post.mMeta.mOrigMsgId)) ; - item = dynamic_cast(feedItem); - - if(item) - { - ui->feedWidget->removeFeedItem(item) ; - - GxsChannelPostItem *item = new GxsChannelPostItem(this, 0, post, true, false,post.mOlderVersions); - ui->feedWidget->addFeedItem(item, ROLE_PUBLISH, QDateTime::fromTime_t(post.mMeta.mPublishTs)); - - return ; - } - } - - if (related) - { - FeedItem *feedItem = ui->feedWidget->findFeedItem(GxsChannelPostItem::computeIdentifier(post.mMeta.mMsgId)) ; - item = dynamic_cast(feedItem); - } - if (item) { - item->setPost(post); - ui->feedWidget->setSort(item, ROLE_PUBLISH, QDateTime::fromTime_t(post.mMeta.mPublishTs)); - } else { - GxsChannelPostItem *item = new GxsChannelPostItem(this, 0, post, true, false,post.mOlderVersions); - ui->feedWidget->addFeedItem(item, ROLE_PUBLISH, QDateTime::fromTime_t(post.mMeta.mPublishTs)); - } - - ui->fileWidget->addFiles(post, related); -} - -void GxsChannelPostsWidget::fillThreadCreatePost(const QVariant &post, bool related, int current, int count) -{ - /* show fill progress */ - if (count) { - ui->progressBar->setValue(current * ui->progressBar->maximum() / count); - } - - if (!post.canConvert()) { - return; - } - - createPostItem(post.value(), related); -} - -void GxsChannelPostsWidget::insertChannelPosts(std::vector &posts, GxsMessageFramePostThread *thread, bool related) -{ - if (related && thread) { - std::cerr << "GxsChannelPostsWidget::insertChannelPosts fill only related posts as thread is not possible" << std::endl; - return; - } - - int count = posts.size(); - int pos = 0; - - if (!thread) { - ui->feedWidget->setSortingEnabled(false); - } - - // collect new versions of posts if any - -#ifdef DEBUG_CHANNEL - std::cerr << "Inserting channel posts" << std::endl; -#endif - - std::vector new_versions ; - for (uint32_t i=0;i search_map ; - for (uint32_t i=0;i versions ; - std::map::const_iterator vit ; - - while(search_map.end() != (vit=search_map.find(posts[current_index].mMeta.mOrigMsgId))) - { -#ifdef DEBUG_CHANNEL - std::cerr << " post at index " << current_index << " replaces a post at position " << vit->second ; -#endif - - // Now replace the post only if the new versionis more recent. It may happen indeed that the same post has been corrected multiple - // times. In this case, we only need to replace the post with the newest version - - //uint32_t prev_index = current_index ; - current_index = vit->second ; - - if(posts[current_index].mMeta.mMsgId.isNull()) // This handles the branching situation where this post has been already erased. No need to go down further. - { -#ifdef DEBUG_CHANNEL - std::cerr << " already erased. Stopping." << std::endl; -#endif - break ; - } - - if(posts[current_index].mMeta.mPublishTs < posts[source_index].mMeta.mPublishTs) - { -#ifdef DEBUG_CHANNEL - std::cerr << " and is more recent => following" << std::endl; -#endif - for(std::set::const_iterator itt(posts[current_index].mOlderVersions.begin());itt!=posts[current_index].mOlderVersions.end();++itt) - posts[source_index].mOlderVersions.insert(*itt); - - posts[source_index].mOlderVersions.insert(posts[current_index].mMeta.mMsgId); - posts[current_index].mMeta.mMsgId.clear(); // clear the msg Id so the post will be ignored - } -#ifdef DEBUG_CHANNEL - else - std::cerr << " but is older -> Stopping" << std::endl; -#endif - } - } - } - -#ifdef DEBUG_CHANNEL - std::cerr << "Now adding posts..." << std::endl; -#endif - - for (std::vector::const_reverse_iterator it = posts.rbegin(); it != posts.rend(); ++it) - { -#ifdef DEBUG_CHANNEL - std::cerr << " adding post: " << (*it).mMeta.mMsgId ; -#endif - - if(!(*it).mMeta.mMsgId.isNull()) - { -#ifdef DEBUG_CHANNEL - std::cerr << " added" << std::endl; -#endif - - if (thread && thread->stopped()) - break; - - if (thread) - thread->emitAddPost(qVariantFromValue(*it), related, ++pos, count); - else - createPostItem(*it, related); - } -#ifdef DEBUG_CHANNEL - else - std::cerr << " skipped" << std::endl; -#endif - } - - if (!thread) { - ui->feedWidget->setSortingEnabled(true); - } -} - -void GxsChannelPostsWidget::clearPosts() -{ - ui->feedWidget->clear(); - ui->fileWidget->clear(); -} - -void GxsChannelPostsWidget::blank() -{ - mStateHelper->setWidgetEnabled(ui->postButton, false); - mStateHelper->setWidgetEnabled(ui->subscribeToolButton, false); - - clearPosts(); - - groupNameChanged(QString()); - - ui->infoWidget->hide(); - ui->feedWidget->show(); - ui->fileWidget->hide(); -} - -bool GxsChannelPostsWidget::navigatePostItem(const RsGxsMessageId &msgId) -{ - FeedItem *feedItem = ui->feedWidget->findFeedItem(GxsChannelPostItem::computeIdentifier(msgId)); - if (!feedItem) { - return false; - } - - return ui->feedWidget->scrollTo(feedItem, true); -} - -void GxsChannelPostsWidget::subscribeGroup(bool subscribe) -{ - RsGxsGroupId grpId(groupId()); - if (grpId.isNull()) return; - - RsThread::async([=]() - { - rsGxsChannels->subscribeToChannel(grpId, subscribe); - } ); -} - -void GxsChannelPostsWidget::setAutoDownload(bool autoDl) -{ - mAutoDownloadAction->setChecked(autoDl); - mAutoDownloadAction->setText(autoDl ? tr("Disable Auto-Download") : tr("Enable Auto-Download")); -} - -void GxsChannelPostsWidget::toggleAutoDownload() -{ - RsGxsGroupId grpId = groupId(); - if (grpId.isNull()) { - return; - } - - bool autoDownload; - if(!rsGxsChannels->getChannelAutoDownload(grpId, autoDownload)) - { - std::cerr << __PRETTY_FUNCTION__ << " failed to get autodownload value " - << "for channel: " << grpId.toStdString() << std::endl; - return; - } - - RsThread::async([this, grpId, autoDownload]() - { - if(!rsGxsChannels->setChannelAutoDownload(grpId, !autoDownload)) - { - std::cerr << __PRETTY_FUNCTION__ << " failed to set autodownload " - << "for channel: " << grpId.toStdString() << std::endl; - return; - } - -// RsQThreadUtils::postToObject( [=]() -// { -// /* Here it goes any code you want to be executed on the Qt Gui -// * thread, for example to update the data model with new information -// * after a blocking call to RetroShare API complete, note that -// * Qt::QueuedConnection is important! -// */ -// -// std::cerr << __PRETTY_FUNCTION__ << " Has been executed on GUI " -// << "thread but was scheduled by async thread" << std::endl; -// }, this ); - }); -} - -bool GxsChannelPostsWidget::insertGroupData(const uint32_t &token, RsGroupMetaData &metaData) -{ - std::vector groups; - rsGxsChannels->getGroupData(token, groups); - - if(groups.size() == 1) - { - insertChannelDetails(groups[0]); - metaData = groups[0].mMeta; - mChannelGroupId = groups[0].mMeta.mGroupId; - return true; - } - else - { - RsGxsChannelGroup distant_group; - if(rsGxsChannels->retrieveDistantGroup(groupId(),distant_group)) - { - insertChannelDetails(distant_group); - metaData = distant_group.mMeta; - mChannelGroupId = distant_group.mMeta.mGroupId; - return true ; - } - } - - return false; -} - -void GxsChannelPostsWidget::insertAllPosts(const uint32_t &token, GxsMessageFramePostThread *thread) -{ - std::vector posts; - rsGxsChannels->getPostData(token, posts); - - insertChannelPosts(posts, thread, false); -} - -void GxsChannelPostsWidget::insertPosts(const uint32_t &token) -{ - std::vector posts; - rsGxsChannels->getPostData(token, posts); - - insertChannelPosts(posts, NULL, true); -} - -class GxsChannelPostsReadData -{ -public: - GxsChannelPostsReadData(bool read) - { - mRead = read; - mLastToken = 0; - } - -public: - bool mRead; - uint32_t mLastToken; -}; - -static void setAllMessagesReadCallback(FeedItem *feedItem, void *data) -{ - GxsChannelPostItem *channelPostItem = dynamic_cast(feedItem); - if (!channelPostItem) { - return; - } - - GxsChannelPostsReadData *readData = (GxsChannelPostsReadData*) data; - bool is_not_new = !channelPostItem->isUnread() ; - - if(is_not_new == readData->mRead) - return ; - - RsGxsGrpMsgIdPair msgPair = std::make_pair(channelPostItem->groupId(), channelPostItem->messageId()); - rsGxsChannels->setMessageReadStatus(readData->mLastToken, msgPair, readData->mRead); -} - -void GxsChannelPostsWidget::setAllMessagesReadDo(bool read, uint32_t &token) -{ - if (groupId().isNull() || !IS_GROUP_SUBSCRIBED(subscribeFlags())) { - return; - } - - GxsChannelPostsReadData data(read); - ui->feedWidget->withAll(setAllMessagesReadCallback, &data); - - token = data.mLastToken; -} diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.h deleted file mode 100644 index 6f27ed677..000000000 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.h +++ /dev/null @@ -1,113 +0,0 @@ -/******************************************************************************* - * retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.h * - * * - * Copyright 2013 by Robert Fernie * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Affero General Public License as * - * published by the Free Software Foundation, either version 3 of the * - * License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Affero General Public License for more details. * - * * - * You should have received a copy of the GNU Affero General Public License * - * along with this program. If not, see . * - * * - *******************************************************************************/ - -#ifndef _GXS_CHANNELPOSTSWIDGET_H -#define _GXS_CHANNELPOSTSWIDGET_H - -#include - -#include "gui/gxs/GxsMessageFramePostWidget.h" - -#include "gui/feeds/FeedHolder.h" - -namespace Ui { -class GxsChannelPostsWidget; -} - -class GxsChannelPostItem; -class QTreeWidgetItem; -class FeedItem; - -class GxsChannelPostsWidget : public GxsMessageFramePostWidget, public FeedHolder -{ - Q_OBJECT - -public: - /* Filters */ - enum Filter { - FILTER_TITLE = 1, - FILTER_MSG = 2, - FILTER_FILE_NAME = 3 - }; - -public: - /** Default Constructor */ - GxsChannelPostsWidget(const RsGxsGroupId &channelId, QWidget *parent = 0); - /** Default Destructor */ - ~GxsChannelPostsWidget(); - - /* GxsMessageFrameWidget */ - virtual QIcon groupIcon(); - - /* FeedHolder */ - virtual QScrollArea *getScrollArea(); - virtual void deleteFeedItem(FeedItem *feedItem, uint32_t type); - virtual void openChat(const RsPeerId& peerId); - virtual void openComments(uint32_t type, const RsGxsGroupId &groupId, const QVector &msg_versions, const RsGxsMessageId &msgId, const QString &title); - -protected: - /* GxsMessageFramePostWidget */ - virtual void groupNameChanged(const QString &name); - virtual bool insertGroupData(const uint32_t &token, RsGroupMetaData &metaData); - virtual void insertAllPosts(const uint32_t &token, GxsMessageFramePostThread *thread); - virtual void insertPosts(const uint32_t &token); - virtual void clearPosts(); - virtual bool useThread() { return mUseThread; } - virtual void fillThreadCreatePost(const QVariant &post, bool related, int current, int count); - virtual bool navigatePostItem(const RsGxsMessageId& msgId); - virtual void blank() ; - - /* GxsMessageFrameWidget */ - virtual void setAllMessagesReadDo(bool read, uint32_t &token); - -private slots: - void createMsg(); - void toggleAutoDownload(); - void subscribeGroup(bool subscribe); - void filterChanged(int filter); - void setViewMode(int viewMode); - void settingsChanged(); - -private: - void processSettings(bool load); - - void setAutoDownload(bool autoDl); - static bool filterItem(FeedItem *feedItem, const QString &text, int filter); - - int viewMode(); - - void insertChannelDetails(const RsGxsChannelGroup &group); - void insertChannelPosts(std::vector &posts, GxsMessageFramePostThread *thread, bool related); - - void createPostItem(const RsGxsChannelPost &post, bool related); - void handleEvent_main_thread(std::shared_ptr event); - -private: - QAction *mAutoDownloadAction; - - bool mUseThread; - RsGxsGroupId mChannelGroupId; - RsEventsHandlerId_t mEventHandlerId ; - - /* UI - from Designer */ - Ui::GxsChannelPostsWidget *ui; -}; - -#endif diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.ui deleted file mode 100644 index fb6845f26..000000000 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.ui +++ /dev/null @@ -1,593 +0,0 @@ - - - GxsChannelPostsWidget - - - - 0 - 0 - 977 - 628 - - - - - 4 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::Box - - - QFrame::Sunken - - - - 4 - - - 4 - - - 4 - - - 4 - - - - - - 64 - 64 - - - - - 64 - 64 - - - - - - - :/images/channels.png - - - true - - - - - - - - - - - - - - Channel Name - - - - - - - - - - QFrame::Box - - - QFrame::Sunken - - - - 4 - - - 2 - - - 6 - - - 2 - - - - - - 0 - 0 - - - - Subscribe - - - - 16 - 16 - - - - QToolButton::MenuButtonPopup - - - Qt::ToolButtonTextBesideIcon - - - true - - - - - - - Qt::NoFocus - - - Post to Channel - - - Add new post - - - - :/icons/png/add.png:/icons/png/add.png - - - - 32 - 16 - - - - Qt::ToolButtonTextBesideIcon - - - true - - - - - - - Qt::Horizontal - - - QSizePolicy::MinimumExpanding - - - - 0 - 0 - - - - - - - - Loading - - - - - - - - 0 - 0 - - - - - 16777215 - 10 - - - - 1000 - - - 0 - - - - - - - 0 - - - - - Show feeds - - - - :/images/view-feeds.png:/images/view-feeds.png - - - true - - - true - - - - - - - Show files - - - - :/images/view-files.png:/images/view-files.png - - - true - - - true - - - - - - - - - - 0 - 0 - - - - - 200 - 0 - - - - - 200 - 16777215 - - - - Search channels - - - true - - - - - - - - - - - - - - 3 - - - 0 - - - 3 - - - 3 - - - - - false - - - QFrame::NoFrame - - - QFrame::Plain - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - Channel details - - - - 9 - - - 9 - - - 9 - - - 9 - - - - - - 0 - 0 - - - - - 75 - true - - - - Last Post: - - - - - - - <!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:'Sans'; font-size:9pt; 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:'MS Shell Dlg 2'; font-size:8pt;">Description</span></p></body></html> - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - true - - - true - - - - - - - - 75 - true - - - - Description: - - - - - - - - 75 - true - - - - Created: - - - - - - - - 75 - true - - - - Administrator: - - - - - - - - 0 - 0 - - - - - 75 - true - - - - Posts: - - - - - - - - 75 - true - - - - Distribution: - - - - - - - unknown - - - - - - - unknown - - - - - - - unknown - - - true - - - - - - - unknown - - - - - - - 0 - - - - - - - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - - - - - Feeds - - - - - Files - - - toolBarFrame - headFrame - feedWidget - fileWidget - infoWidget - - - - GxsIdLabel - QLabel -
gui/gxs/GxsIdLabel.h
-
- - SubscribeToolButton - QToolButton -
gui/common/SubscribeToolButton.h
-
- - StyledElidedLabel - QLabel -
gui/common/StyledElidedLabel.h
-
- - LineEditClear - QLineEdit -
gui/common/LineEditClear.h
-
- - GxsChannelFilesWidget - QWidget -
gui/gxschannels/GxsChannelFilesWidget.h
- 1 -
- - RSFeedWidget - QWidget -
gui/common/RSFeedWidget.h
- 1 -
-
- - - - - -
diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp new file mode 100644 index 000000000..046678ad9 --- /dev/null +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -0,0 +1,1237 @@ +/******************************************************************************* + * retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.cpp * + * * + * Copyright 2013 by Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include +#include +#include +#include +#include + +#include "retroshare/rsgxscircles.h" + +#include "ui_GxsChannelPostsWidgetWithModel.h" +#include "gui/feeds/GxsChannelPostItem.h" +#include "gui/gxs/GxsIdDetails.h" +#include "util/misc.h" +#include "gui/gxschannels/CreateGxsChannelMsg.h" +#include "gui/common/UIStateHelper.h" +#include "gui/settings/rsharesettings.h" +#include "gui/feeds/SubFileItem.h" +#include "gui/notifyqt.h" +#include "gui/RetroShareLink.h" +#include "util/HandleRichText.h" +#include "util/DateTime.h" +#include "util/qtthreadsutils.h" +#include "gui/common/FilesDefs.h" + +#include "GxsChannelPostsWidgetWithModel.h" +#include "GxsChannelPostsModel.h" +#include "GxsChannelPostFilesModel.h" +#include "GxsChannelFilesStatusWidget.h" +#include "GxsChannelPostThumbnail.h" + +#include + +#define ROLE_PUBLISH FEED_TREEWIDGET_SORTROLE + +/**** + * #define DEBUG_CHANNEL + ***/ + +static const int mTokenTypeGroupData = 1; + +static const int CHANNEL_TABS_DETAILS= 0; +static const int CHANNEL_TABS_POSTS = 1; +static const int CHANNEL_TABS_FILES = 2; + +QColor SelectedColor = QRgb(0xff308dc7); + +/* View mode */ +#define VIEW_MODE_FEEDS 1 +#define VIEW_MODE_FILES 2 + +// Determine the Shape and size of cells as a factor of the font height. An aspect ratio of 3/4 is what's needed +// for the image, so it's important that the height is a bit larger so as to leave some room for the text. +// +// +#define COLUMN_SIZE_FONT_FACTOR_W 6 +#define COLUMN_SIZE_FONT_FACTOR_H 10 + +#define STAR_OVERLAY_IMAGE ":icons/star_overlay_128.png" +#define IMAGE_COPYLINK ":icons/png/copy.png" +#define IMAGE_GRID_VIEW ":icons/png/menu.png" +#define IMAGE_DOWNLOAD ":icons/png/download.png" +#define IMAGE_UNREAD ":icons/png/message.png" + +Q_DECLARE_METATYPE(ChannelPostFileInfo) + +// Delegate used to paint into the table of thumbnails + +//===============================================================================================================================================// +//=== ChannelPostDelegate ===// +//===============================================================================================================================================// + +int ChannelPostDelegate::cellSize(int col,const QFont& font,uint32_t parent_width) const +{ + if(mUseGrid || col==0) + return mZoom*COLUMN_SIZE_FONT_FACTOR_W*QFontMetricsF(font).height(); + else + return 0.8*parent_width - mZoom*COLUMN_SIZE_FONT_FACTOR_W*QFontMetricsF(font).height(); +} + +void ChannelPostDelegate::zoom(bool zoom_or_unzoom) +{ + if(zoom_or_unzoom) + mZoom *= 1.02; + else + mZoom /= 1.02; + + if(mZoom < 0.5) + mZoom = 0.5; + if(mZoom > 2.0) + mZoom = 2.0; +} + +void ChannelPostDelegate::setAspectRatio(ChannelPostThumbnailView::AspectRatio r) +{ + mAspectRatio = r; +} +void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const +{ + // prepare + painter->save(); + painter->setClipRect(option.rect); + + RsGxsChannelPost post = index.data(Qt::UserRole).value() ; + + // if(index.row() & 0x01) + // painter->fillRect( option.rect, option.palette.alternateBase().color()); + // else + painter->fillRect( option.rect, option.palette.base().color()); + + painter->restore(); + + if(mUseGrid || index.column()==0) + { + // Draw a thumbnail + + uint32_t flags = (mUseGrid)?(ChannelPostThumbnailView::FLAG_SHOW_TEXT | ChannelPostThumbnailView::FLAG_SCALE_FONT):0; + ChannelPostThumbnailView w(post,flags); + w.setBackgroundRole(QPalette::AlternateBase); + w.setAspectRatio(mAspectRatio); + w.updateGeometry(); + w.adjustSize(); + + QPixmap pixmap(w.size()); + + if((option.state & QStyle::State_Selected) && post.mMeta.mPublishTs > 0) // check if post is selected and is not empty (end of last row) + pixmap.fill(SelectedColor); // I dont know how to grab the backgroud color for selected objects automatically. + else + { + // we need to do the alternate color manually + + //if(index.row() & 0x01) + // pixmap.fill(option.palette.alternateBase().color()); + //else + pixmap.fill(option.palette.base().color()); + } + + w.render(&pixmap,QPoint(),QRegion(),QWidget::DrawChildren );// draw the widgets, not the background + + // We extract from the pixmap the part of the widget that we want. Saddly enough, Qt adds some white space + // below the widget and there is no way to control that. + + pixmap = pixmap.copy(QRect(0,0,w.actualSize().width(),w.actualSize().height())); + +// if(index.row()==0 && index.column()==0) +// { +// QFile file("yourFile.png"); +// file.open(QIODevice::WriteOnly); +// pixmap.save(&file, "PNG"); +// file.close(); +// } + + if(mUseGrid || index.column()==0) + { + if(mZoom != 1.0) + pixmap = pixmap.scaled(mZoom*pixmap.size(),Qt::KeepAspectRatio,Qt::SmoothTransformation); + + if(IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus)) + { + QPainter p(&pixmap); + QFontMetricsF fm(option.font); + + p.drawPixmap(mZoom*QPoint(0.1*fm.height(),-3.6*fm.height()),FilesDefs::getPixmapFromQtResourcePath(STAR_OVERLAY_IMAGE).scaled(mZoom*7*fm.height(),mZoom*7*fm.height(),Qt::KeepAspectRatio,Qt::SmoothTransformation)); + } + } + + painter->drawPixmap(option.rect.topLeft(), + pixmap.scaled(option.rect.width(),option.rect.width()*pixmap.height()/(float)pixmap.width(),Qt::IgnoreAspectRatio,Qt::SmoothTransformation)); + } + else + { + // We're drawing the text on the second column + + uint32_t font_height = QFontMetricsF(option.font).height(); + QPoint p = option.rect.topLeft(); + float y = p.y() + font_height; + + painter->save(); + + if((option.state & QStyle::State_Selected) && post.mMeta.mPublishTs > 0) // check if post is selected and is not empty (end of last row) + painter->setPen(SelectedColor); + + if(IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus)) + { + QFont font(option.font); + font.setBold(true); + painter->setFont(font); + } + painter->drawText(QPoint(p.x()+0.5*font_height,y),QString::fromUtf8(post.mMeta.mMsgName.c_str())); + y += font_height; + + painter->drawText(QPoint(p.x()+0.5*font_height,y),QDateTime::fromSecsSinceEpoch(post.mMeta.mPublishTs).toString(Qt::DefaultLocaleShortDate)); + y += font_height; + + if(post.mCount > 0) + { + painter->drawText(QPoint(p.x()+0.5*font_height,y),QString::number(post.mCount)+ " " +((post.mCount>1)?tr("files"):tr("file")) + " (" + QString::number(post.mSize) + " " + tr("bytes") + ")" ); + y += font_height; + } + + painter->restore(); + } +} + +QSize ChannelPostDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + // This is the only place where we actually set the size of cells + + QFontMetricsF fm(option.font); + + RsGxsChannelPost post = index.data(Qt::UserRole).value() ; + uint32_t flags = (mUseGrid)?(ChannelPostThumbnailView::FLAG_SHOW_TEXT | ChannelPostThumbnailView::FLAG_SCALE_FONT):0; + + ChannelPostThumbnailView w(post,flags); + w.setAspectRatio(mAspectRatio); + w.updateGeometry(); + w.adjustSize(); + + //std::cerr << "w.size(): " << w.width() << " x " << w.height() << ". Actual size: " << w.actualSize().width() << " x " << w.actualSize().height() << std::endl; + + float aspect_ratio = w.actualSize().height()/(float)w.actualSize().width(); + + float cell_width = mZoom*COLUMN_SIZE_FONT_FACTOR_W*fm.height(); + float cell_height = mZoom*COLUMN_SIZE_FONT_FACTOR_W*fm.height()*aspect_ratio; + + if(mUseGrid || index.column()==0) + return QSize(cell_width,cell_height); + else + return QSize(option.rect.width()-cell_width,cell_height); +} + +void ChannelPostDelegate::setWidgetGrid(bool use_grid) +{ + mUseGrid = use_grid; +} + +//===============================================================================================================================================// +//=== ChannelPostFilesDelegate ===// +//===============================================================================================================================================// + +QWidget *ChannelPostFilesDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex& index) const +{ + ChannelPostFileInfo file = index.data(Qt::UserRole).value() ; + + if(index.column() == RsGxsChannelPostFilesModel::COLUMN_FILES_FILE) + return new GxsChannelFilesStatusWidget(file,parent); + else + return NULL; +} +void ChannelPostFilesDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const +{ + editor->setGeometry(option.rect); +} + +void ChannelPostFilesDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const +{ + ChannelPostFileInfo file = index.data(Qt::UserRole).value() ; + + // prepare + painter->save(); + painter->setClipRect(option.rect); + + painter->save(); + + painter->fillRect( option.rect, option.backgroundBrush); + //optionFocusRect.backgroundColor = option.palette.color(colorgroup, (option.state & QStyle::State_Selected) ? QPalette::Highlight : QPalette::Background); + painter->restore(); + + switch(index.column()) + { + case RsGxsChannelPostFilesModel::COLUMN_FILES_NAME: painter->drawText(option.rect,Qt::AlignLeft | Qt::AlignVCenter," " + QString::fromUtf8(file.mName.c_str())); + break; + case RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE: painter->drawText(option.rect,Qt::AlignRight | Qt::AlignVCenter,misc::friendlyUnit(qulonglong(file.mSize))); + break; + case RsGxsChannelPostFilesModel::COLUMN_FILES_DATE: painter->drawText(option.rect,Qt::AlignLeft | Qt::AlignVCenter,QDateTime::fromMSecsSinceEpoch(file.mPublishTime*1000).toString("MM/dd/yyyy, hh:mm")); + break; + case RsGxsChannelPostFilesModel::COLUMN_FILES_FILE: { + + GxsChannelFilesStatusWidget w(file); + + w.setFixedWidth(option.rect.width()); + w.setFixedHeight(option.rect.height()); + + QPixmap pixmap(w.size()); + + // apparently we need to do the alternate colors manually + //if(index.row() & 0x01) + // pixmap.fill(option.palette.alternateBase().color()); + //else + pixmap.fill(option.palette.base().color()); + + w.render(&pixmap,QPoint(),QRegion(),QWidget::DrawChildren );// draw the widgets, not the background + + painter->drawPixmap(option.rect.topLeft(),pixmap); + } + break; + + default: + painter->drawText(option.rect,Qt::AlignLeft,QString("[No data]")); + break; + } +} + +QSize ChannelPostFilesDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + ChannelPostFileInfo file = index.data(Qt::UserRole).value() ; + + QFontMetricsF fm(option.font); + + switch(index.column()) + { + case RsGxsChannelPostFilesModel::COLUMN_FILES_NAME: return QSize(1.1*fm.width(QString::fromUtf8(file.mName.c_str())),fm.height()); + case RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE: return QSize(1.1*fm.width(misc::friendlyUnit(qulonglong(file.mSize))),fm.height()); + case RsGxsChannelPostFilesModel::COLUMN_FILES_DATE: return QSize(1.1*fm.width(QDateTime::fromMSecsSinceEpoch(file.mPublishTime*1000).toString("MM/dd/yyyy, hh:mm")),fm.height()); + default: + case RsGxsChannelPostFilesModel::COLUMN_FILES_FILE: return QSize(option.rect.width(),GxsChannelFilesStatusWidget(file).height()); + } +} + +//===============================================================================================================================================// +//=== GxsChannelPostWidgetWithModel ===// +//===============================================================================================================================================// + +/** Constructor */ +GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupId &channelId, QWidget *parent) : + GxsMessageFrameWidget(rsGxsChannels, parent), + ui(new Ui::GxsChannelPostsWidgetWithModel) +{ + /* Invoke the Qt Designer generated object setup routine */ + ui->setupUi(this); + + ui->viewType_TB->setIcon(FilesDefs::getIconFromQtResourcePath(":icons/svg/gridlayout.svg")); + ui->viewType_TB->setToolTip(tr("Click to switch to list view")); + connect(ui->viewType_TB,SIGNAL(clicked()),this,SLOT(switchView())); + + ui->showUnread_TB->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/message-state-unread.png")); + ui->showUnread_TB->setChecked(false); + ui->showUnread_TB->setToolTip(tr("Show unread posts only")); + connect(ui->showUnread_TB,SIGNAL(toggled(bool)),this,SLOT(switchOnlyUnread(bool))); + + ui->postsTree->setAlternatingRowColors(false); + ui->postsTree->setModel(mChannelPostsModel = new RsGxsChannelPostsModel()); + ui->postsTree->setItemDelegate(mChannelPostsDelegate = new ChannelPostDelegate()); + ui->postsTree->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // prevents bug on w10, since row size depends on widget width + ui->postsTree->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);// more beautiful if we scroll at pixel level + ui->postsTree->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + + mChannelPostsDelegate->setAspectRatio(ChannelPostThumbnailView::ASPECT_RATIO_16_9); + + connect(ui->postsTree,SIGNAL(zoomRequested(bool)),this,SLOT(updateZoomFactor(bool))); + connect(ui->commentsDialog,SIGNAL(commentsLoaded(int)),this,SLOT(updateCommentsCount(int))); + + ui->channelPostFiles_TV->setModel(mChannelPostFilesModel = new RsGxsChannelPostFilesModel(this)); + ui->channelPostFiles_TV->setItemDelegate(new ChannelPostFilesDelegate()); + ui->channelPostFiles_TV->setPlaceholderText(tr("No files in this post, or no post selected")); + ui->channelPostFiles_TV->setSortingEnabled(true); + ui->channelPostFiles_TV->sortByColumn(0, Qt::AscendingOrder); + ui->channelPostFiles_TV->setAlternatingRowColors(false); + + ui->channelFiles_TV->setModel(mChannelFilesModel = new RsGxsChannelPostFilesModel()); + ui->channelFiles_TV->setItemDelegate(mFilesDelegate = new ChannelPostFilesDelegate()); + ui->channelFiles_TV->setPlaceholderText(tr("No files in the channel, or no channel selected")); + ui->channelFiles_TV->setSortingEnabled(true); + ui->channelFiles_TV->sortByColumn(0, Qt::AscendingOrder); + + connect(ui->channelPostFiles_TV->header(),SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(sortColumnPostFiles(int,Qt::SortOrder))); + connect(ui->channelFiles_TV->header(),SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(sortColumnFiles(int,Qt::SortOrder))); + + connect(ui->postsTree->selectionModel(),SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)),this,SLOT(showPostDetails())); + connect(ui->postsTree,SIGNAL(customContextMenuRequested(const QPoint&)),this,SLOT(postContextMenu(const QPoint&))); + + connect(mChannelPostsModel,SIGNAL(channelPostsLoaded()),this,SLOT(postChannelPostLoad())); + + ui->postName_LB->hide(); + ui->postTime_LB->hide(); + ui->postLogo_LB->hide(); + + ui->postDetails_TE->setPlaceholderText(tr("No text to display")); + + // Set initial size of the splitter + ui->splitter->setStretchFactor(0, 1); + ui->splitter->setStretchFactor(1, 0); + + QFontMetricsF fm(font()); + + if(mChannelPostsModel->getMode() == RsGxsChannelPostsModel::TREE_MODE_GRID) + for(int i=0;icolumnCount();++i) + ui->postsTree->setColumnWidth(i,mChannelPostsDelegate->cellSize(i,font(),ui->postsTree->width())); + + /* Setup UI helper */ + + /* Connect signals */ + connect(ui->postButton, SIGNAL(clicked()), this, SLOT(createMsg())); + connect(ui->subscribeToolButton, SIGNAL(subscribe(bool)), this, SLOT(subscribeGroup(bool))); + connect(NotifyQt::getInstance(), SIGNAL(settingsChanged()), this, SLOT(settingsChanged())); + + ui->postButton->setText(tr("Add new post")); + + /* add filter actions */ + ui->filterLineEdit->setPlaceholderText(tr("Search...")); + connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString))); + + ui->postsTree->setPlaceholderText(tr("Thumbnails")); + ui->postsTree->setMinimumWidth(COLUMN_SIZE_FONT_FACTOR_W*QFontMetricsF(font()).height()+1); + + connect(ui->postsTree,SIGNAL(sizeChanged(QSize)),this,SLOT(handlePostsTreeSizeChange(QSize))); + + /* Set initial section sizes */ + QHeaderView * channelpostfilesheader = ui->channelPostFiles_TV->header () ; + QHeaderView * channelfilesheader = ui->channelFiles_TV->header () ; + + channelpostfilesheader->resizeSection (RsGxsChannelPostFilesModel::COLUMN_FILES_NAME, fm.width("RetroShare-v0.6.5-1487-g6714648e5-Windows-x64-portable-20200518-Qt-5.14.2.7z")); + channelfilesheader->resizeSection (RsGxsChannelPostFilesModel::COLUMN_FILES_NAME, fm.width("RetroShare-v0.6.5-1487-g6714648e5-Windows-x64-portable-20200518-Qt-5.14.2.7z")); + + /* Initialize feed widget */ + //ui->feedWidget->setSortRole(ROLE_PUBLISH, Qt::DescendingOrder); + //ui->feedWidget->setFilterCallback(filterItem); + + /* load settings */ + processSettings(true); + + ui->channelPostFiles_TV->setColumnHidden(RsGxsChannelPostFilesModel::COLUMN_FILES_DATE, true); // no need to show this here. + + /* Initialize subscribe button */ + QIcon icon; + icon.addPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/redled.png"), QIcon::Normal, QIcon::On); + icon.addPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/start.png"), QIcon::Normal, QIcon::Off); + mAutoDownloadAction = new QAction(icon, "", this); + mAutoDownloadAction->setCheckable(true); + connect(mAutoDownloadAction, SIGNAL(triggered()), this, SLOT(toggleAutoDownload())); + + ui->subscribeToolButton->addSubscribedAction(mAutoDownloadAction); + + ui->commentsDialog->setTokenService(rsGxsChannels->getTokenService(),rsGxsChannels); + + /* Initialize GUI */ + setAutoDownload(false); + settingsChanged(); + + setGroupId(channelId); + mChannelPostsModel->updateChannel(channelId); + + mEventHandlerId = 0; + // Needs to be asynced because this function is called by another thread! + rsEvents->registerEventsHandler( [this](std::shared_ptr event) + { + RsQThreadUtils::postToObject([=](){ handleEvent_main_thread(event); }, this ); + }, mEventHandlerId, RsEventType::GXS_CHANNELS ); +} + +void GxsChannelPostsWidgetWithModel::updateZoomFactor(bool zoom_or_unzoom) +{ + mChannelPostsDelegate->zoom(zoom_or_unzoom); + + for(int i=0;icolumnCount();++i) + ui->postsTree->setColumnWidth(i,mChannelPostsDelegate->cellSize(i,font(),ui->postsTree->width())); + + QSize s = ui->postsTree->size(); + + int n_columns = std::max(1,(int)floor(s.width() / (mChannelPostsDelegate->cellSize(0,font(),s.width())))); + + mChannelPostsModel->setNumColumns(n_columns); // forces the update + + ui->postsTree->dataChanged(QModelIndex(),QModelIndex()); +} + +void GxsChannelPostsWidgetWithModel::sortColumnPostFiles(int col,Qt::SortOrder so) +{ + std::cerr << "Sorting post files according to col " << col << std::endl; + mChannelPostFilesModel->sort(col,so); +} +void GxsChannelPostsWidgetWithModel::sortColumnFiles(int col,Qt::SortOrder so) +{ + std::cerr << "Sorting channel files according to col " << col << std::endl; + mChannelFilesModel->sort(col,so); +} + +void GxsChannelPostsWidgetWithModel::postContextMenu(const QPoint&) +{ + QMenu menu(this); + +#ifdef TO_REMOVE + if(mChannelPostsModel->getMode() == RsGxsChannelPostsModel::TREE_MODE_GRID) + menu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_GRID_VIEW), tr("Switch to list view"), this, SLOT(switchView())); + else + menu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_GRID_VIEW), tr("Switch to grid view"), this, SLOT(switchView())); + + menu.addSeparator(); +#endif + + QModelIndex index = ui->postsTree->selectionModel()->currentIndex(); + + if(index.isValid()) + { + RsGxsChannelPost post = index.data(Qt::UserRole).value() ; + + if(!post.mFiles.empty()) + menu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_DOWNLOAD), tr("Download files"), this, SLOT(download())); + + if(!IS_MSG_UNREAD(post.mMeta.mMsgStatus) && !IS_MSG_NEW(post.mMeta.mMsgStatus)) + menu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_UNREAD), tr("Mark as unread"), this, SLOT(markMessageUnread())); + } + menu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COPYLINK), tr("Copy RetroShare Link"), this, SLOT(copyMessageLink())); + + if(IS_GROUP_PUBLISHER(mGroup.mMeta.mSubscribeFlags)) + menu.addAction(FilesDefs::getIconFromQtResourcePath(":/icons/png/pencil-edit-button.png"), tr("Edit"), this, SLOT(editPost())); + + menu.exec(QCursor::pos()); +} + +void GxsChannelPostsWidgetWithModel::markMessageUnread() +{ + QModelIndex index = ui->postsTree->selectionModel()->currentIndex(); + + mChannelPostsModel->setMsgReadStatus(index,false); +} + +RsGxsMessageId GxsChannelPostsWidgetWithModel::getCurrentItemId() const +{ + RsGxsMessageId selected_msg_id ; + QModelIndex index = ui->postsTree->selectionModel()->currentIndex(); + + if(index.isValid()) + selected_msg_id = index.data(Qt::UserRole).value().mMeta.mMsgId ; + + return selected_msg_id; +} + +void GxsChannelPostsWidgetWithModel::selectItem(const RsGxsMessageId& msg_id) +{ + auto index = mChannelPostsModel->getIndexOfMessage(msg_id); + + ui->postsTree->selectionModel()->setCurrentIndex(index,QItemSelectionModel::ClearAndSelect); + ui->postsTree->scrollTo(index);//May change if model reloaded +} + +void GxsChannelPostsWidgetWithModel::switchView() +{ + auto msg_id = getCurrentItemId(); + + if(mChannelPostsModel->getMode() == RsGxsChannelPostsModel::TREE_MODE_GRID) + { + ui->viewType_TB->setIcon(FilesDefs::getIconFromQtResourcePath(":icons/svg/listlayout.svg")); + ui->viewType_TB->setToolTip(tr("Click to switch to grid view")); + + ui->postsTree->setSelectionBehavior(QAbstractItemView::SelectRows); + + mChannelPostsDelegate->setWidgetGrid(false); + mChannelPostsModel->setMode(RsGxsChannelPostsModel::TREE_MODE_LIST); + } + else + { + ui->viewType_TB->setIcon(FilesDefs::getIconFromQtResourcePath(":icons/svg/gridlayout.svg")); + ui->viewType_TB->setToolTip(tr("Click to switch to list view")); + + ui->postsTree->setSelectionBehavior(QAbstractItemView::SelectItems); + + mChannelPostsDelegate->setWidgetGrid(true); + mChannelPostsModel->setMode(RsGxsChannelPostsModel::TREE_MODE_GRID); + + handlePostsTreeSizeChange(ui->postsTree->size(),true); + } + + for(int i=0;icolumnCount();++i) + ui->postsTree->setColumnWidth(i,mChannelPostsDelegate->cellSize(i,font(),ui->postsTree->width())); + + selectItem(msg_id); + ui->postsTree->setFocus(); + + mChannelPostsModel->triggerViewUpdate(); // This is already called by setMode(), but the model cannot know how many + // columns is actually has until we call handlePostsTreeSizeChange(), so + // we have to call it again here. +} + +void GxsChannelPostsWidgetWithModel::copyMessageLink() +{ + try + { + if (groupId().isNull()) + throw std::runtime_error("No channel currently selected!"); + + QModelIndex index = ui->postsTree->selectionModel()->currentIndex(); + + if(!index.isValid()) + throw std::runtime_error("No post under mouse!"); + + RsGxsChannelPost post = index.data(Qt::UserRole).value() ; + + if(post.mMeta.mMsgId.isNull()) + throw std::runtime_error("Post has empty MsgId!"); + + RetroShareLink link = RetroShareLink::createGxsMessageLink(RetroShareLink::TYPE_CHANNEL, groupId(), post.mMeta.mMsgId, QString::fromUtf8(post.mMeta.mMsgName.c_str())); + + if (!link.valid()) + throw std::runtime_error("Link is not valid"); + + QList urls; + urls.push_back(link); + RSLinkClipboard::copyLinks(urls); + } + catch(std::exception& e) + { + QMessageBox::critical(NULL,tr("Link creation error"),tr("Link could not be created: ")+e.what()); + } +} +void GxsChannelPostsWidgetWithModel::download() +{ + QModelIndex index = ui->postsTree->selectionModel()->currentIndex(); + RsGxsChannelPost post = index.data(Qt::UserRole).value() ; + + std::string destination; + rsGxsChannels->getChannelDownloadDirectory(mGroup.mMeta.mGroupId,destination); + + for(auto file:post.mFiles) + { + std::list sources; + std::string destination; + + // Add possible direct sources. + FileInfo fileInfo; + rsFiles->FileDetails(file.mHash, RS_FILE_HINTS_REMOTE, fileInfo); + + for(std::vector::const_iterator it = fileInfo.peers.begin(); it != fileInfo.peers.end(); ++it) { + sources.push_back((*it).peerId); + } + + rsFiles->FileRequest(file.mName, file.mHash, file.mSize, destination, RS_FILE_REQ_ANONYMOUS_ROUTING, sources); + } +} + +void GxsChannelPostsWidgetWithModel::editPost() +{ + QModelIndex index = ui->postsTree->selectionModel()->currentIndex(); + RsGxsChannelPost post = index.data(Qt::UserRole).value() ; + + CreateGxsChannelMsg *msgDialog = new CreateGxsChannelMsg(post.mMeta.mGroupId,post.mMeta.mMsgId); + msgDialog->show(); +} + +void GxsChannelPostsWidgetWithModel::handlePostsTreeSizeChange(QSize s,bool force) +{ + if(mChannelPostsModel->getMode() != RsGxsChannelPostsModel::TREE_MODE_GRID) + return; + + int n_columns = std::max(1,(int)floor(s.width() / (mChannelPostsDelegate->cellSize(0,font(),ui->postsTree->width())))); + std::cerr << "nb columns: " << n_columns << " current count=" << mChannelPostsModel->columnCount() << std::endl; + + if(force || (n_columns != mChannelPostsModel->columnCount())) + mChannelPostsModel->setNumColumns(n_columns); +} + +void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptr event) +{ + const RsGxsChannelEvent *e = dynamic_cast(event.get()); + + if(!e) + return; + + switch(e->mChannelEventCode) + { + case RsChannelEventCode::NEW_CHANNEL: // [[fallthrough]]; + case RsChannelEventCode::UPDATED_CHANNEL: // [[fallthrough]]; + case RsChannelEventCode::NEW_MESSAGE: // [[fallthrough]]; + case RsChannelEventCode::UPDATED_MESSAGE: + { + if(e->mChannelGroupId == groupId()) + updateDisplay(true); + } + + default: + break; + } +} + +void GxsChannelPostsWidgetWithModel::showPostDetails() +{ + QModelIndex index = ui->postsTree->selectionModel()->currentIndex(); + RsGxsChannelPost post = index.data(Qt::UserRole).value() ; + + QTextDocument doc; + doc.setHtml(post.mMsg.c_str()); + + if(post.mMeta.mPublishTs == 0) + { + ui->postDetails_TE->clear(); + ui->postLogo_LB->hide(); + ui->postName_LB->hide(); + ui->postTime_LB->hide(); + mChannelPostFilesModel->clear(); + ui->details_TW->setEnabled(false); + + return; + } + ui->details_TW->setEnabled(true); + + ui->postLogo_LB->show(); + ui->postName_LB->show(); + ui->postTime_LB->show(); + + if(index.row()==0 && index.column()==0) + std::cerr << "here" << std::endl; + + std::cerr << "showPostDetails: setting mSelectedPost to current post Id " << post.mMeta.mMsgId << ". Previous value: " << mSelectedPost << std::endl; + mSelectedPost = post.mMeta.mMsgId; + + std::list files; + for(auto& file:post.mFiles) + files.push_back(ChannelPostFileInfo(file,post.mMeta.mPublishTs)); + + mChannelPostFilesModel->setFiles(files); + + auto all_msgs_versions(post.mOlderVersions); + all_msgs_versions.insert(post.mMeta.mMsgId); + + ui->commentsDialog->commentLoad(post.mMeta.mGroupId, all_msgs_versions, post.mMeta.mMsgId); + + std::cerr << "Showing details about selected index : "<< index.row() << "," << index.column() << std::endl; + + ui->postDetails_TE->setText(RsHtml().formatText(NULL, QString::fromUtf8(post.mMsg.c_str()), RSHTML_FORMATTEXT_EMBED_SMILEYS | RSHTML_FORMATTEXT_EMBED_LINKS)); + + QPixmap postImage; + + if (post.mThumbnail.mData != NULL) + GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData, post.mThumbnail.mSize, postImage,GxsIdDetails::ORIGINAL); + else + postImage = FilesDefs::getPixmapFromQtResourcePath(ChannelPostThumbnailView::CHAN_DEFAULT_IMAGE); + + int W = QFontMetricsF(font()).height() * 8; + + // Using fixed width so that the post will not displace the text when we browse. + + ui->postLogo_LB->setPixmap(postImage); + ui->postLogo_LB->setFixedSize(W,postImage.height()/(float)postImage.width()*W); + + ui->postName_LB->setText(QString::fromUtf8(post.mMeta.mMsgName.c_str())); + + ui->postTime_LB->setText(QDateTime::fromMSecsSinceEpoch(post.mMeta.mPublishTs*1000).toString("MM/dd/yyyy, hh:mm")); + ui->postTime_LB->setFixedWidth(W); + + //ui->channelPostFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_FILE); + //ui->channelPostFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE); + ui->channelPostFiles_TV->setAutoSelect(true); + + // Now also set the post as read + + if(IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus)) + { + RsGxsGrpMsgIdPair postId; + postId.second = post.mMeta.mMsgId; + postId.first = post.mMeta.mGroupId; + + RsThread::async([postId]() { rsGxsChannels->markRead(postId, true) ; } ); + } +} + +void GxsChannelPostsWidgetWithModel::updateCommentsCount(int n) +{ + if(n > 0) + ui->details_TW->setTabText(2,tr("Comments (%1)").arg(n)); + else + ui->details_TW->setTabText(2,tr("Comments")); +} +void GxsChannelPostsWidgetWithModel::updateGroupData() +{ + if(groupId().isNull()) + { + // clear post, files and comment widgets + + showPostDetails(); + return; + } + + RsThread::async([this]() + { + RsGxsChannelGroup group; + std::vector groups; + + if(rsGxsChannels->getChannelsInfo(std::list{ groupId() }, groups) && groups.size()==1) + group = groups[0]; + else if(!rsGxsChannels->getDistantSearchResultGroupData(groupId(),group)) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to get group data for channel: " << groupId() << std::endl; + return; + } + + RsQThreadUtils::postToObject( [this,group]() + { + mGroup = group; + mChannelPostsModel->updateChannel(groupId()); + whileBlocking(ui->filterLineEdit)->clear(); + whileBlocking(ui->showUnread_TB)->setChecked(false); + + insertChannelDetails(mGroup); + + emit groupDataLoaded(); + emit groupChanged(this); // signals the parent widget to e.g. update the group tab name + } ); + }); +} + +void GxsChannelPostsWidgetWithModel::postChannelPostLoad() +{ + std::cerr << "Post channel load..." << std::endl; + + if(!mSelectedPost.isNull()) + { + QModelIndex index = mChannelPostsModel->getIndexOfMessage(mSelectedPost); + + std::cerr << "Setting current index to " << index.row() << ","<< index.column() << " for current post " + << mSelectedPost.toStdString() << std::endl; + + ui->postsTree->selectionModel()->setCurrentIndex(index,QItemSelectionModel::ClearAndSelect); + ui->postsTree->scrollTo(index);//May change if model reloaded + ui->postsTree->setFocus(); + } + else + std::cerr << "No pre-selected channel post." << std::endl; + + std::list files; + + mChannelPostsModel->getFilesList(files); + mChannelFilesModel->setFiles(files); + + ui->channelFiles_TV->setAutoSelect(true); + ui->channelFiles_TV->sortByColumn(0, Qt::AscendingOrder); + + ui->infoPosts->setText(QString::number(mChannelPostsModel->getNumberOfPosts()) + " / " + QString::number(mGroup.mMeta.mVisibleMsgCount)); + + // now compute aspect ratio for posts. We do that by looking at the 5 latest posts and compute the best aspect ratio for them. + + std::vector ar_votes(4,0); + + for(uint32_t i=0;igetNumberOfPosts(),5u);++i) + { + const RsGxsChannelPost& post = mChannelPostsModel->post(i); + ChannelPostThumbnailView v(post,ChannelPostThumbnailView::FLAG_SHOW_TEXT | ChannelPostThumbnailView::FLAG_SCALE_FONT); + + ++ar_votes[ static_cast( v.bestAspectRatio() )]; + } + int best=0; + for(uint32_t i=0;i<4;++i) + if(ar_votes[i] > ar_votes[best]) + best = i; + + mChannelPostsDelegate->setAspectRatio(static_cast(best)); + mChannelPostsModel->triggerViewUpdate(); + handlePostsTreeSizeChange(ui->postsTree->size(),true); // force the update +} + +void GxsChannelPostsWidgetWithModel::updateDisplay(bool complete) +{ +#ifdef DEBUG_CHANNEL + std::cerr << "udateDisplay: groupId()=" << groupId()<< std::endl; +#endif + if(groupId().isNull()) + { +#ifdef DEBUG_CHANNEL + std::cerr << " group_id=0. Return!"<< std::endl; +#endif + return; + } + + if(mGroup.mMeta.mGroupId.isNull() && !groupId().isNull()) + { +#ifdef DEBUG_FORUMS + std::cerr << " inconsistent group data. Reloading!"<< std::endl; +#endif + complete = true; + } + if(complete) // need to update the group data, reload the messages etc. + { +#warning todo + //saveExpandedItems(mSavedExpandedMessages); + + //if(mGroupId != mChannelPostsModel->currentGroupId()) + // mThreadId.clear(); + + updateGroupData(); + + return; + } +} +GxsChannelPostsWidgetWithModel::~GxsChannelPostsWidgetWithModel() +{ + rsEvents->unregisterEventsHandler(mEventHandlerId); + // save settings + processSettings(false); + + delete(mAutoDownloadAction); + delete mFilesDelegate; + delete ui; +} + +void GxsChannelPostsWidgetWithModel::processSettings(bool load) +{ + QHeaderView *channelpostfilesheader = ui->channelPostFiles_TV->header () ; + QHeaderView *channelfilesheader = ui->channelFiles_TV->header () ; + + Settings->beginGroup(QString("ChannelPostsWidget")); + + if (load) { + + // state of files tree + channelpostfilesheader->restoreState(Settings->value("PostFilesTree").toByteArray()); + channelfilesheader->restoreState(Settings->value("FilesTree").toByteArray()); + + // state of splitter + ui->splitter->restoreState(Settings->value("SplitterChannelPosts").toByteArray()); + } else { + // state of files tree + Settings->setValue("PostFilesTree", channelpostfilesheader->saveState()); + Settings->setValue("FilesTree", channelfilesheader->saveState()); + + // state of splitter + Settings->setValue("SplitterChannelPosts", ui->splitter->saveState()); + } + + Settings->endGroup(); +} + +void GxsChannelPostsWidgetWithModel::settingsChanged() +{ + mUseThread = Settings->getChannelLoadThread(); + + //mStateHelper->setWidgetVisible(ui->progressBar, mUseThread); +} + +QString GxsChannelPostsWidgetWithModel::groupName(bool) +{ + return QString::fromUtf8(mGroup.mMeta.mGroupName.c_str()); +} + +void GxsChannelPostsWidgetWithModel::groupNameChanged(const QString &name) +{ +// if (groupId().isNull()) { +// ui->nameLabel->setText(tr("No Channel Selected")); +// ui->logoLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/channels.png")); +// } else { +// ui->nameLabel->setText(name); +// } +} + +QIcon GxsChannelPostsWidgetWithModel::groupIcon() +{ + /* CHANNEL IMAGE */ + QPixmap chanImage; + if (mGroup.mImage.mData != NULL) { + GxsIdDetails::loadPixmapFromData(mGroup.mImage.mData, mGroup.mImage.mSize, chanImage,GxsIdDetails::ORIGINAL); + } else { + chanImage = FilesDefs::getPixmapFromQtResourcePath(ChannelPostThumbnailView::CHAN_DEFAULT_IMAGE); + } + + return QIcon(chanImage); +} + +/*************************************************************************************/ +/*************************************************************************************/ +/*************************************************************************************/ + +// Callback from Widget->FeedHolder->ServiceDialog->CommentContainer->CommentDialog, +void GxsChannelPostsWidgetWithModel::openComments(uint32_t /*type*/, const RsGxsGroupId &groupId, const QVector& msg_versions,const RsGxsMessageId &msgId, const QString &title) +{ + emit loadComment(groupId, msg_versions,msgId, title); +} + +void GxsChannelPostsWidgetWithModel::createMsg() +{ + if (groupId().isNull()) { + return; + } + + if (!IS_GROUP_SUBSCRIBED(mGroup.mMeta.mSubscribeFlags)) { + return; + } + + CreateGxsChannelMsg *msgDialog = new CreateGxsChannelMsg(groupId()); + msgDialog->show(); + + /* window will destroy itself! */ +} + +void GxsChannelPostsWidgetWithModel::insertChannelDetails(const RsGxsChannelGroup &group) +{ + // save selection if needed + + /* IMAGE */ + QPixmap chanImage; + if (group.mImage.mData != NULL) { + GxsIdDetails::loadPixmapFromData(group.mImage.mData, group.mImage.mSize, chanImage,GxsIdDetails::ORIGINAL); + } else { + chanImage = FilesDefs::getPixmapFromQtResourcePath(ChannelPostThumbnailView::CHAN_DEFAULT_IMAGE); + } + if(group.mMeta.mGroupName.empty()) + ui->channelName_LB->setText(tr("[No name]")); + else + ui->channelName_LB->setText(QString::fromUtf8(group.mMeta.mGroupName.c_str())); + + ui->logoLabel->setPixmap(chanImage); + ui->logoLabel->setFixedSize(QSize(ui->logoLabel->height()*chanImage.width()/(float)chanImage.height(),ui->logoLabel->height())); // make the logo have the same aspect ratio than the original image + + ui->postButton->setEnabled(bool(group.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_PUBLISH)); + + ui->subscribeToolButton->setSubscribed(IS_GROUP_SUBSCRIBED(group.mMeta.mSubscribeFlags)); + ui->subscribeToolButton->setEnabled(true); + + + bool autoDownload ; + rsGxsChannels->getChannelAutoDownload(group.mMeta.mGroupId,autoDownload); + setAutoDownload(autoDownload); + + if (IS_GROUP_SUBSCRIBED(group.mMeta.mSubscribeFlags)) + { + ui->subscribeToolButton->setText(tr("Subscribed") + " " + QString::number(group.mMeta.mPop) ); + //ui->feedToolButton->setEnabled(true); + //ui->fileToolButton->setEnabled(true); + ui->channel_TW->setTabEnabled(CHANNEL_TABS_POSTS,true); + ui->channel_TW->setTabEnabled(CHANNEL_TABS_FILES,true); + ui->details_TW->setEnabled(true); + } + else + { + ui->details_TW->setEnabled(false); + ui->channel_TW->setTabEnabled(CHANNEL_TABS_POSTS,false); + ui->channel_TW->setTabEnabled(CHANNEL_TABS_FILES,false); + } + + + ui->infoPosts->setText(QString::number(group.mMeta.mVisibleMsgCount)); + + if(group.mMeta.mLastPost==0) + ui->infoLastPost->setText(tr("Never")); + else + ui->infoLastPost->setText(DateTime::formatLongDateTime(group.mMeta.mLastPost)); + QString formatDescription = QString::fromUtf8(group.mDescription.c_str()); + + unsigned int formatFlag = RSHTML_FORMATTEXT_EMBED_LINKS; + + // embed smileys ? + if (Settings->valueFromGroup(QString("ChannelPostsWidget"), QString::fromUtf8("Emoteicons_ChannelDecription"), true).toBool()) { + formatFlag |= RSHTML_FORMATTEXT_EMBED_SMILEYS; + } + + formatDescription = RsHtml().formatText(NULL, formatDescription, formatFlag); + + ui->infoDescription->setText(formatDescription); + + ui->infoAdministrator->setId(group.mMeta.mAuthorId) ; + + if(!group.mMeta.mAuthorId.isNull()) + { + RetroShareLink link = RetroShareLink::createMessage(group.mMeta.mAuthorId, ""); + ui->infoAdministrator->setText(link.toHtml()); + } + + ui->infoCreated->setText(DateTime::formatLongDateTime(group.mMeta.mPublishTs)); + + QString distrib_string ( "[unknown]" ); + + switch(group.mMeta.mCircleType) + { + case GXS_CIRCLE_TYPE_PUBLIC: distrib_string = tr("Public") ; + break ; + case GXS_CIRCLE_TYPE_EXTERNAL: + { + RsGxsCircleDetails det ; + + // !! What we need here is some sort of CircleLabel, which loads the circle and updates the label when done. + + if(rsGxsCircles->getCircleDetails(group.mMeta.mCircleId,det)) + distrib_string = tr("Restricted to members of circle \"")+QString::fromUtf8(det.mCircleName.c_str()) +"\""; + else + distrib_string = tr("Restricted to members of circle ")+QString::fromStdString(group.mMeta.mCircleId.toStdString()) ; + } + break ; + case GXS_CIRCLE_TYPE_YOUR_EYES_ONLY: distrib_string = tr("Your eyes only"); + break ; + case GXS_CIRCLE_TYPE_LOCAL: distrib_string = tr("You and your friend nodes"); + break ; + default: + std::cerr << "(EE) badly initialised group distribution ID = " << group.mMeta.mCircleType << std::endl; + } + + ui->infoDistribution->setText(distrib_string); +#ifdef TODO + ui->infoWidget->show(); + ui->feedWidget->hide(); + ui->fileWidget->hide(); + + //ui->feedToolButton->setEnabled(false); + //ui->fileToolButton->setEnabled(false); +#endif + ui->subscribeToolButton->setText(tr("Subscribe ") + " " + QString::number(group.mMeta.mPop) ); + + showPostDetails(); +} + +void GxsChannelPostsWidgetWithModel::switchOnlyUnread(bool) +{ + filterChanged(ui->filterLineEdit->text()); + std::cerr << "Switched to unread/read"<< std::endl; +} +void GxsChannelPostsWidgetWithModel::filterChanged(QString s) +{ + QStringList ql = s.split(' ',QString::SkipEmptyParts); + uint32_t count; + mChannelPostsModel->setFilter(ql,ui->showUnread_TB->isChecked(),count); + mChannelFilesModel->setFilter(ql,count); +} + +void GxsChannelPostsWidgetWithModel::blank() +{ + ui->postButton->setEnabled(false); + ui->subscribeToolButton->setEnabled(false); + + ui->channelName_LB->setText(tr("No Channel Selected")); + ui->logoLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/channels.png")); + ui->infoPosts->setText(""); + ui->infoLastPost->setText(""); + ui->infoAdministrator->setText(""); + ui->infoDistribution->setText(""); + ui->infoCreated->setText(""); + ui->infoDescription->setText(""); + + mChannelPostsModel->clear(); + mChannelPostFilesModel->clear(); + ui->postDetails_TE->clear(); + ui->postLogo_LB->hide(); + ui->postName_LB->hide(); + ui->postTime_LB->hide(); + groupNameChanged(QString()); + +} + +bool GxsChannelPostsWidgetWithModel::navigate(const RsGxsMessageId& msgId) +{ + QModelIndex index = mChannelPostsModel->getIndexOfMessage(msgId); + + if(!index.isValid()) + { + std::cerr << "(EE) Cannot navigate to msg " << msgId << " in channel " << mGroup.mMeta.mGroupId << ": index unknown. Setting mNavigatePendingMsgId." << std::endl; + + mSelectedPost = msgId; // not found. That means the forum may not be loaded yet. So we keep that post in mind, for after loading. + return true; // we have to return true here, otherwise the caller will intepret the async loading as an error. + } + + ui->postsTree->selectionModel()->setCurrentIndex(index,QItemSelectionModel::ClearAndSelect); + ui->postsTree->scrollTo(index);//May change if model reloaded + ui->postsTree->setFocus(); + + return true; +} + +void GxsChannelPostsWidgetWithModel::subscribeGroup(bool subscribe) +{ + RsGxsGroupId grpId(groupId()); + if (grpId.isNull()) return; + + RsThread::async([=]() + { + rsGxsChannels->subscribeToChannel(grpId, subscribe); + } ); +} + +void GxsChannelPostsWidgetWithModel::setAutoDownload(bool autoDl) +{ + mAutoDownloadAction->setChecked(autoDl); + mAutoDownloadAction->setText(autoDl ? tr("Disable Auto-Download") : tr("Enable Auto-Download")); +} + +void GxsChannelPostsWidgetWithModel::toggleAutoDownload() +{ + RsGxsGroupId grpId = groupId(); + if (grpId.isNull()) { + return; + } + + bool autoDownload; + if(!rsGxsChannels->getChannelAutoDownload(grpId, autoDownload)) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to get autodownload value " + << "for channel: " << grpId.toStdString() << std::endl; + return; + } + + RsThread::async([this, grpId, autoDownload]() + { + if(!rsGxsChannels->setChannelAutoDownload(grpId, !autoDownload)) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to set autodownload " + << "for channel: " << grpId.toStdString() << std::endl; + return; + } + }); +} + +class GxsChannelPostsReadData +{ +public: + GxsChannelPostsReadData(bool read) + { + mRead = read; + mLastToken = 0; + } + +public: + bool mRead; + uint32_t mLastToken; +}; + +void GxsChannelPostsWidgetWithModel::setAllMessagesReadDo(bool read, uint32_t& /*token*/) +{ + if (groupId().isNull() || !IS_GROUP_SUBSCRIBED(mGroup.mMeta.mSubscribeFlags)) + return; + + QModelIndex src_index; + + mChannelPostsModel->setAllMsgReadStatus(read); +} + diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h new file mode 100644 index 000000000..36e3d7f5e --- /dev/null +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h @@ -0,0 +1,196 @@ +/******************************************************************************* + * retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.h * + * * + * Copyright 2013 by Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#ifndef _GXS_CHANNELPOSTSWIDGET_H +#define _GXS_CHANNELPOSTSWIDGET_H + +#include + +#include + +#include "gui/gxs/GxsMessageFramePostWidget.h" +#include "gui/feeds/FeedHolder.h" + +#include "GxsChannelPostThumbnail.h" + +namespace Ui { +class GxsChannelPostsWidgetWithModel; +} + +class GxsChannelPostItem; +class QTreeWidgetItem; +class QSortFilterProxyModel; +class FeedItem; +class RsGxsChannelPostsModel; +class RsGxsChannelPostFilesModel; +class RsGxsChannelPostFilesProxyModel; + +class ChannelPostFilesDelegate: public QStyledItemDelegate +{ + Q_OBJECT + + public: + ChannelPostFilesDelegate(QObject *parent=0) : QStyledItemDelegate(parent){} + virtual ~ChannelPostFilesDelegate(){} + + void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const override; + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; + + void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &) const override; + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; +}; + +class ChannelPostDelegate: public QAbstractItemDelegate +{ + Q_OBJECT + + public: + ChannelPostDelegate(QObject *parent=0) : QAbstractItemDelegate(parent), mZoom(1.0), mUseGrid(true){} + virtual ~ChannelPostDelegate(){} + + void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const override; + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; + + int cellSize(int col, const QFont& font, uint32_t parent_width) const; + void zoom(bool zoom_or_unzoom) ; + void setWidgetGrid(bool use_grid) ; + void setAspectRatio(ChannelPostThumbnailView::AspectRatio r) ; + + private: + static constexpr float IMAGE_MARGIN_FACTOR = 1.0; + static constexpr float IMAGE_SIZE_FACTOR_W = 4.0 ; + static constexpr float IMAGE_SIZE_FACTOR_H = 6.0 ; + static constexpr float IMAGE_ZOOM_FACTOR = 1.0; + + float mZoom; // zoom factor for the whole thumbnail + bool mUseGrid; // wether we use the grid widget or the list widget + ChannelPostThumbnailView::AspectRatio mAspectRatio; +}; + +class GxsChannelPostsWidgetWithModel: public GxsMessageFrameWidget +{ + Q_OBJECT + +public: + /* Filters */ + enum Filter { + FILTER_TITLE = 1, + FILTER_MSG = 2, + FILTER_FILE_NAME = 3 + }; + +public: + /** Default Constructor */ + GxsChannelPostsWidgetWithModel(const RsGxsGroupId &channelId, QWidget *parent = 0); + /** Default Destructor */ + ~GxsChannelPostsWidgetWithModel(); + + /* GxsMessageFrameWidget */ + virtual QIcon groupIcon(); + virtual void groupIdChanged() { updateDisplay(true); } + virtual QString groupName(bool) override; + virtual bool navigate(const RsGxsMessageId&) override; + + void updateDisplay(bool complete); + +#ifdef TODO + /* FeedHolder */ + virtual QScrollArea *getScrollArea(); + virtual void deleteFeedItem(FeedItem *feedItem, uint32_t type); + virtual void openChat(const RsPeerId& peerId); +#endif + virtual void openComments(uint32_t type, const RsGxsGroupId &groupId, const QVector &msg_versions, const RsGxsMessageId &msgId, const QString &title); + +protected: + /* GxsMessageFramePostWidget */ + virtual void groupNameChanged(const QString &name); + +#ifdef TODO + virtual bool insertGroupData(const RsGxsGenericGroupData *data) override; +#endif + virtual bool useThread() { return mUseThread; } + virtual void blank() ; + +#ifdef TODO + virtual bool getGroupData(RsGxsGenericGroupData *& data) override; + virtual void getMsgData(const std::set& msgIds,std::vector& posts) override; + virtual void getAllMsgData(std::vector& posts) override; + virtual void insertPosts(const std::vector& posts) override; + virtual void insertAllPosts(const std::vector& posts, GxsMessageFramePostThread *thread) override; +#endif + + /* GxsMessageFrameWidget */ + virtual void setAllMessagesReadDo(bool read, uint32_t &token); + +private slots: + void showPostDetails(); + void updateGroupData(); + void download(); + void createMsg(); + void toggleAutoDownload(); + void subscribeGroup(bool subscribe); + void filterChanged(QString); + void settingsChanged(); + void handlePostsTreeSizeChange(QSize s, bool force=false); + void postChannelPostLoad(); + void editPost(); + void postContextMenu(const QPoint&); + void copyMessageLink(); + void updateZoomFactor(bool zoom_or_unzoom); + void switchView(); + void switchOnlyUnread(bool b); + void markMessageUnread(); + +public slots: + void sortColumnFiles(int col,Qt::SortOrder so); + void sortColumnPostFiles(int col,Qt::SortOrder so); + void updateCommentsCount(int n); + +private: + void processSettings(bool load); + RsGxsMessageId getCurrentItemId() const; + void selectItem(const RsGxsMessageId& msg_id); + + void setAutoDownload(bool autoDl); + static bool filterItem(FeedItem *feedItem, const QString &text, int filter); + + void insertChannelDetails(const RsGxsChannelGroup &group); + void handleEvent_main_thread(std::shared_ptr event); + +private: + QAction *mAutoDownloadAction; + + RsGxsChannelGroup mGroup; + bool mUseThread; + RsEventsHandlerId_t mEventHandlerId ; + + RsGxsChannelPostsModel *mChannelPostsModel; + RsGxsChannelPostFilesModel *mChannelPostFilesModel; + RsGxsChannelPostFilesModel *mChannelFilesModel; + ChannelPostDelegate *mChannelPostsDelegate; + ChannelPostFilesDelegate *mFilesDelegate; + + RsGxsMessageId mSelectedPost; + + /* UI - from Designer */ + Ui::GxsChannelPostsWidgetWithModel *ui; +}; + +#endif diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui new file mode 100644 index 000000000..46683b31c --- /dev/null +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -0,0 +1,625 @@ + + + GxsChannelPostsWidgetWithModel + + + + 0 + 0 + 977 + 628 + + + + + 4 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::Box + + + QFrame::Sunken + + + + 4 + + + 2 + + + 6 + + + 2 + + + + + + 0 + 0 + + + + Subscribe + + + + 16 + 16 + + + + QToolButton::MenuButtonPopup + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + Qt::NoFocus + + + Post to Channel + + + Add new post + + + + :/icons/png/add.png:/icons/png/add.png + + + + 32 + 16 + + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + Qt::Horizontal + + + QSizePolicy::MinimumExpanding + + + + 0 + 0 + + + + + + + + ... + + + true + + + + + + + ... + + + false + + + + + + + 0 + + + + + + + + 0 + 0 + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + Search channels + + + true + + + + + + + + + + 1 + + + + Channel details + + + + + + + 75 + true + false + + + + Channel title + + + + + + + + + TextLabel + + + true + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + + unknown + + + true + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Last Post: + + + + + + + unknown + + + + + + + + 75 + true + + + + Administrator: + + + + + + + unknown + + + + + + + + 75 + true + + + + Distribution: + + + + + + + + 75 + true + + + + Created: + + + + + + + 0 + + + + + + + unknown + + + + + + + + 0 + 0 + + + + + 75 + true + + + + <html><head/><body><p align="justify">The number of posts at friends is progressively updated.</p><p align="justify">The new numbers will show next time you load this channel.</p><p align="justify">The two numbers differ depending on your friends synchronization</p><p>time limits as well as yours.</p></body></html> + + + Posts (locally / at friends): + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + <!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:'Ubuntu'; font-size:11pt; 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:'MS Shell Dlg 2'; font-size:8pt;">Description</span></p></body></html> + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + true + + + true + + + + + + + + Posts + + + + 0 + + + + + Qt::Vertical + + + + Qt::CustomContextMenu + + + <html><head/><body><p>Click to view post. </p><p>Use Ctrl+mouse wheel </p><p>to zoom/unzoom.</p></body></html> + + + QAbstractScrollArea::AdjustToContents + + + QAbstractItemView::SelectItems + + + Qt::ElideNone + + + 0 + + + false + + + false + + + true + + + true + + + false + + + + + + true + + + + 1 + + + + Details + + + + + + + + TextLabel + + + true + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + TextLabel + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + TextLabel + + + + + + + true + + + + + + + + + + Files + + + + + + QAbstractItemView::CurrentChanged|QAbstractItemView::SelectedClicked + + + true + + + 5 + + + false + + + + + + + + Comments + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + + All files + + + + + + QAbstractItemView::CurrentChanged|QAbstractItemView::SelectedClicked + + + false + + + true + + + false + + + + + + + + + + + Feeds + + + + + Files + + + + + + GxsIdLabel + QLabel +
gui/gxs/GxsIdLabel.h
+
+ + SubscribeToolButton + QToolButton +
gui/common/SubscribeToolButton.h
+
+ + RSTreeView + QTreeView +
gui/common/RSTreeView.h
+
+ + LineEditClear + QLineEdit +
gui/common/LineEditClear.h
+
+ + GxsCommentDialog + QWidget +
gui/gxs/GxsCommentDialog.h
+ 1 +
+ + StyledElidedLabel + QLabel +
gui/common/StyledElidedLabel.h
+
+
+ + + + +
diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelUserNotify.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelUserNotify.cpp index aa82953ed..ce88231fc 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelUserNotify.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelUserNotify.cpp @@ -18,11 +18,13 @@ * * *******************************************************************************/ +#include "retroshare/rsgxschannels.h" #include "GxsChannelUserNotify.h" #include "gui/MainWindow.h" +#include "gui/common/FilesDefs.h" -GxsChannelUserNotify::GxsChannelUserNotify(RsGxsIfaceHelper *ifaceImpl, QObject *parent) : - GxsUserNotify(ifaceImpl, parent) +GxsChannelUserNotify::GxsChannelUserNotify(RsGxsIfaceHelper *ifaceImpl, const GxsGroupFrameDialog *g, QObject *parent) : + GxsUserNotify(ifaceImpl, g, parent) { } @@ -36,12 +38,12 @@ bool GxsChannelUserNotify::hasSetting(QString *name, QString *group) QIcon GxsChannelUserNotify::getIcon() { - return QIcon(":/icons/png/channel.png"); + return FilesDefs::getIconFromQtResourcePath(":/icons/png/channel.png"); } QIcon GxsChannelUserNotify::getMainIcon(bool hasNew) { - return hasNew ? QIcon(":/icons/png/channels-notify.png") : QIcon(":/icons/png/channel.png"); + return hasNew ? FilesDefs::getIconFromQtResourcePath(":/icons/png/channels-notify.png") : FilesDefs::getIconFromQtResourcePath(":/icons/png/channel.png"); } void GxsChannelUserNotify::iconClicked() diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelUserNotify.h b/retroshare-gui/src/gui/gxschannels/GxsChannelUserNotify.h index d36b92bd7..8345b96d6 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelUserNotify.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelUserNotify.h @@ -22,15 +22,17 @@ #define GXSCHANNELUSERNOTIFY_H #include "gui/gxs/GxsUserNotify.h" +#include "gui/gxs/GxsGroupFrameDialog.h" class GxsChannelUserNotify : public GxsUserNotify { Q_OBJECT public: - GxsChannelUserNotify(RsGxsIfaceHelper *ifaceImpl, QObject *parent = 0); + GxsChannelUserNotify(RsGxsIfaceHelper *ifaceImpl, const GxsGroupFrameDialog *g, QObject *parent = 0); virtual bool hasSetting(QString *name, QString *group); + virtual QString textInfo() const override { return tr("new message(s)"); } private: virtual QIcon getIcon(); diff --git a/retroshare-gui/src/gui/gxsforums/CreateGxsForumMsg.cpp b/retroshare-gui/src/gui/gxsforums/CreateGxsForumMsg.cpp index 2ba83a31a..dd9a242b6 100644 --- a/retroshare-gui/src/gui/gxsforums/CreateGxsForumMsg.cpp +++ b/retroshare-gui/src/gui/gxsforums/CreateGxsForumMsg.cpp @@ -35,6 +35,7 @@ #include "gui/common/Emoticons.h" #include "gui/common/UIStateHelper.h" #include "gui/Identity/IdEditDialog.h" +#include "gui/common/FilesDefs.h" #include "util/HandleRichText.h" #include "util/misc.h" @@ -89,7 +90,7 @@ CreateGxsForumMsg::CreateGxsForumMsg(const RsGxsGroupId &fId, const RsGxsMessage ui.forumMessage->setPlaceholderText(tr ("Text")); - ui.headerFrame->setHeaderImage(QPixmap(":/icons/png/forums.png")); + ui.headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/forums.png")); ui.headerFrame->setHeaderText(text); ui.generateSpinBox->setEnabled(false); diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumGroupDialog.cpp b/retroshare-gui/src/gui/gxsforums/GxsForumGroupDialog.cpp index 20ab13c52..424091867 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumGroupDialog.cpp +++ b/retroshare-gui/src/gui/gxsforums/GxsForumGroupDialog.cpp @@ -19,6 +19,7 @@ *******************************************************************************/ #include "GxsForumGroupDialog.h" +#include "gui/common/FilesDefs.h" #include #include @@ -58,15 +59,15 @@ const uint32_t ForumCreateDefaultsFlags = ( GXS_GROUP_DEFAULTS_DISTRIB_PUBLIC const uint32_t ForumEditEnabledFlags = ForumCreateEnabledFlags; const uint32_t ForumEditDefaultsFlags = ForumCreateDefaultsFlags; -GxsForumGroupDialog::GxsForumGroupDialog(TokenQueue *tokenQueue, QWidget *parent) - : GxsGroupDialog(tokenQueue, ForumCreateEnabledFlags, ForumCreateDefaultsFlags, parent) +GxsForumGroupDialog::GxsForumGroupDialog(QWidget *parent) + : GxsGroupDialog(ForumCreateEnabledFlags, ForumCreateDefaultsFlags, parent) { ui.pubKeyShare_cb->setEnabled(true) ; ui.label_2->setToolTip(tr("

Put one of your identities here to allow others to send feedback and also have moderator rights on the forum. You may as well leave that field blank and keep the forum anonymously administrated.

")); } -GxsForumGroupDialog::GxsForumGroupDialog(TokenQueue *tokenExternalQueue, RsTokenService *tokenService, Mode mode, RsGxsGroupId groupId, QWidget *parent) - : GxsGroupDialog(tokenExternalQueue, tokenService, mode, groupId, ForumEditEnabledFlags, ForumEditDefaultsFlags, parent) +GxsForumGroupDialog::GxsForumGroupDialog(Mode mode, RsGxsGroupId groupId, QWidget *parent) + : GxsGroupDialog(mode, groupId, ForumEditEnabledFlags, ForumEditDefaultsFlags, parent) { ui.pubKeyShare_cb->setEnabled(true) ; } @@ -96,10 +97,10 @@ void GxsForumGroupDialog::initUi() QPixmap GxsForumGroupDialog::serviceImage() { - return QPixmap(":/icons/png/forums.png"); + return FilesDefs::getPixmapFromQtResourcePath(":/icons/png/forums.png"); } -bool GxsForumGroupDialog::service_CreateGroup(uint32_t &token, const RsGroupMetaData &meta) +bool GxsForumGroupDialog::service_createGroup(RsGroupMetaData& meta) { // Specific Function. RsGxsForumGroup grp; @@ -107,11 +108,16 @@ bool GxsForumGroupDialog::service_CreateGroup(uint32_t &token, const RsGroupMeta grp.mDescription = getDescription().toUtf8().constData(); getSelectedModerators(grp.mAdminList.ids); - rsGxsForums->createGroup(token, grp); - return true; + if(rsGxsForums->createForum(grp)) + { + meta = grp.mMeta; + return true; + } + else + return false; } -bool GxsForumGroupDialog::service_EditGroup(uint32_t &token, RsGroupMetaData &editedMeta) +bool GxsForumGroupDialog::service_updateGroup(const RsGroupMetaData& editedMeta) { RsGxsForumGroup grp(mGroupData); // start again from cached information. That allows to keep the pinned posts for instance. @@ -125,44 +131,45 @@ bool GxsForumGroupDialog::service_EditGroup(uint32_t &token, RsGroupMetaData &ed std::cerr << "GxsForumGroupDialog::service_EditGroup() submitting changes"; std::cerr << std::endl; - rsGxsForums->updateGroup(token, grp); - return true; + return rsGxsForums->editForum(grp); } -bool GxsForumGroupDialog::service_loadGroup(uint32_t token, Mode /*mode*/, RsGroupMetaData& groupMetaData, QString &description) +bool GxsForumGroupDialog::service_loadGroup(const RsGxsGenericGroupData *data, Mode /*mode*/, QString &description) { - std::cerr << "GxsForumGroupDialog::service_loadGroup(" << token << ")"; - std::cerr << std::endl; + const RsGxsForumGroup *pgroup = dynamic_cast(data); - std::vector groups; - - if (!rsGxsForums->getGroupData(token, groups)) + if (!pgroup) { - std::cerr << "GxsForumGroupDialog::service_loadGroup() Error getting GroupData"; - std::cerr << std::endl; + RsErr() << "GxsForumGroupDialog::service_loadGroup() supplied generic group is not a RsGxsForumGroup"<< std::endl; return false; } - if (groups.size() != 1) - { - std::cerr << "GxsForumGroupDialog::service_loadGroup() Error Group.size() != 1"; - std::cerr << std::endl; - return false; - } - - std::cerr << "GxsForumsGroupDialog::service_loadGroup() Unfinished Loading"; - std::cerr << std::endl; - // Information handled by GxsGroupDialog. description should rather be handled here in the service part! - groupMetaData = groups[0].mMeta; - description = QString::fromUtf8(groups[0].mDescription.c_str()); + description = QString::fromUtf8(pgroup->mDescription.c_str()); // Local information. Description should be handled here. - setSelectedModerators(groups[0].mAdminList.ids); + setSelectedModerators(pgroup->mAdminList.ids); - mGroupData = groups[0]; // keeps the private information + mGroupData = *pgroup; // keeps the private information return true; } + +bool GxsForumGroupDialog::service_getGroupData(const RsGxsGroupId& grpId,RsGxsGenericGroupData *& data) +{ + std::vector forumsInfo ; + + if( rsGxsForums->getForumsInfo(std::list({grpId}),forumsInfo) && forumsInfo.size() == 1) + { + data = new RsGxsForumGroup(forumsInfo[0]); + return true; + } + else + return false; + +} + + + diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumGroupDialog.h b/retroshare-gui/src/gui/gxsforums/GxsForumGroupDialog.h index 51ca29082..de24a9bd6 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumGroupDialog.h +++ b/retroshare-gui/src/gui/gxsforums/GxsForumGroupDialog.h @@ -29,15 +29,16 @@ class GxsForumGroupDialog : public GxsGroupDialog Q_OBJECT public: - GxsForumGroupDialog(TokenQueue *tokenQueue, QWidget *parent); - GxsForumGroupDialog(TokenQueue *tokenExternalQueue, RsTokenService *tokenService, Mode mode, RsGxsGroupId groupId, QWidget *parent); + GxsForumGroupDialog(QWidget *parent); + GxsForumGroupDialog(Mode mode, RsGxsGroupId groupId, QWidget *parent); protected: - virtual void initUi(); - virtual QPixmap serviceImage(); - virtual bool service_CreateGroup(uint32_t &token, const RsGroupMetaData &meta); - virtual bool service_loadGroup(uint32_t token, Mode mode, RsGroupMetaData& groupMetaData, QString &description); - virtual bool service_EditGroup(uint32_t &token, RsGroupMetaData &editedMeta); + void initUi() override; + QPixmap serviceImage() override; + bool service_createGroup(RsGroupMetaData& meta) override; + bool service_loadGroup(const RsGxsGenericGroupData *data,Mode mode, QString &description) override; + bool service_updateGroup(const RsGroupMetaData& editedMeta) override; + bool service_getGroupData(const RsGxsGroupId& grpId,RsGxsGenericGroupData *& data) override; private: RsGxsForumGroup mGroupData; diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumModel.cpp b/retroshare-gui/src/gui/gxsforums/GxsForumModel.cpp index 851f6fd16..4f91d2c02 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumModel.cpp +++ b/retroshare-gui/src/gui/gxsforums/GxsForumModel.cpp @@ -23,10 +23,12 @@ #include #include +#include "gui/common/FilesDefs.h" #include "util/qtthreadsutils.h" #include "util/HandleRichText.h" #include "util/DateTime.h" #include "gui/gxs/GxsIdDetails.h" +#include "gui/gxs/GxsIdTreeWidgetItem.h" #include "GxsForumModel.h" #include "retroshare/rsgxsflags.h" #include "retroshare/rsgxsforums.h" @@ -41,21 +43,21 @@ std::ostream& operator<<(std::ostream& o, const QModelIndex& i);// defined elsew const QString RsGxsForumModel::FilterString("filtered"); RsGxsForumModel::RsGxsForumModel(QObject *parent) - : QAbstractItemModel(parent) + : QAbstractItemModel(parent), mUseChildTS(false),mFilteringEnabled(false),mTreeMode(TREE_MODE_TREE) { initEmptyHierarchy(mPosts); - - mUseChildTS=false; - mFilteringEnabled=false; - mTreeMode = TREE_MODE_TREE; } void RsGxsForumModel::preMods() { - emit layoutAboutToBeChanged(); + //emit layoutAboutToBeChanged(); //Generate SIGSEGV when click on button move next/prev. + + beginResetModel(); } void RsGxsForumModel::postMods() { + endResetModel(); + if(mTreeMode == TREE_MODE_FLAT) emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mPosts.size(),COLUMN_THREAD_NB_COLUMNS-1,(void*)NULL)); else @@ -348,7 +350,6 @@ QVariant RsGxsForumModel::headerData(int section, Qt::Orientation /*orientation* case COLUMN_THREAD_TITLE: return tr("Title"); case COLUMN_THREAD_DATE: return tr("Date"); case COLUMN_THREAD_AUTHOR: return tr("Author"); - case COLUMN_THREAD_DISTRIBUTION: return tr("Distribution"); default: return QVariant(); } @@ -356,8 +357,8 @@ QVariant RsGxsForumModel::headerData(int section, Qt::Orientation /*orientation* if(role == Qt::DecorationRole) switch(section) { - case COLUMN_THREAD_DISTRIBUTION: return QIcon(":/icons/flag_green.png"); - case COLUMN_THREAD_READ: return QIcon(":/images/message-state-read.png"); + case COLUMN_THREAD_DISTRIBUTION: return FilesDefs::getIconFromQtResourcePath(":/icons/flag-green.png"); + case COLUMN_THREAD_READ: return FilesDefs::getIconFromQtResourcePath(":/images/message-state-read.png"); default: return QVariant(); } @@ -444,10 +445,13 @@ QVariant RsGxsForumModel::textColorRole(const ForumModelPostEntry& fmpe,int /*co if( (fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_MISSING)) return QVariant(mTextColorMissing); - if(IS_MSG_UNREAD(fmpe.mMsgStatus) || (fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_PINNED)) + if(IS_MSG_UNREAD(fmpe.mMsgStatus)) return QVariant(mTextColorUnread); else - return QVariant(mTextColorRead); + if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_PINNED) + return QVariant(mTextColorPinned); + else + return QVariant(mTextColorRead); return QVariant(); } @@ -592,10 +596,10 @@ QVariant RsGxsForumModel::pinnedRole(const ForumModelPostEntry& fmpe,int /*colum QVariant RsGxsForumModel::backgroundRole(const ForumModelPostEntry& fmpe,int /*column*/) const { if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_PINNED) - return QVariant(QBrush(QColor(255,200,180))); + return QVariant(QBrush(mBackgroundColorPinned)); if(mFilteringEnabled && (fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_PASSES_FILTER)) - return QVariant(QBrush(QColor(255,240,210))); + return QVariant(QBrush(mBackgroundColorFiltered)); return QVariant(); } @@ -668,9 +672,20 @@ QVariant RsGxsForumModel::displayRole(const ForumModelPostEntry& fmpe,int col) c return QVariant(DateTime::formatDateTime(qtime)); } - case COLUMN_THREAD_DISTRIBUTION: - case COLUMN_THREAD_AUTHOR: return QVariant(); - case COLUMN_THREAD_MSGID: return QVariant(); + case COLUMN_THREAD_DISTRIBUTION: // passthrough // handled by delegate. + case COLUMN_THREAD_MSGID: + return QVariant(); + case COLUMN_THREAD_AUTHOR: + { + QString name; + RsGxsId id = RsGxsId(fmpe.mAuthorId.toStdString()); + + if(id.isNull()) + return QVariant(tr("[Notification]")); + if(GxsIdTreeItemDelegate::computeName(id,name)) + return name; + return QVariant(tr("[Unknown]")); + } #ifdef TODO if (filterColumn == COLUMN_THREAD_CONTENT) { // need content for filter @@ -700,12 +715,17 @@ QVariant RsGxsForumModel::userRole(const ForumModelPostEntry& fmpe,int col) cons QVariant RsGxsForumModel::decorationRole(const ForumModelPostEntry& fmpe,int col) const { - if(col == COLUMN_THREAD_DISTRIBUTION) - return QVariant(fmpe.mReputationWarningLevel); - else if(col == COLUMN_THREAD_READ) - return QVariant(fmpe.mMsgStatus); - else - return QVariant(); + bool exist=false; + switch(col) + { + case COLUMN_THREAD_DISTRIBUTION: + return QVariant(fmpe.mReputationWarningLevel); + case COLUMN_THREAD_READ: + return QVariant(fmpe.mMsgStatus); + case COLUMN_THREAD_AUTHOR://Return icon as place holder. + return FilesDefs::getIconFromGxsIdCache(RsGxsId(fmpe.mAuthorId.toStdString()),QIcon(), exist); + } + return QVariant(); } const RsGxsGroupId& RsGxsForumModel::currentGroupId() const @@ -1210,7 +1230,7 @@ void RsGxsForumModel::setMsgReadStatus(const QModelIndex& i,bool read_status,boo if(!i.isValid()) return ; - preMods(); + // no need to call preMods()/postMods() here because we'renot changing the model void *ref = i.internalPointer(); uint32_t entry = 0; @@ -1218,38 +1238,45 @@ void RsGxsForumModel::setMsgReadStatus(const QModelIndex& i,bool read_status,boo if(!convertRefPointerToTabEntry(ref,entry) || entry >= mPosts.size()) return ; - bool has_unread_below,has_read_below; - recursSetMsgReadStatus(entry,read_status,with_children) ; + bool has_unread_below, has_read_below; + recursSetMsgReadStatus(entry,read_status,with_children) ; recursUpdateReadStatusAndTimes(0,has_unread_below,has_read_below); - postMods(); } void RsGxsForumModel::recursSetMsgReadStatus(ForumModelIndex i,bool read_status,bool with_children) { - if(read_status) - mPosts[i].mMsgStatus = 0; - else - mPosts[i].mMsgStatus = GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD; + int newStatus = (read_status ? mPosts[i].mMsgStatus & ~static_cast(GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD) + : mPosts[i].mMsgStatus | static_cast(GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD)); + bool bChanged = (mPosts[i].mMsgStatus != newStatus); + mPosts[i].mMsgStatus = newStatus; + //Remove Unprocessed and New flags + mPosts[i].mMsgStatus &= ~(GXS_SERV::GXS_MSG_STATUS_UNPROCESSED | GXS_SERV::GXS_MSG_STATUS_GUI_NEW); - uint32_t token; + if (bChanged) + { + //Don't recurs post versions as this should be done before, if no change. + uint32_t token; + auto s = getPostVersions(mPosts[i].mMsgId) ; - auto s = getPostVersions(mPosts[i].mMsgId) ; + if(!s.empty()) + for(auto it(s.begin());it!=s.end();++it) + { + rsGxsForums->setMessageReadStatus(token,std::make_pair( mForumGroup.mMeta.mGroupId, it->second ), read_status); + std::cerr << "Setting version " << it->second << " of post " << mPosts[i].mMsgId << " as read." << std::endl; + } + else + rsGxsForums->setMessageReadStatus(token,std::make_pair( mForumGroup.mMeta.mGroupId, mPosts[i].mMsgId ), read_status); - if(!s.empty()) - for(auto it(s.begin());it!=s.end();++it) - { - rsGxsForums->setMessageReadStatus(token,std::make_pair( mForumGroup.mMeta.mGroupId, it->second ), read_status); - std::cerr << "Setting version " << it->second << " of post " << mPosts[i].mMsgId << " as read." << std::endl; - } - else - rsGxsForums->setMessageReadStatus(token,std::make_pair( mForumGroup.mMeta.mGroupId, mPosts[i].mMsgId ), read_status); + QModelIndex itemIndex = createIndex(i - 1, 0, &mPosts[i]); + emit dataChanged(itemIndex, itemIndex); + } - if(!with_children) - return; + if(!with_children) + return; - for(uint32_t j=0;jdrawPixmap(r.topLeft() + p, pix); } + + virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override + { + static auto img(FilesDefs::getPixmapFromQtResourcePath(IMAGE_WARNING_YELLOW)); + + return QSize(img.width()*1.2,option.rect.height()); + } }; class ReadStatusItemDelegate: public QStyledItemDelegate @@ -171,9 +179,9 @@ public: else { if (unread) - icon = QIcon(":/images/message-state-unread.png"); + icon = FilesDefs::getIconFromQtResourcePath(":/images/message-state-unread.png"); else - icon = QIcon(":/images/message-state-read.png"); + icon = FilesDefs::getIconFromQtResourcePath(":/images/message-state-read.png"); } QPixmap pix = icon.pixmap(r.size()); @@ -182,94 +190,12 @@ public: const QPoint p = QPoint((r.width() - pix.width())/2, (r.height() - pix.height())/2); painter->drawPixmap(r.topLeft() + p, pix); } -}; -class AuthorItemDelegate: public QStyledItemDelegate -{ -public: - AuthorItemDelegate() {} - - QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override - { - QStyleOptionViewItemV4 opt = option; - initStyleOption(&opt, index); - - // disable default icon - opt.icon = QIcon(); - const QRect r = option.rect; - - RsGxsId id(index.data(Qt::UserRole).toString().toStdString()); - QString str; - QList icons; - QString comment; - - QFontMetricsF fm(option.font); - float f = fm.height(); - - QIcon icon ; - - if(!GxsIdDetails::MakeIdDesc(id, true, str, icons, comment,GxsIdDetails::ICON_TYPE_AVATAR)) - icon = GxsIdDetails::getLoadingIcon(id); - else - icon = *icons.begin(); - - QPixmap pix = icon.pixmap(r.size()); - - return QSize(1.2*(pix.width() + fm.width(str)),std::max(1.1*pix.height(),1.4*fm.height())); - } - - virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex& index) const override + virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override { - if(!index.isValid()) - { - std::cerr << "(EE) attempt to draw an invalid index." << std::endl; - return ; - } + static auto img(FilesDefs::getPixmapFromQtResourcePath(":/images/message-state-unread.png")); - QStyleOptionViewItemV4 opt = option; - initStyleOption(&opt, index); - - // disable default icon - opt.icon = QIcon(); - // draw default item - QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter, 0); - - const QRect r = option.rect; - - RsGxsId id(index.data(Qt::UserRole).toString().toStdString()); - QString str; - QList icons; - QString comment; - - QFontMetricsF fm(painter->font()); - float f = fm.height(); - - QIcon icon ; - - if(!GxsIdDetails::MakeIdDesc(id, true, str, icons, comment,GxsIdDetails::ICON_TYPE_AVATAR)) - icon = GxsIdDetails::getLoadingIcon(id); - else - icon = *icons.begin(); - - unsigned int warning_level = qvariant_cast(index.sibling(index.row(),RsGxsForumModel::COLUMN_THREAD_DISTRIBUTION).data(Qt::DecorationRole)); - - if(warning_level == 2) - { - str = tr("[Banned]"); - icon = QIcon(IMAGE_BIOHAZARD); - } - - if(index.data(RsGxsForumModel::MissingRole).toBool()) - painter->drawText(r.topLeft() + QPoint(f/2.0,f*1.0), tr("[None]")); - else - { - QPixmap pix = icon.pixmap(r.size()); - const QPoint p = QPoint(r.height()/2.0, (r.height() - pix.height())/2); - - // draw pixmap at center of item - painter->drawPixmap(r.topLeft() + p, pix); - painter->drawText(r.topLeft() + QPoint(r.height()+ f/2.0 + f/2.0,f*1.0), str); - } + return QSize(img.width()*1.2,option.rect.height()); } }; @@ -304,6 +230,11 @@ void GxsForumThreadWidget::setTextColorUnread (QColor color) { mTextColor void GxsForumThreadWidget::setTextColorUnreadChildren(QColor color) { mTextColorUnreadChildren = color; mThreadModel->setTextColorUnreadChildren(color);} void GxsForumThreadWidget::setTextColorNotSubscribed (QColor color) { mTextColorNotSubscribed = color; mThreadModel->setTextColorNotSubscribed (color);} void GxsForumThreadWidget::setTextColorMissing (QColor color) { mTextColorMissing = color; mThreadModel->setTextColorMissing (color);} +// Suppose to be different from unread one +void GxsForumThreadWidget::setTextColorPinned (QColor color) { mTextColorPinned = color; mThreadModel->setTextColorPinned (color);} + +void GxsForumThreadWidget::setBackgroundColorPinned (QColor color) { mBackgroundColorPinned = color; mThreadModel->setBackgroundColorPinned (color);} +void GxsForumThreadWidget::setBackgroundColorFiltered(QColor color) { mBackgroundColorFiltered = color; mThreadModel->setBackgroundColorFiltered (color);} GxsForumThreadWidget::GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget *parent) : GxsMessageFrameWidget(rsGxsForums, parent), @@ -311,7 +242,7 @@ GxsForumThreadWidget::GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget { ui->setupUi(this); - setUpdateWhenInvisible(true); + //setUpdateWhenInvisible(true); //mUpdating = false; mUnreadCount = 0; @@ -331,7 +262,7 @@ GxsForumThreadWidget::GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget ui->threadTreeWidget->setSortingEnabled(true); ui->threadTreeWidget->setItemDelegateForColumn(RsGxsForumModel::COLUMN_THREAD_DISTRIBUTION,new DistributionItemDelegate()) ; - ui->threadTreeWidget->setItemDelegateForColumn(RsGxsForumModel::COLUMN_THREAD_AUTHOR,new AuthorItemDelegate()) ; + ui->threadTreeWidget->setItemDelegateForColumn(RsGxsForumModel::COLUMN_THREAD_AUTHOR,new GxsIdTreeItemDelegate()) ; ui->threadTreeWidget->setItemDelegateForColumn(RsGxsForumModel::COLUMN_THREAD_READ,new ReadStatusItemDelegate()) ; ui->threadTreeWidget->header()->setSortIndicatorShown(true); @@ -380,9 +311,6 @@ GxsForumThreadWidget::GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget mLastViewType = -1; - // load settings - processSettings(true); - float f = QFontMetricsF(font()).height()/14.0f ; /* Set header resize modes and initial section sizes */ @@ -390,9 +318,10 @@ GxsForumThreadWidget::GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget QHeaderView * ttheader = ui->threadTreeWidget->header () ; ttheader->resizeSection (RsGxsForumModel::COLUMN_THREAD_DATE, 140*f); ttheader->resizeSection (RsGxsForumModel::COLUMN_THREAD_TITLE, 440*f); - ttheader->resizeSection (RsGxsForumModel::COLUMN_THREAD_DISTRIBUTION, 24*f); ttheader->resizeSection (RsGxsForumModel::COLUMN_THREAD_AUTHOR, 150*f); - ttheader->resizeSection (RsGxsForumModel::COLUMN_THREAD_READ, 24*f); + + ui->threadTreeWidget->resizeColumnToContents(RsGxsForumModel::COLUMN_THREAD_DISTRIBUTION); + ui->threadTreeWidget->resizeColumnToContents(RsGxsForumModel::COLUMN_THREAD_READ); QHeaderView_setSectionResizeModeColumn(ttheader, RsGxsForumModel::COLUMN_THREAD_TITLE, QHeaderView::Interactive); QHeaderView_setSectionResizeModeColumn(ttheader, RsGxsForumModel::COLUMN_THREAD_DATE, QHeaderView::Interactive); @@ -407,6 +336,9 @@ GxsForumThreadWidget::GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget ttheader->hideSection (RsGxsForumModel::COLUMN_THREAD_MSGID); ttheader->hideSection (RsGxsForumModel::COLUMN_THREAD_DATA); + ttheader->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ttheader, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(headerContextMenuRequested(QPoint))); + ui->progressBar->hide(); ui->progressText->hide(); @@ -416,15 +348,12 @@ GxsForumThreadWidget::GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget //ui->threadTreeWidget->installEventFilter(this) ; - ui->postText->clear() ; - ui->by_label->setId(RsGxsId()) ; - ui->time_label->clear(); - ui->lineRight->hide(); - ui->lineLeft->hide(); - ui->by_text_label->hide(); - ui->by_label->hide(); - ui->postText->setImageBlockWidget(ui->imageBlockWidget) ; - ui->postText->resetImagesStatus(Settings->getForumLoadEmbeddedImages()); + // load settings + processSettings(true); + + mDisplayBannedText = false; + + blankPost(); ui->subscribeToolButton->setToolTip(tr( "

Subscribing to the forum will gather \ available posts from your subscribed friends, and make the \ @@ -433,10 +362,12 @@ GxsForumThreadWidget::GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget ui->threadTreeWidget->enableColumnCustomize(true); #endif - mEventHandlerId = 0; - // Needs to be asynced because this function is likely to be called by another thread! - - rsEvents->registerEventsHandler(RsEventType::GXS_FORUMS, [this](std::shared_ptr event) { RsQThreadUtils::postToObject( [=]() { handleEvent_main_thread(event); }, this ); }, mEventHandlerId ); + mEventHandlerId = 0; + // Needs to be asynced because this function is called by another thread! + rsEvents->registerEventsHandler( + [this](std::shared_ptr event) + { RsQThreadUtils::postToObject([=](){ handleEvent_main_thread(event); }, this ); }, + mEventHandlerId, RsEventType::GXS_FORUMS ); } void GxsForumThreadWidget::handleEvent_main_thread(std::shared_ptr event) @@ -462,30 +393,17 @@ void GxsForumThreadWidget::handleEvent_main_thread(std::shared_ptrprogressBar->hide(); - ui->progressText->hide(); - ui->postText->clear() ; - ui->by_label->setId(RsGxsId()) ; - ui->time_label->clear(); - ui->lineRight->hide(); - ui->lineLeft->hide(); - ui->by_text_label->hide(); - ui->by_label->hide(); - ui->postText->setImageBlockWidget(ui->imageBlockWidget) ; - ui->postText->resetImagesStatus(Settings->getForumLoadEmbeddedImages()); -#ifdef SUSPENDED_CODE - ui->threadTreeWidget->clear(); -#endif + ui->subscribeToolButton->hide(); + ui->newthreadButton->hide(); ui->forumName->setText(""); + ui->progressText->hide(); + ui->progressBar->hide(); + ui->viewBox->setEnabled(false); + ui->filterLineEdit->setEnabled(false); - //mThreadModel->clear(); + mThreadModel->clear(); -#ifdef SUSPENDED_CODE - mStateHelper->setWidgetEnabled(ui->newthreadButton, false); - mStateHelper->setWidgetEnabled(ui->previousButton, false); - mStateHelper->setWidgetEnabled(ui->nextButton, false); -#endif - ui->versions_CB->hide(); + blankPost(); } GxsForumThreadWidget::~GxsForumThreadWidget() @@ -535,9 +453,14 @@ void GxsForumThreadWidget::processSettings(bool load) Settings->endGroup(); } -void GxsForumThreadWidget::changedSelection(const QModelIndex& current,const QModelIndex&) +void GxsForumThreadWidget::changedSelection(const QModelIndex& current,const QModelIndex& last) { - changedThread(current); + if (current!=last + && ( ( last.row()>=0 && last.column()>=0) //Double call when retrieve focus. + || mThreadId.isNull() //For first click + ) + ) + changedThread(current); } void GxsForumThreadWidget::groupIdChanged() @@ -564,61 +487,12 @@ QString GxsForumThreadWidget::groupName(bool withUnreadCount) QIcon GxsForumThreadWidget::groupIcon() { if (mNewCount) { - return QIcon(":/images/message-state-new.png"); + return FilesDefs::getIconFromQtResourcePath(":/images/message-state-new.png"); } return QIcon(); } -#ifdef TO_REMOVE -void GxsForumThreadWidget::changeEvent(QEvent *e) -{ - RsGxsUpdateBroadcastWidget::changeEvent(e); - switch (e->type()) { - case QEvent::StyleChange: - //calculateIconsAndFonts(); - break; - default: - // remove compiler warnings - break; - } -} - -static void removeMessages(std::map > &msgIds, QList &removeMsgId) -{ - QList removedMsgId; - - for (auto grpIt = msgIds.begin(); grpIt != msgIds.end(); ) - { - std::set &msgs = grpIt->second; - - QList::const_iterator removeMsgIt; - for (removeMsgIt = removeMsgId.begin(); removeMsgIt != removeMsgId.end(); ++removeMsgIt) { - if(msgs.find(*removeMsgIt) != msgs.end()) - { - removedMsgId.push_back(*removeMsgIt); - msgs.erase(*removeMsgIt); - } - } - - if (msgs.empty()) { - std::map >::iterator grpItErase = grpIt++; - msgIds.erase(grpItErase); - } else { - ++grpIt; - } - } - - if (!removedMsgId.isEmpty()) { - QList::const_iterator removedMsgIt; - for (removedMsgIt = removedMsgId.begin(); removedMsgIt != removedMsgId.end(); ++removedMsgIt) { - // remove first message id - removeMsgId.removeOne(*removedMsgIt); - } - } -} -#endif - void GxsForumThreadWidget::saveExpandedItems(QList& expanded_items) const { expanded_items.clear(); @@ -643,27 +517,18 @@ void GxsForumThreadWidget::recursSaveExpandedItems(const QModelIndex& index, QLi } } -void GxsForumThreadWidget::recursRestoreExpandedItems(const QModelIndex& index, const QList& expanded_items) +void GxsForumThreadWidget::recursRestoreExpandedItems(const QModelIndex& /*index*/, const QList& expanded_items) { for(auto it(expanded_items.begin());it!=expanded_items.end();++it) ui->threadTreeWidget->setExpanded( mThreadProxyModel->mapFromSource(mThreadModel->getIndexOfMessage(*it)) ,true) ; } + void GxsForumThreadWidget::updateDisplay(bool complete) { #ifdef DEBUG_FORUMS std::cerr << "udateDisplay: groupId()=" << groupId()<< std::endl; #endif -#ifdef TO_REMOVE - if(mUpdating) - { -#ifdef DEBUG_FORUMS - std::cerr << " Already updating. Return!"<< std::endl; -#endif - return; - } -#endif - if(groupId().isNull()) { #ifdef DEBUG_FORUMS @@ -679,53 +544,6 @@ void GxsForumThreadWidget::updateDisplay(bool complete) #endif complete = true; } - - if(!complete) - { -#ifdef DEBUG_FORUMS - std::cerr << " checking changed group data and msgs"<< std::endl; -#endif - - const std::set &grpIdsMeta = getGrpIdsMeta(); - - if(grpIdsMeta.find(groupId())!=grpIdsMeta.end()) - { -#ifdef DEBUG_FORUMS - std::cerr << " grpMeta change. reloading!" << std::endl; -#endif - complete = true; - } - - const std::set &grpIds = getGrpIds(); - - if (grpIds.find(groupId())!=grpIds.end()) - { -#ifdef DEBUG_FORUMS - std::cerr << " grp data change. reloading!" << std::endl; -#endif - complete = true; - } - else - { - // retrieve the list of modified msg ids - // if current group is listed in the map, reload the whole hierarchy - - std::map > msgIds; - getAllMsgIds(msgIds); - - // if (!mIgnoredMsgId.empty()) /* Filter ignored messages */ - // removeMessages(msgIds, mIgnoredMsgId); - - if (msgIds.find(groupId()) != msgIds.end()) - { -#ifdef DEBUG_FORUMS - std::cerr << " msg data change. reloading!" << std::endl; -#endif - complete=true; - } - } - } - if(complete) // need to update the group data, reload the messages etc. { saveExpandedItems(mSavedExpandedMessages); @@ -768,64 +586,77 @@ void GxsForumThreadWidget::threadListCustomPopupMenu(QPoint /*point*/) #ifdef DEBUG_FORUMS std::cerr << "Clicked on msg " << current_post.mMsgId << std::endl; #endif - QAction *editAct = new QAction(QIcon(IMAGE_MESSAGEEDIT), tr("Edit"), &contextMnu); + QAction *editAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MESSAGEEDIT), tr("Edit"), &contextMnu); connect(editAct, SIGNAL(triggered()), this, SLOT(editforummessage())); bool is_pinned = mForumGroup.mPinnedPosts.ids.find(mThreadId) != mForumGroup.mPinnedPosts.ids.end(); - QAction *pinUpPostAct = new QAction(QIcon(IMAGE_PINPOST), (is_pinned?tr("Un-pin this post"):tr("Pin this post up")), &contextMnu); + QAction *pinUpPostAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_PINPOST), (is_pinned?tr("Un-pin this post"):tr("Pin this post up")), &contextMnu); connect(pinUpPostAct , SIGNAL(triggered()), this, SLOT(togglePinUpPost())); - QAction *replyAct = new QAction(QIcon(IMAGE_MESSAGEREPLY), tr("Reply"), &contextMnu); + QAction *replyAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_REPLY), tr("Reply"), &contextMnu); connect(replyAct, SIGNAL(triggered()), this, SLOT(replytoforummessage())); - QAction *replyauthorAct = new QAction(QIcon(IMAGE_MESSAGEREPLY), tr("Reply to author with private message"), &contextMnu); + QAction *replyauthorAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MESSAGEREPLY), tr("Reply to author with private message"), &contextMnu); connect(replyauthorAct, SIGNAL(triggered()), this, SLOT(reply_with_private_message())); - QAction *flagaspositiveAct = new QAction(QIcon(IMAGE_POSITIVE_OPINION), tr("Give positive opinion"), &contextMnu); + QAction *flagaspositiveAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_POSITIVE_OPINION), tr("Give positive opinion"), &contextMnu); flagaspositiveAct->setToolTip(tr("This will block/hide messages from this person, and notify friend nodes.")) ; flagaspositiveAct->setData(static_cast(RsOpinion::POSITIVE)); connect(flagaspositiveAct, SIGNAL(triggered()), this, SLOT(flagperson())); - QAction *flagasneutralAct = new QAction(QIcon(IMAGE_NEUTRAL_OPINION), tr("Give neutral opinion"), &contextMnu); + QAction *flagasneutralAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_NEUTRAL_OPINION), tr("Give neutral opinion"), &contextMnu); flagasneutralAct->setToolTip(tr("Doing this, you trust your friends to decide to forward this message or not.")) ; flagasneutralAct->setData(static_cast(RsOpinion::NEUTRAL)); connect(flagasneutralAct, SIGNAL(triggered()), this, SLOT(flagperson())); - QAction *flagasnegativeAct = new QAction(QIcon(IMAGE_NEGATIVE_OPINION), tr("Give negative opinion"), &contextMnu); + QAction *flagasnegativeAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_NEGATIVE_OPINION), tr("Give negative opinion"), &contextMnu); flagasnegativeAct->setToolTip(tr("This will block/hide messages from this person, and notify friend nodes.")) ; flagasnegativeAct->setData(static_cast(RsOpinion::NEGATIVE)); connect(flagasnegativeAct, SIGNAL(triggered()), this, SLOT(flagperson())); - QAction *newthreadAct = new QAction(QIcon(IMAGE_MESSAGE), tr("Start New Thread"), &contextMnu); + QAction *newthreadAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MESSAGE), tr("Start New Thread"), &contextMnu); newthreadAct->setEnabled (IS_GROUP_SUBSCRIBED(mForumGroup.mMeta.mSubscribeFlags)); connect(newthreadAct , SIGNAL(triggered()), this, SLOT(createthread())); QAction* expandAll = new QAction(tr("Expand all"), &contextMnu); connect(expandAll, SIGNAL(triggered()), ui->threadTreeWidget, SLOT(expandAll())); +#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) + QAction* expandSubtree = new QAction(tr("Expand subtree"), &contextMnu); + connect(expandSubtree, SIGNAL(triggered()), this, SLOT(expandSubtree())); +#endif + QAction* collapseAll = new QAction(tr( "Collapse all"), &contextMnu); connect(collapseAll, SIGNAL(triggered()), ui->threadTreeWidget, SLOT(collapseAll())); - QAction *markMsgAsRead = new QAction(QIcon(":/images/message-mail-read.png"), tr("Mark as read"), &contextMnu); + QAction *markMsgAsRead = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/message-mail-read.png"), tr("Mark as read"), &contextMnu); connect(markMsgAsRead, SIGNAL(triggered()), this, SLOT(markMsgAsRead())); - QAction *markMsgAsReadChildren = new QAction(QIcon(":/images/message-mail-read.png"), tr("Mark as read") + " (" + tr ("with children") + ")", &contextMnu); + QAction *markMsgAsReadChildren = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/message-mail-read.png"), tr("Mark as read") + " (" + tr ("with children") + ")", &contextMnu); connect(markMsgAsReadChildren, SIGNAL(triggered()), this, SLOT(markMsgAsReadChildren())); - QAction *markMsgAsUnread = new QAction(QIcon(":/images/message-mail.png"), tr("Mark as unread"), &contextMnu); + QAction *markMsgAsUnread = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/message-mail.png"), tr("Mark as unread"), &contextMnu); connect(markMsgAsUnread, SIGNAL(triggered()), this, SLOT(markMsgAsUnread())); - QAction *markMsgAsUnreadChildren = new QAction(QIcon(":/images/message-mail.png"), tr("Mark as unread") + " (" + tr ("with children") + ")", &contextMnu); + QAction *markMsgAsUnreadChildren = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/message-mail.png"), tr("Mark as unread") + " (" + tr ("with children") + ")", &contextMnu); connect(markMsgAsUnreadChildren, SIGNAL(triggered()), this, SLOT(markMsgAsUnreadChildren())); - QAction *showinpeopleAct = new QAction(QIcon(":/images/info16.png"), tr("Show author in people tab"), &contextMnu); + QAction *showinpeopleAct = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/info16.png"), tr("Show author in people tab"), &contextMnu); connect(showinpeopleAct, SIGNAL(triggered()), this, SLOT(showInPeopleTab())); + bool has_children = false; + if (has_current_post) { + has_children = !current_post.mChildren.empty(); + } + if (IS_GROUP_SUBSCRIBED(mForumGroup.mMeta.mSubscribeFlags)) { markMsgAsReadChildren->setEnabled(current_post.mPostFlags & ForumModelPostEntry::FLAG_POST_HAS_UNREAD_CHILDREN); markMsgAsUnreadChildren->setEnabled(current_post.mPostFlags & ForumModelPostEntry::FLAG_POST_HAS_READ_CHILDREN); +#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) + expandSubtree->setEnabled(has_children); +#endif replyAct->setEnabled (true); replyauthorAct->setEnabled (true); } @@ -837,6 +668,22 @@ void GxsForumThreadWidget::threadListCustomPopupMenu(QPoint /*point*/) markMsgAsUnreadChildren->setDisabled(true); replyAct->setDisabled (true); replyauthorAct->setDisabled (true); +#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) + expandSubtree->setDisabled(true); + expandSubtree->setVisible(false); +#endif + } + + // disable visibility for childless + if (has_current_post) { + // still no setEnabled + markMsgAsRead->setVisible(IS_MSG_UNREAD(current_post.mMsgStatus)); + markMsgAsUnread->setVisible(!IS_MSG_UNREAD(current_post.mMsgStatus)); +#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) + expandSubtree->setVisible(has_children); +#endif + markMsgAsReadChildren->setVisible(has_children); + markMsgAsUnreadChildren->setVisible(has_children); } if(has_current_post) @@ -870,8 +717,8 @@ void GxsForumThreadWidget::threadListCustomPopupMenu(QPoint /*point*/) } contextMnu.addAction(replyAct); - contextMnu.addAction(newthreadAct); - QAction* action = contextMnu.addAction(QIcon(IMAGE_COPYLINK), tr("Copy RetroShare Link"), this, SLOT(copyMessageLink())); + contextMnu.addAction(newthreadAct); + QAction* action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COPYLINK), tr("Copy RetroShare Link"), this, SLOT(copyMessageLink())); action->setEnabled(!groupId().isNull() && !mThreadId.isNull()); contextMnu.addSeparator(); contextMnu.addAction(markMsgAsRead); @@ -880,6 +727,9 @@ void GxsForumThreadWidget::threadListCustomPopupMenu(QPoint /*point*/) contextMnu.addAction(markMsgAsUnreadChildren); contextMnu.addSeparator(); contextMnu.addAction(expandAll); +#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) + contextMnu.addAction(expandSubtree); +#endif contextMnu.addAction(collapseAll); if(has_current_post) @@ -931,6 +781,64 @@ void GxsForumThreadWidget::contextMenuTextBrowser(QPoint point) delete(contextMnu); } +void GxsForumThreadWidget::headerContextMenuRequested(const QPoint &pos) +{ + QMenu* header_context_menu = new QMenu(tr("Show column"), this); + + QAction* title = header_context_menu->addAction(QIcon(), tr("Title")); + title->setCheckable(true); + title->setChecked(!ui->threadTreeWidget->isColumnHidden(RsGxsForumModel::COLUMN_THREAD_TITLE)); + title->setData(RsGxsForumModel::COLUMN_THREAD_TITLE); + connect(title, SIGNAL(toggled(bool)), this, SLOT(changeHeaderColumnVisibility(bool))); + + QAction* read = header_context_menu->addAction(QIcon(), tr("Read")); + read->setCheckable(true); + read->setChecked(!ui->threadTreeWidget->isColumnHidden(RsGxsForumModel::COLUMN_THREAD_READ)); + read->setData(RsGxsForumModel::COLUMN_THREAD_READ); + connect(read, SIGNAL(toggled(bool)), this, SLOT(changeHeaderColumnVisibility(bool))); + + QAction* date = header_context_menu->addAction(QIcon(), tr("Date")); + date->setCheckable(true); + date->setChecked(!ui->threadTreeWidget->isColumnHidden(RsGxsForumModel::COLUMN_THREAD_DATE)); + date->setData(RsGxsForumModel::COLUMN_THREAD_DATE); + connect(date, SIGNAL(toggled(bool)), this, SLOT(changeHeaderColumnVisibility(bool))); + + QAction* distribution = header_context_menu->addAction(QIcon(), tr("Distribution")); + distribution->setCheckable(true); + distribution->setChecked(!ui->threadTreeWidget->isColumnHidden(RsGxsForumModel::COLUMN_THREAD_DISTRIBUTION)); + distribution->setData(RsGxsForumModel::COLUMN_THREAD_DISTRIBUTION); + connect(distribution, SIGNAL(toggled(bool)), this, SLOT(changeHeaderColumnVisibility(bool))); + + // QAction* author = header_context_menu->addAction(QIcon(), tr("Author")); + // author->setCheckable(true); + // author->setChecked(!ui->threadTreeWidget->isColumnHidden(RsGxsForumModel::COLUMN_THREAD_AUTHOR)); + // author->setData(RsGxsForumModel::COLUMN_THREAD_AUTHOR); + // connect(author, SIGNAL(toggled(bool)), this, SLOT(changeHeaderColumnVisibility(bool))); + + QAction* show_text_from_banned = header_context_menu->addAction(QIcon(), tr("Show text from banned persons")); + show_text_from_banned->setCheckable(true); + show_text_from_banned->setChecked(mDisplayBannedText); + connect(show_text_from_banned, SIGNAL(toggled(bool)), this, SLOT(showBannedText(bool))); + + header_context_menu->exec(mapToGlobal(pos)); + delete(header_context_menu); +} + +void GxsForumThreadWidget::changeHeaderColumnVisibility(bool visibility) { + QAction* the_action = qobject_cast(sender()); + if ( !the_action ) { + return; + } + ui->threadTreeWidget->setColumnHidden(the_action->data().toInt(), !visibility); +} + +void GxsForumThreadWidget::showBannedText(bool display) { + mDisplayBannedText = display; + if (!mThreadId.isNull()) { + updateMessageData(mThreadId); + } +} + #ifdef TODO bool GxsForumThreadWidget::eventFilter(QObject *obj, QEvent *event) { @@ -963,11 +871,11 @@ void GxsForumThreadWidget::togglethreadview_internal() { // if (ui->expandButton->isChecked()) { ui->postText->setVisible(true); - ui->expandButton->setIcon(QIcon(QString(":/images/edit_remove24.png"))); + ui->expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/images/edit_remove24.png"))); ui->expandButton->setToolTip(tr("Hide")); // } else { // ui->postText->setVisible(false); -// ui->expandButton->setIcon(QIcon(QString(":/images/edit_add24.png"))); +// ui->expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/images/edit_add24.png"))); // ui->expandButton->setToolTip(tr("Expand")); // } } @@ -985,66 +893,63 @@ void GxsForumThreadWidget::changedVersion() void GxsForumThreadWidget::changedThread(QModelIndex index) { - //if(mUpdating) - // return; + if(!index.isValid()) + return; - if(!index.isValid()) - return; + RsGxsMessageId new_id(index.sibling(index.row(),RsGxsForumModel::COLUMN_THREAD_MSGID).data(Qt::UserRole).toString().toStdString()); - RsGxsMessageId new_id(index.sibling(index.row(),RsGxsForumModel::COLUMN_THREAD_MSGID).data(Qt::UserRole).toString().toStdString()); - - if(new_id == mThreadId) - return; + if(new_id == mThreadId) + return; mThreadId = mOrigThreadId = new_id; #ifdef DEBUG_FORUMS - std::cerr << "Switched to new thread ID " << mThreadId << std::endl; + std::cerr << "Switched to new thread ID " << mThreadId << std::endl; #endif - //ui->postText->resetImagesStatus(Settings->getForumLoadEmbeddedImages()) ; insertMessage(); - QModelIndex src_index = mThreadProxyModel->mapToSource(index); + if(Settings->getForumMsgSetToReadOnActivate()) + { #ifdef DEBUG_FORUMS - std::cerr << "Setting message read status to true" << std::endl; + std::cerr << "Setting message read status to true" << std::endl; #endif - bool setToReadOnActive = Settings->getForumMsgSetToReadOnActivate(); - - if(setToReadOnActive) - mThreadModel->setMsgReadStatus(src_index, true,false); + markMsgAsReadUnread(true, false, false); + } } void GxsForumThreadWidget::clickedThread(QModelIndex index) { #ifdef DEBUG_FORUMS - std::cerr << "Clicked on message ID " << mThreadId << ", index=" << index << std::endl; + std::cerr << "Clicked on message ID " << mThreadId << ", index=" << index << std::endl; #endif - if(!index.isValid()) - { + if(!index.isValid()) + { #ifdef DEBUG_FORUMS std::cerr << " early return because index is invalid" << std::endl; #endif return; - } + } if (index.column() == RsGxsForumModel::COLUMN_THREAD_READ) - { - ForumModelPostEntry fmpe; - + { QModelIndex src_index = mThreadProxyModel->mapToSource(index); - mThreadModel->getPostData(src_index,fmpe); + ForumModelPostEntry fmpe; + mThreadModel->getPostData(src_index,fmpe); #ifdef DEBUG_FORUMS std::cerr << "Setting message read status to false" << std::endl; #endif - mThreadModel->setMsgReadStatus(src_index, IS_MSG_UNREAD(fmpe.mMsgStatus),false); + // First Load Message (may change read status) to not recall it after index change. + changedThread(index); + // Now index is invalid as model was reloaded, Selection isn't updated. + markMsgAsReadUnread(IS_MSG_UNREAD(static_cast(fmpe.mMsgStatus)), false, false, mThreadId); } #ifdef DEBUG_FORUMS - else - std::cerr << " doing nothing" << std::endl; + else + std::cerr << " doing nothing" << std::endl; #endif } @@ -1064,11 +969,56 @@ static QString getDurationString(uint32_t days) } } -void GxsForumThreadWidget::updateForumDescription() +void GxsForumThreadWidget::setForumDescriptionLoading() { + ui->postText->setText(tr("Loading...")); +} + +void GxsForumThreadWidget::clearForumDescription() +{ + ui->postText->clear(); +} + +void GxsForumThreadWidget::blankPost() +{ + ui->newmessageButton->setEnabled(false); + ui->previousButton->setEnabled(false); + ui->nextButton->setEnabled(false); + ui->nextUnreadButton->setEnabled(false); + ui->downloadButton->setEnabled(false); + ui->lineLeft->hide(); + ui->time_label->clear(); + ui->versions_CB->hide(); + ui->lineRight->hide(); + ui->by_text_label->hide(); + ui->by_label->setId(RsGxsId()) ; + ui->by_label->hide(); + ui->expandButton->hide(); + + ui->postText->clear() ; + ui->postText->setImageBlockWidget(ui->imageBlockWidget) ; + ui->postText->resetImagesStatus(Settings->getForumLoadEmbeddedImages()); + +} + +void GxsForumThreadWidget::updateForumDescription(bool success) +{ + if(!success) + { + blank(); + QString forum_description = QString("ERROR: Forum could not be loaded. Database might be in heavy use. Please try later."); + ui->postText->setText(forum_description); + mStateHelper->setWidgetEnabled(ui->newthreadButton, false); + return; + } + + std::cerr << "Updating forum description" << std::endl; if (!mThreadId.isNull()) return; + // still call it to not left leftovers from previous post if any + blankPost(); + RsIdentityDetails details; rsIdentity->getIdDetails(mForumGroup.mMeta.mAuthorId,details); @@ -1077,7 +1027,10 @@ void GxsForumThreadWidget::updateForumDescription() const RsGxsForumGroup& group = mForumGroup; - ui->forumName->setText(QString::fromUtf8(group.mMeta.mGroupName.c_str())); + ui->newthreadButton->show(); + ui->forumName->setText(QString::fromUtf8(group.mMeta.mGroupName.c_str())); + ui->viewBox->setEnabled(true); + ui->filterLineEdit->setEnabled(true); QString anti_spam_features1 ; QString forum_description; @@ -1190,6 +1143,10 @@ void GxsForumThreadWidget::insertMessage() return; } + /* blank text, incase we get nothing */ + blankPost(); + ui->nextUnreadButton->setEnabled(true); + // We use this instead of getCurrentIndex() because right here the currentIndex() is not set yet. QModelIndex index = mThreadProxyModel->mapFromSource(mThreadModel->getIndexOfMessage(mOrigThreadId)); @@ -1214,16 +1171,7 @@ void GxsForumThreadWidget::insertMessage() return; } - mStateHelper->setWidgetEnabled(ui->newmessageButton, (IS_GROUP_SUBSCRIBED(mForumGroup.mMeta.mSubscribeFlags) && mThreadId.isNull() == false)); - - /* blank text, incase we get nothing */ - ui->postText->clear(); - ui->by_label->setId(RsGxsId()) ; - ui->time_label->clear(); - ui->lineRight->hide(); - ui->lineLeft->hide(); - ui->by_text_label->hide(); - ui->by_label->hide(); + ui->newmessageButton->setEnabled(IS_GROUP_SUBSCRIBED(mForumGroup.mMeta.mSubscribeFlags) && mThreadId.isNull() == false); // add/show combobox for versions, if applicable, and enable it. If no older versions of the post available, hide the combobox. @@ -1277,6 +1225,19 @@ void GxsForumThreadWidget::insertMessage() // markMsgAsRead(); } +void GxsForumThreadWidget::setMessageLoadingError(const QString& error) +{ + ui->time_label->setText(QString("")); + ui->by_label->setId(RsGxsId()); + ui->lineRight->show(); + ui->lineLeft->show(); + ui->by_text_label->show(); + ui->by_label->show(); + ui->threadTreeWidget->setFocus(); + + ui->postText->setText(error); +} + void GxsForumThreadWidget::insertMessageData(const RsGxsForumMsg &msg) { /* As some time has elapsed since request - check that this is still the current msg. @@ -1300,54 +1261,60 @@ void GxsForumThreadWidget::insertMessageData(const RsGxsForumMsg &msg) rsReputations->overallReputationLevel(msg.mMeta.mAuthorId); bool redacted = (overall_reputation == RsReputationLevel::LOCALLY_NEGATIVE); - -#ifdef TO_REMOVE - bool setToReadOnActive = Settings->getForumMsgSetToReadOnActivate(); - uint32_t status = msg.mMeta.mMsgStatus ;//item->data(RsGxsForumModel::COLUMN_THREAD_DATA, ROLE_THREAD_STATUS).toUInt(); - QModelIndex index = getCurrentIndex(); - if (IS_MSG_NEW(status)) { - if (setToReadOnActive) { - /* set to read */ - mThreadModel->setMsgReadStatus(mThreadProxyModel->mapToSource(index),true,false); - } else { - /* set to unread by user */ - mThreadModel->setMsgReadStatus(mThreadProxyModel->mapToSource(index),false,false); - } - } else { - if (setToReadOnActive && IS_MSG_UNREAD(status)) { - /* set to read */ - mThreadModel->setMsgReadStatus(mThreadProxyModel->mapToSource(index), true,false); - } - } -#endif - - ui->time_label->setText(DateTime::formatLongDateTime(msg.mMeta.mPublishTs)); - ui->by_label->setId(msg.mMeta.mAuthorId); - ui->lineRight->show(); + // TODO enabled even when there are no new message + ui->nextUnreadButton->setEnabled(true); ui->lineLeft->show(); + ui->time_label->setText(DateTime::formatLongDateTime(msg.mMeta.mPublishTs)); + ui->lineRight->show(); ui->by_text_label->show(); + ui->by_label->setId(msg.mMeta.mAuthorId); ui->by_label->show(); ui->threadTreeWidget->setFocus(); - if(redacted) - { - QString extraTxt = tr( "

The author of this message (with ID %1) is banned.").arg(QString::fromStdString(msg.mMeta.mAuthorId.toStdString())) ; - extraTxt += tr( "

  • Messages from this author are not forwarded.
  • ") ; - extraTxt += tr( "
  • Messages from this author are replaced by this text.
") ; - extraTxt += tr( "

You can force the visibility and forwarding of messages by setting a different opinion for that Id in People's tab.

") ; + QString banned_text_info = ""; + if(redacted) { + ui->downloadButton->setDisabled(true); + if (!mDisplayBannedText) { + QString extraTxt = tr( "

The author of this message (with ID %1) is banned.").arg(QString::fromStdString(msg.mMeta.mAuthorId.toStdString())) ; + extraTxt += tr( "

  • Messages from this author are not forwarded.
  • ") ; + extraTxt += tr( "
  • Messages from this author are replaced by this text.
") ; + extraTxt += tr( "

You can force the visibility and forwarding of messages by setting a different opinion for that Id in People's tab.

") ; - ui->postText->setHtml(extraTxt) ; - } - else - { - uint32_t flags = RSHTML_FORMATTEXT_EMBED_LINKS; - if(Settings->getForumLoadEmoticons()) - flags |= RSHTML_FORMATTEXT_EMBED_SMILEYS ; + ui->postText->setHtml(extraTxt) ; + return; + } + else { + RsIdentityDetails details; + rsIdentity->getIdDetails(msg.mMeta.mAuthorId, details); + QString name = GxsIdDetails::getName(details); - QString extraTxt = RsHtml().formatText(ui->postText->document(), QString::fromUtf8(msg.mMsg.c_str()),flags); - ui->postText->setHtml(extraTxt); + banned_text_info += "

" + tr( "The author of this message (with ID %1) is banned. And named by name ( %2 )").arg(QString::fromStdString(msg.mMeta.mAuthorId.toStdString()), name) + ""; + banned_text_info += "

  • " + tr( "Messages from this author are not forwarded.") + "
"; + banned_text_info += "

" + tr( "You can force the visibility and forwarding of messages by setting a different opinion for that Id in People's tab.") + "


"; + } } + + uint32_t flags = RSHTML_FORMATTEXT_EMBED_LINKS; + if(Settings->getForumLoadEmoticons()) + flags |= RSHTML_FORMATTEXT_EMBED_SMILEYS ; + flags |= RSHTML_OPTIMIZEHTML_MASK; + + QColor backgroundColor = ui->postText->palette().base().color(); + qreal desiredContrast = Settings->valueFromGroup("Forum", + "MinimumContrast", 4.5).toDouble(); + int desiredMinimumFontSize = Settings->valueFromGroup("Forum", + "MinimumFontSize", 10).toInt(); + + QString extraTxt = banned_text_info + RsHtml().formatText(ui->postText->document(), + QString::fromUtf8(msg.mMsg.c_str()), flags + , backgroundColor, desiredContrast, desiredMinimumFontSize + ); + ui->postText->setHtml(extraTxt); + + QStringList urls; + RsHtml::findAnchors(ui->postText->toHtml(), urls); + ui->downloadButton->setEnabled(urls.count() > 0); } void GxsForumThreadWidget::previousMessage() @@ -1368,8 +1335,8 @@ void GxsForumThreadWidget::previousMessage() if (prevItem.isValid()) { ui->threadTreeWidget->setCurrentIndex(prevItem); + ui->threadTreeWidget->scrollTo(ui->threadTreeWidget->currentIndex());//May change if model reloaded ui->threadTreeWidget->setFocus(); - changedThread(prevItem); } } ui->previousButton->setEnabled(index-1 > 0); @@ -1395,8 +1362,8 @@ void GxsForumThreadWidget::nextMessage() if (nextItem.isValid()) { ui->threadTreeWidget->setCurrentIndex(nextItem); + ui->threadTreeWidget->scrollTo(ui->threadTreeWidget->currentIndex()); //May change if model reloaded ui->threadTreeWidget->setFocus(); - changedThread(nextItem); } } ui->previousButton->setEnabled(true); @@ -1440,29 +1407,31 @@ void GxsForumThreadWidget::nextUnreadMessage() } ui->threadTreeWidget->setCurrentIndex(index); - ui->threadTreeWidget->scrollTo(index); - changedThread(index); + ui->threadTreeWidget->scrollTo(ui->threadTreeWidget->currentIndex());//May change if model reloaded } -void GxsForumThreadWidget::markMsgAsReadUnread (bool read, bool children, bool forum) +void GxsForumThreadWidget::markMsgAsReadUnread (bool read, bool children, bool forum, RsGxsMessageId msgId /*= RsGxsMessageId()*/) { if (groupId().isNull() || !IS_GROUP_SUBSCRIBED(mForumGroup.mMeta.mSubscribeFlags)) { return; } + saveExpandedItems(mSavedExpandedMessages); - if(forum) - mThreadModel->setMsgReadStatus(mThreadModel->root(),read,children); - else + QModelIndex src_index; + if(forum) + src_index = mThreadModel->root(); + else { - QModelIndexList selectedIndexes = ui->threadTreeWidget->selectionModel()->selectedIndexes(); - - if(selectedIndexes.size() != RsGxsForumModel::COLUMN_THREAD_NB_COLUMNS) // check that a single row is selected - return ; - - QModelIndex index = *selectedIndexes.begin(); - - mThreadModel->setMsgReadStatus(mThreadProxyModel->mapToSource(index),read,children); + if (!msgId.isNull()) + src_index = mThreadProxyModel->mapToSource(getCurrentIndex()); + else + src_index = mThreadModel->getIndexOfMessage(mThreadId); } + mThreadModel->setMsgReadStatus(src_index,read,children); + + //Restore Selection + whileBlocking(ui->threadTreeWidget)->setCurrentIndex(mThreadProxyModel->mapFromSource(mThreadModel->getIndexOfMessage(mThreadId))); + recursRestoreExpandedItems(QModelIndex(),mSavedExpandedMessages); } void GxsForumThreadWidget::markMsgAsRead() @@ -1490,6 +1459,20 @@ void GxsForumThreadWidget::setAllMessagesReadDo(bool read, uint32_t &/*token*/) markMsgAsReadUnread(read, true, true); } +#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) +void GxsForumThreadWidget::expandSubtree() { + QAction* the_action = qobject_cast(sender()); + if (!the_action) { + return; + } + const QModelIndex current_index = ui->threadTreeWidget->currentIndex(); + if (!current_index.isValid()) { + return; + } + ui->threadTreeWidget->expandRecursively(current_index); +} +#endif + bool GxsForumThreadWidget::navigate(const RsGxsMessageId &msgId) { QModelIndex source_index = mThreadModel->getIndexOfMessage(msgId); @@ -1505,9 +1488,8 @@ bool GxsForumThreadWidget::navigate(const RsGxsMessageId &msgId) QModelIndex indx = mThreadProxyModel->mapFromSource(source_index); ui->threadTreeWidget->setCurrentIndex(indx); - ui->threadTreeWidget->scrollTo(indx); + ui->threadTreeWidget->scrollTo(ui->threadTreeWidget->currentIndex());//May change if model reloaded ui->threadTreeWidget->setFocus(); - changedThread(indx); return true; } @@ -1660,7 +1642,7 @@ void GxsForumThreadWidget::async_msg_action(const MsgMethod &action) if(!rsGxsForums->getForumContent(groupId(),msgs_to_request,msgs)) { - std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve forum group info for forum " << groupId() << std::endl; + std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve forum message info for forum " << groupId() << " and thread " << mThreadId << std::endl; return; } @@ -1834,10 +1816,26 @@ void GxsForumThreadWidget::filterItems(const QString& text) // We do this in order to trigger a new filtering action in the proxy model. mThreadProxyModel->setFilterRegExp(QRegExp(QString(RsGxsForumModel::FilterString))) ; - if(!lst.empty()) + if(!lst.empty()) ui->threadTreeWidget->expandAll(); - else + else { + // currentIndex() not on the clicked message, so not this way + // if (!mThreadId.isNull()) { + // an_index = mThreadProxyModel->mapToSource(ui->threadTreeWidget->currentIndex()); + // } ui->threadTreeWidget->collapseAll(); + if (!mThreadId.isNull()) { + // ...but this one + QModelIndex an_index = mThreadModel->getIndexOfMessage(mThreadId); + if (an_index.isValid()) { + QModelIndex the_index = mThreadProxyModel->mapFromSource(an_index); + ui->threadTreeWidget->setCurrentIndex(the_index); + ui->threadTreeWidget->scrollTo(the_index); + // don't change focus + // ui->threadTreeWidget->setFocus(); + } + } + } if(count > 0) ui->filterLineEdit->setToolTip(tr("No result.")) ; @@ -1851,25 +1849,27 @@ void GxsForumThreadWidget::filterItems(const QString& text) void GxsForumThreadWidget::postForumLoading() { + if(groupId().isNull()) + return; + #ifdef DEBUG_FORUMS - std::cerr << "Post forum loading..." << std::endl; + std::cerr << "Post forum loading..." << std::endl; #endif - if(!mNavigatePendingMsgId.isNull() && mThreadModel->getIndexOfMessage(mNavigatePendingMsgId).isValid()) - { + if(!mNavigatePendingMsgId.isNull() && mThreadModel->getIndexOfMessage(mNavigatePendingMsgId).isValid()) + { #ifdef DEBUG_FORUMS - std::cerr << "Pending msg navigation: " << mNavigatePendingMsgId << ". Using it as new thread Id" << std::endl; + std::cerr << "Pending msg navigation: " << mNavigatePendingMsgId << ". Using it as new thread Id" << std::endl; #endif - QModelIndex source_index = mThreadModel->getIndexOfMessage(mNavigatePendingMsgId); - QModelIndex index = mThreadProxyModel->mapFromSource(source_index); + QModelIndex source_index = mThreadModel->getIndexOfMessage(mNavigatePendingMsgId); + QModelIndex index = mThreadProxyModel->mapFromSource(source_index); ui->threadTreeWidget->selectionModel()->setCurrentIndex(index,QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); - ui->threadTreeWidget->scrollTo(index); + ui->threadTreeWidget->scrollTo(ui->threadTreeWidget->currentIndex());//May change if model reloaded - changedThread(index); - mNavigatePendingMsgId.clear(); - } - else + mNavigatePendingMsgId.clear(); + } + else { QModelIndex source_index = mThreadModel->getIndexOfMessage(mThreadId); @@ -1878,7 +1878,7 @@ void GxsForumThreadWidget::postForumLoading() { QModelIndex index = mThreadProxyModel->mapFromSource(source_index); ui->threadTreeWidget->selectionModel()->setCurrentIndex(index,QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); - ui->threadTreeWidget->scrollTo(index); + ui->threadTreeWidget->scrollTo(ui->threadTreeWidget->currentIndex());//May change if model reloaded #ifdef DEBUG_FORUMS std::cerr << " re-selecting index of message " << mThreadId << " to " << source_index.row() << "," << source_index.column() << " " << (void*)source_index.internalPointer() << std::endl; #endif @@ -1896,13 +1896,17 @@ void GxsForumThreadWidget::postForumLoading() // we also need to restore expanded threads } + ui->newthreadButton->show(); ui->forumName->setText(QString::fromUtf8(mForumGroup.mMeta.mGroupName.c_str())); ui->threadTreeWidget->sortByColumn(RsGxsForumModel::COLUMN_THREAD_DATE, Qt::DescendingOrder); - ui->threadTreeWidget->update(); + ui->threadTreeWidget->update(); + ui->viewBox->setEnabled(true); + ui->filterLineEdit->setEnabled(true); - recursRestoreExpandedItems(mThreadProxyModel->mapFromSource(mThreadModel->root()),mSavedExpandedMessages); - //mUpdating = false; + recursRestoreExpandedItems(mThreadProxyModel->mapFromSource(mThreadModel->root()),mSavedExpandedMessages); + //mUpdating = false; } + void GxsForumThreadWidget::updateGroupData() { if(groupId().isNull()) @@ -1912,6 +1916,8 @@ void GxsForumThreadWidget::updateGroupData() // ui->threadTreeWidget->selectionModel()->reset(); // mThreadProxyModel->clear(); + setForumDescriptionLoading(); + RsThread::async([this]() { // 1 - get message data from p3GxsForums @@ -1920,41 +1926,41 @@ void GxsForumThreadWidget::updateGroupData() std::vector groups; forumIds.push_back(groupId()); + bool success = false; if(!rsGxsForums->getForumsInfo(forumIds,groups)) - { std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve forum group info for forum " << groupId() << std::endl; - return; - } - - if(groups.size() != 1) - { + else if(groups.size() != 1) std::cerr << __PRETTY_FUNCTION__ << " obtained more than one group info for forum " << groupId() << std::endl; - return; - } + else + success = true; - // 2 - sort the messages into a proper hierarchy - - RsGxsForumGroup *group = new RsGxsForumGroup(groups[0]); // we use a pointer in order to avoid group deletion while we're in the thread. - - // 3 - update the model in the UI thread. - - RsQThreadUtils::postToObject( [group,this]() + if(success) { - /* Here it goes any code you want to be executed on the Qt Gui + // 2 - sort the messages into a proper hierarchy + + RsGxsForumGroup group(groups[0]); // we use a copy to share the object in order to avoid group deletion while we're in the thread. + + // 3 - update the model in the UI thread. + + RsQThreadUtils::postToObject( [group,this]() + { + /* Here it goes any code you want to be executed on the Qt Gui * thread, for example to update the data model with new information * after a blocking call to RetroShare API complete */ - mForumGroup = *group; - delete group; + mForumGroup = group; + mThreadId.clear(); - ui->threadTreeWidget->setColumnHidden(RsGxsForumModel::COLUMN_THREAD_DISTRIBUTION, !IS_GROUP_PGP_KNOWN_AUTHED(mForumGroup.mMeta.mSignFlags) && !(IS_GROUP_PGP_AUTHED(mForumGroup.mMeta.mSignFlags))); - ui->subscribeToolButton->setHidden(IS_GROUP_SUBSCRIBED(mForumGroup.mMeta.mSubscribeFlags)) ; + ui->threadTreeWidget->setColumnHidden(RsGxsForumModel::COLUMN_THREAD_DISTRIBUTION, !IS_GROUP_PGP_KNOWN_AUTHED(mForumGroup.mMeta.mSignFlags) && !(IS_GROUP_PGP_AUTHED(mForumGroup.mMeta.mSignFlags))); + ui->subscribeToolButton->setHidden(IS_GROUP_SUBSCRIBED(mForumGroup.mMeta.mSubscribeFlags)) ; - updateForumDescription(); - - }, this ); + updateForumDescription(true); + }, this ); + } + else + RsQThreadUtils::postToObject( [this]() { updateForumDescription(false); },this); }); } @@ -1972,17 +1978,18 @@ void GxsForumThreadWidget::updateMessageData(const RsGxsMessageId& msgId) std::vector msgs; msgs_to_request.insert(msgId); + QString error_string; if(!rsGxsForums->getForumContent(groupId(),msgs_to_request,msgs)) { - std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve forum group info for forum " << groupId() << std::endl; - return; + std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve message info for forum " << groupId() << " and MsgId " << msgId << std::endl; + error_string = tr("Failed to retrieve this message. Is the database currently overloaded?"); } if(msgs.empty()) { std::cerr << __PRETTY_FUNCTION__ << " no posts for msgId " << msgId << ". Database corruption?" << std::endl; - return; + error_string = tr("No data for this message. Is the database corrupted?"); } if(msgs.size() > 1) { @@ -1990,26 +1997,32 @@ void GxsForumThreadWidget::updateMessageData(const RsGxsMessageId& msgId) std::cerr << "Messages are:" << std::endl; for(auto it(msgs.begin());it!=msgs.end();++it) std::cerr << (*it).mMeta << std::endl; + + error_string = tr("More than one entry for this message. Is the database corrupted?"); } - // 2 - sort the messages into a proper hierarchy - - RsGxsForumMsg *msg = new RsGxsForumMsg(msgs[0]); - - // 3 - update the model in the UI thread. - - RsQThreadUtils::postToObject( [msg,this]() + if(error_string.isNull()) { - /* Here it goes any code you want to be executed on the Qt Gui + // 2 - sort the messages into a proper hierarchy + + RsGxsForumMsg msg(msgs[0]); + + // 3 - update the model in the UI thread. + + RsQThreadUtils::postToObject( [msg,this]() + { + /* Here it goes any code you want to be executed on the Qt Gui * thread, for example to update the data model with new information * after a blocking call to RetroShare API complete */ - insertMessageData(*msg); + insertMessageData(msg); - delete msg; - ui->threadTreeWidget->setColumnHidden(RsGxsForumModel::COLUMN_THREAD_DISTRIBUTION, !IS_GROUP_PGP_KNOWN_AUTHED(mForumGroup.mMeta.mSignFlags) && !(IS_GROUP_PGP_AUTHED(mForumGroup.mMeta.mSignFlags))); - ui->subscribeToolButton->setHidden(IS_GROUP_SUBSCRIBED(mForumGroup.mMeta.mSubscribeFlags)) ; - }, this ); + ui->threadTreeWidget->setColumnHidden(RsGxsForumModel::COLUMN_THREAD_DISTRIBUTION, !IS_GROUP_PGP_KNOWN_AUTHED(mForumGroup.mMeta.mSignFlags) && !(IS_GROUP_PGP_AUTHED(mForumGroup.mMeta.mSignFlags))); + ui->subscribeToolButton->setHidden(IS_GROUP_SUBSCRIBED(mForumGroup.mMeta.mSubscribeFlags)) ; + }, this ); + } + else + RsQThreadUtils::postToObject( [error_string,this](){ setMessageLoadingError(error_string); } ); }); } diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.h b/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.h index a548cda07..22aa2ecc3 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.h +++ b/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.h @@ -52,6 +52,10 @@ class GxsForumThreadWidget : public GxsMessageFrameWidget Q_PROPERTY(QColor textColorUnreadChildren READ textColorUnreadChildren WRITE setTextColorUnreadChildren) Q_PROPERTY(QColor textColorNotSubscribed READ textColorNotSubscribed WRITE setTextColorNotSubscribed) Q_PROPERTY(QColor textColorMissing READ textColorMissing WRITE setTextColorMissing) + Q_PROPERTY(QColor textColorPinned READ textColorPinned WRITE setTextColorPinned) + + Q_PROPERTY(QColor backgroundColorPinned READ backgroundColorPinned WRITE setBackgroundColorPinned) + Q_PROPERTY(QColor backgroundColorFiltered READ backgroundColorFiltered WRITE setBackgroundColorFiltered) public: explicit GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget *parent = NULL); @@ -62,12 +66,20 @@ public: QColor textColorUnreadChildren() const { return mTextColorUnreadChildren; } QColor textColorNotSubscribed() const { return mTextColorNotSubscribed; } QColor textColorMissing() const { return mTextColorMissing; } + QColor textColorPinned() const { return mTextColorPinned; } + + QColor backgroundColorPinned() const { return mBackgroundColorPinned; } + QColor backgroundColorFiltered() const { return mBackgroundColorFiltered; } void setTextColorRead (QColor color) ; void setTextColorUnread (QColor color) ; void setTextColorUnreadChildren(QColor color) ; void setTextColorNotSubscribed (QColor color) ; void setTextColorMissing (QColor color) ; + void setTextColorPinned (QColor color) ; + + void setBackgroundColorPinned (QColor color); + void setBackgroundColorFiltered (QColor color); /* GxsMessageFrameWidget */ virtual void groupIdChanged(); @@ -92,16 +104,18 @@ protected: /* GxsMessageFrameWidget */ virtual void setAllMessagesReadDo(bool read, uint32_t &token); + void setMessageLoadingError(const QString& error); private slots: /** Create the context popup menu and it's submenus */ void threadListCustomPopupMenu(QPoint point); void contextMenuTextBrowser(QPoint point); + void headerContextMenuRequested(const QPoint& pos); void changedSelection(const QModelIndex &, const QModelIndex &); void changedThread(QModelIndex index); void changedVersion(); void clickedThread (QModelIndex index); - void postForumLoading(); + void postForumLoading(); void reply_with_private_message(); void replytoforummessage(); @@ -143,6 +157,12 @@ private slots: void filterColumnChanged(int column); void filterItems(const QString &text); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) + void expandSubtree(); +#endif + void changeHeaderColumnVisibility(bool visibility); + void showBannedText(bool display); private: void insertMessageData(const RsGxsForumMsg &msg); bool getCurrentPost(ForumModelPostEntry& fmpe) const ; @@ -157,7 +177,7 @@ private: int getSelectedMsgCount(QList *pRows, QList *pRowsRead, QList *pRowsUnread); void setMsgReadStatus(QList &rows, bool read); - void markMsgAsReadUnread(bool read, bool children, bool forum); + void markMsgAsReadUnread(bool read, bool children, bool forum, RsGxsMessageId msgId = RsGxsMessageId()); void calculateUnreadCount(); void togglethreadview_internal(); @@ -170,11 +190,15 @@ private: static void loadAuthorIdCallback(GxsIdDetailsType type, const RsIdentityDetails &details, QObject *object, const QVariant &/*data*/); void updateMessageData(const RsGxsMessageId& msgId); - void updateForumDescription(); + void updateForumDescription(bool success); void handleEvent_main_thread(std::shared_ptr event); private: + void setForumDescriptionLoading(); + void clearForumDescription(); + void blankPost(); + RsGxsGroupId mLastForumID; RsGxsMessageId mThreadId; RsGxsMessageId mOrigThreadId; @@ -187,6 +211,7 @@ private: GxsForumsFillThread *mFillThread; unsigned int mUnreadCount; unsigned int mNewCount; + bool mDisplayBannedText; /* Color definitions (for standard see qss.default) */ QColor mTextColorRead; @@ -194,6 +219,10 @@ private: QColor mTextColorUnreadChildren; QColor mTextColorNotSubscribed; QColor mTextColorMissing; + QColor mTextColorPinned; + + QColor mBackgroundColorPinned; + QColor mBackgroundColorFiltered; RsGxsMessageId mNavigatePendingMsgId; QList mIgnoredMsgId; diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.ui b/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.ui index ee9b71c67..088c50cf7 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.ui +++ b/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.ui @@ -95,7 +95,7 @@ - :/icons/png/add.png:/icons/png/add.png + :/icons/mail/compose.png:/icons/mail/compose.png @@ -245,32 +245,6 @@ - - - - - 24 - 24 - - - - Qt::NoFocus - - - Download all files - - - Qt::LeftToRight - - - - :/icons/global_switch_on_128.png:/icons/global_switch_on_128.png - - - true - - - @@ -289,8 +263,8 @@ - - :/images/replymailall24-hover.png:/images/replymailall24-hover.png + + :/icons/mail/reply.png:/icons/mail/reply.png Qt::ToolButtonIconOnly @@ -300,53 +274,7 @@ - - - - - 24 - 24 - - - - Qt::NoFocus - - - - - - - :/images/edit_remove24.png:/images/edit_remove24.png - - - true - - - true - - - - - - - Qt::Vertical - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - + @@ -376,15 +304,15 @@ - - :/images/back.png:/images/back.png + + :/icons/png/arrow-left.png:/icons/png/arrow-left.png true - + false @@ -417,46 +345,15 @@ - - :/images/forward.png:/images/forward.png + + :/icons/png/arrow-right.png:/icons/png/arrow-right.png true - - - - - - - - - - - Qt::Vertical - - - - - - - - - - - - - - - - - By - - - - + @@ -470,9 +367,115 @@ + + + :/icons/png/next-unread.png:/icons/png/next-unread.png + + + true + + + + + + + + 24 + 24 + + + + Qt::NoFocus + + + Download all files + + + Qt::LeftToRight + + + + :/icons/png/download.png:/icons/png/download.png + + + true + + + + + + + Qt::Vertical + + + + + + + + + + + + + + + + + Qt::Vertical + + + + + + + By + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 24 + 24 + + + + Qt::NoFocus + + + + - :/images/start.png:/images/start.png + :/images/edit_remove24.png:/images/edit_remove24.png + + + true + + + true @@ -503,6 +506,11 @@ 10 + + + MS Sans Serif + + Qt::CustomContextMenu @@ -524,6 +532,16 @@ + + LineEditClear + QLineEdit +
gui/common/LineEditClear.h
+
+ + RSTextBrowser + QTextBrowser +
gui/common/RSTextBrowser.h
+
SubscribeToolButton QToolButton @@ -534,16 +552,6 @@ QLabel
gui/gxs/GxsIdLabel.h
- - LineEditClear - QLineEdit -
gui/common/LineEditClear.h
-
- - RSTextBrowser - QTextBrowser -
gui/common/RSTextBrowser.h
-
ElidedLabel QLabel diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumUserNotify.cpp b/retroshare-gui/src/gui/gxsforums/GxsForumUserNotify.cpp index 8a910659a..b2e5bcc8e 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumUserNotify.cpp +++ b/retroshare-gui/src/gui/gxsforums/GxsForumUserNotify.cpp @@ -18,11 +18,13 @@ * * *******************************************************************************/ +#include "retroshare/rsgxsforums.h" +#include "gui/common/FilesDefs.h" #include "GxsForumUserNotify.h" #include "gui/MainWindow.h" -GxsForumUserNotify::GxsForumUserNotify(RsGxsIfaceHelper *ifaceImpl, QObject *parent) : - GxsUserNotify(ifaceImpl, parent) +GxsForumUserNotify::GxsForumUserNotify(RsGxsIfaceHelper *ifaceImpl, const GxsGroupFrameDialog *g, QObject *parent) : + GxsUserNotify(ifaceImpl, g, parent) { mCountChildMsgs = true; } @@ -37,12 +39,12 @@ bool GxsForumUserNotify::hasSetting(QString *name, QString *group) QIcon GxsForumUserNotify::getIcon() { - return QIcon(":/icons/png/forums.png"); + return FilesDefs::getIconFromQtResourcePath(":/icons/png/forums.png"); } QIcon GxsForumUserNotify::getMainIcon(bool hasNew) { - return hasNew ? QIcon(":/icons/png/forums-notify.png") : QIcon(":/icons/png/forums.png"); + return hasNew ? FilesDefs::getIconFromQtResourcePath(":/icons/png/forums-notify.png") : FilesDefs::getIconFromQtResourcePath(":/icons/png/forums.png"); } void GxsForumUserNotify::iconClicked() diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumUserNotify.h b/retroshare-gui/src/gui/gxsforums/GxsForumUserNotify.h index 190f177e7..9fbef1e5e 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumUserNotify.h +++ b/retroshare-gui/src/gui/gxsforums/GxsForumUserNotify.h @@ -28,9 +28,10 @@ class GxsForumUserNotify : public GxsUserNotify Q_OBJECT public: - GxsForumUserNotify(RsGxsIfaceHelper *ifaceImpl, QObject *parent = 0); + GxsForumUserNotify(RsGxsIfaceHelper *ifaceImpl, const GxsGroupFrameDialog *g, QObject *parent = 0); virtual bool hasSetting(QString *name, QString *group); + virtual QString textInfo() const override { return tr("new message(s)"); } private: virtual QIcon getIcon(); diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumsDialog.cpp b/retroshare-gui/src/gui/gxsforums/GxsForumsDialog.cpp index 71766ca63..7ce84ea5c 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumsDialog.cpp +++ b/retroshare-gui/src/gui/gxsforums/GxsForumsDialog.cpp @@ -38,14 +38,15 @@ public: }; /** Constructor */ -GxsForumsDialog::GxsForumsDialog(QWidget *parent) - : GxsGroupFrameDialog(rsGxsForums, parent) +GxsForumsDialog::GxsForumsDialog(QWidget *parent) : + GxsGroupFrameDialog(rsGxsForums, parent), mEventHandlerId(0) { mCountChildMsgs = true; - mEventHandlerId = 0; - // Needs to be asynced because this function is likely to be called by another thread! - rsEvents->registerEventsHandler(RsEventType::GXS_FORUMS, [this](std::shared_ptr event) { RsQThreadUtils::postToObject( [=]() { handleEvent_main_thread(event); }, this ); }, mEventHandlerId ); + rsEvents->registerEventsHandler( + [this](std::shared_ptr event) + { RsQThreadUtils::postToObject( [=]() { handleEvent_main_thread(event); }, this ); }, + mEventHandlerId, RsEventType::GXS_FORUMS ); } void GxsForumsDialog::handleEvent_main_thread(std::shared_ptr event) @@ -60,21 +61,20 @@ void GxsForumsDialog::handleEvent_main_thread(std::shared_ptr eve switch(e->mForumEventCode) { case RsForumEventCode::NEW_MESSAGE: - updateMessageSummaryList(e->mForumGroupId); - break; - case RsForumEventCode::UPDATED_MESSAGE: // [[fallthrough]]; - updateDisplay(false); - break; - case RsForumEventCode::READ_STATUS_CHANGED: - updateMessageSummaryList(e->mForumGroupId); + updateGroupStatisticsReal(e->mForumGroupId); // update the list immediately break; case RsForumEventCode::NEW_FORUM: // [[fallthrough]]; case RsForumEventCode::SUBSCRIBE_STATUS_CHANGED: updateDisplay(true); break; + + case RsForumEventCode::STATISTICS_CHANGED: + updateGroupStatistics(e->mForumGroupId); // update the list when redraw less often than once every 2 mins + break; + default: break; } @@ -86,6 +86,26 @@ GxsForumsDialog::~GxsForumsDialog() rsEvents->unregisterEventsHandler(mEventHandlerId); } +bool GxsForumsDialog::getGroupData(std::list& groupInfo) +{ + std::vector groups; + + if(! rsGxsForums->getForumsInfo(std::list(),groups)) + return false; + + for (auto& group: groups) + groupInfo.push_back(new RsGxsForumGroup(group)); + + return true; +} + +bool GxsForumsDialog::getGroupStatistics(const RsGxsGroupId& groupId,GxsGroupStatistic& stat) +{ + return rsGxsForums->getForumStatistics(groupId,stat); +} + + + QString GxsForumsDialog::getHelpString() const { QString hlp_str = tr( @@ -119,7 +139,7 @@ void GxsForumsDialog::shareInMessage(const RsGxsGroupId& forum_id,const QList &groupInfo, RsUserdata *&userdata) +void GxsForumsDialog::groupInfoToGroupItemInfo(const RsGxsGenericGroupData *groupData, GroupItemInfo &groupItemInfo) { - std::vector groups; - rsGxsForums->getGroupData(token, groups); + GxsGroupFrameDialog::groupInfoToGroupItemInfo(groupData, groupItemInfo); - /* Save groups to fill description */ - GxsForumGroupInfoData *forumData = new GxsForumGroupInfoData; - userdata = forumData; + const RsGxsForumGroup *forumGroupData = dynamic_cast(groupData); - std::vector::iterator groupIt; - for (groupIt = groups.begin(); groupIt != groups.end(); ++groupIt) { - RsGxsForumGroup &group = *groupIt; - groupInfo.push_back(group.mMeta); - - if (!group.mDescription.empty()) { - forumData->mDescription[group.mMeta.mGroupId] = QString::fromUtf8(group.mDescription.c_str()); - } - } -} - -void GxsForumsDialog::groupInfoToGroupItemInfo(const RsGroupMetaData &groupInfo, GroupItemInfo &groupItemInfo, const RsUserdata *userdata) -{ - GxsGroupFrameDialog::groupInfoToGroupItemInfo(groupInfo, groupItemInfo, userdata); - - const GxsForumGroupInfoData *forumData = dynamic_cast(userdata); - if (!forumData) { - std::cerr << "GxsForumsDialog::groupInfoToGroupItemInfo() Failed to cast data to GxsForumGroupInfoData"; - std::cerr << std::endl; + if (!forumGroupData) + { + std::cerr << "GxsChannelDialog::groupInfoToGroupItemInfo() Failed to cast data to GxsChannelGroupInfoData"<< std::endl; return; } - QMap::const_iterator descriptionIt = forumData->mDescription.find(groupInfo.mGroupId); - if (descriptionIt != forumData->mDescription.end()) { - groupItemInfo.description = descriptionIt.value(); - } - - //if (IS_GROUP_ADMIN(groupInfo.mSubscribeFlags)) - // groupItemInfo.icon = QIcon(":images/konv_message2.png"); - if ((IS_GROUP_PGP_AUTHED(groupInfo.mSignFlags)) || (IS_GROUP_MESSAGE_TRACKING(groupInfo.mSignFlags)) ) - groupItemInfo.icon = QIcon(":icons/png/forums-signed.png"); + groupItemInfo.description = QString::fromUtf8(forumGroupData->mDescription.c_str()); + if(IS_GROUP_ADMIN(groupData->mMeta.mSubscribeFlags)) + groupItemInfo.icon = FilesDefs::getIconFromQtResourcePath(":icons/png/forums.png"); + else if ((IS_GROUP_PGP_AUTHED(groupData->mMeta.mSignFlags)) || (IS_GROUP_MESSAGE_TRACKING(groupData->mMeta.mSignFlags)) ) + groupItemInfo.icon = FilesDefs::getIconFromQtResourcePath(":icons/png/forums-signed.png"); } + diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumsDialog.h b/retroshare-gui/src/gui/gxsforums/GxsForumsDialog.h index 9b6da9d00..75d10a1e1 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumsDialog.h +++ b/retroshare-gui/src/gui/gxsforums/GxsForumsDialog.h @@ -40,24 +40,27 @@ public: void shareInMessage(const RsGxsGroupId& forum_id, const QList& file_link) ; protected: - virtual UserNotify *createUserNotify(QObject *parent) override; - virtual QString getHelpString() const ; virtual RetroShareLink::enumType getLinkType() { return RetroShareLink::TYPE_FORUM; } virtual GroupFrameSettings::Type groupFrameSettingsType() { return GroupFrameSettings::Forum; } - virtual void groupInfoToGroupItemInfo(const RsGroupMetaData &groupInfo, GroupItemInfo &groupItemInfo, const RsUserdata *userdata); + + UserNotify *createUserNotify(QObject *parent) override; + bool getGroupData(std::list& groupInfo) override; + bool getGroupStatistics(const RsGxsGroupId& groupId,GxsGroupStatistic& stat) override; + void groupInfoToGroupItemInfo(const RsGxsGenericGroupData *groupData, GroupItemInfo &groupItemInfo) override; private: /* GxsGroupFrameDialog */ - virtual QString text(TextType type); - virtual QString icon(IconType type); - virtual QString settingsGroupName() { return "ForumsDialog"; } - virtual GxsGroupDialog *createNewGroupDialog(TokenQueue *tokenQueue); - virtual GxsGroupDialog *createGroupDialog(TokenQueue *tokenQueue, RsTokenService *tokenService, GxsGroupDialog::Mode mode, RsGxsGroupId groupId); - virtual int shareKeyType(); - virtual GxsMessageFrameWidget *createMessageFrameWidget(const RsGxsGroupId &groupId); - virtual uint32_t requestGroupSummaryType() { return GXS_REQUEST_TYPE_GROUP_DATA; } // request complete group data - virtual void loadGroupSummaryToken(const uint32_t &token, std::list &groupInfo, RsUserdata* &userdata); + QString text(TextType type)override ; + QString icon(IconType type)override ; + QString settingsGroupName() override { return "ForumsDialog"; } + + GxsGroupDialog *createNewGroupDialog() override; + GxsGroupDialog *createGroupDialog(GxsGroupDialog::Mode mode, RsGxsGroupId groupId) override; + + int shareKeyType() override; + GxsMessageFrameWidget *createMessageFrameWidget(const RsGxsGroupId &groupId) override; + uint32_t requestGroupSummaryType() override { return GXS_REQUEST_TYPE_GROUP_DATA; } // request complete group data void handleEvent_main_thread(std::shared_ptr event); diff --git a/retroshare-gui/src/gui/help/browser/helpbrowser.ui b/retroshare-gui/src/gui/help/browser/helpbrowser.ui index a3d739438..824ffcb80 100644 --- a/retroshare-gui/src/gui/help/browser/helpbrowser.ui +++ b/retroshare-gui/src/gui/help/browser/helpbrowser.ui @@ -14,7 +14,7 @@ RetroShare Help - + :/images/help.png:/images/help.png @@ -25,7 +25,16 @@ 6 - + + 9 + + + 9 + + + 9 + + 9 @@ -58,7 +67,16 @@ QFrame::Raised - + + 9 + + + 9 + + + 9 + + 9 @@ -69,7 +87,16 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -171,7 +198,16 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -238,7 +274,7 @@ RetroShare Help - + :/images/help.png:/images/help.png @@ -248,7 +284,16 @@ 6 - + + 6 + + + 6 + + + 6 + + 6 @@ -297,7 +342,16 @@ Search - + + 6 + + + 6 + + + 6 + + 6 @@ -308,7 +362,16 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -445,8 +508,8 @@ - - :/images/back.png:/images/back.png + + :/icons/png/arrow-left.png:/icons/png/arrow-left.png Back @@ -460,8 +523,8 @@ - - :/images/forward.png:/images/forward.png + + :/icons/png/arrow-right.png:/icons/png/arrow-right.png Forward @@ -475,8 +538,8 @@ - - :/images/gohome.png:/images/gohome.png + + :/icons/png/home.png:/icons/png/home.png Home @@ -514,8 +577,8 @@ - - :/images/exit_24x24.png:/images/exit_24x24.png + + :/icons/png/exit2.png:/icons/png/exit2.png Close @@ -551,6 +614,7 @@ + diff --git a/retroshare-gui/src/gui/help/content/de/peerdetails.html b/retroshare-gui/src/gui/help/content/de/peerdetails.html index f008600c1..b1b0d3db7 100644 --- a/retroshare-gui/src/gui/help/content/de/peerdetails.html +++ b/retroshare-gui/src/gui/help/content/de/peerdetails.html @@ -33,7 +33,7 @@ Peer Details Trust settings and Signing

Trust Settings

- +

The trust settings refers to the gnupg web of trust mecanism.
This trust means how you trust your friends when he is signing other PGP keys.
diff --git a/retroshare-gui/src/gui/icons.qrc b/retroshare-gui/src/gui/icons.qrc index 81eeecb08..0f3477394 100644 --- a/retroshare-gui/src/gui/icons.qrc +++ b/retroshare-gui/src/gui/icons.qrc @@ -1,9 +1,16 @@ icons/onion.png + icons/svg/design.svg icons/svg/hidden.svg + icons/svg/ratio-auto.svg + icons/svg/ratio-1-1.svg + icons/svg/ratio-3-4.svg + icons/svg/ratio-16-9.svg icons/svg/randomness.svg icons/svg/password.svg + icons/svg/listlayout.svg + icons/svg/gridlayout.svg icons/stars/star0.png icons/stars/star1.png icons/stars/star2.png @@ -35,14 +42,11 @@ icons/bullet_grey_128.png icons/bullet_red_128.png icons/bullet_yellow_128.png - icons/channels_128.png - icons/channels_red_128.png - icons/chat_128.png - icons/chat_red_128.png - icons/circles_128.png - icons/circles_new_128.png + icons/collections.png icons/flag-green.png icons/friends_128.png + icons/folder.png + icons/folderopen.png icons/global_switch_off_128.png icons/global_switch_on_128.png icons/gmail.png @@ -51,27 +55,19 @@ icons/information_128.png icons/internet_128.png icons/invite64.png - icons/knews_128.png - icons/knews_red_128.png - icons/konversation_128.png - icons/konversation128.png - icons/konversation_red_128.png - icons/ktorrent_128.png - icons/ktorrent_red_128.png icons/logo_0_connected_128.png icons/logo_128.png icons/logo_1_connected_128.png icons/logo_2_connected_128.png - icons/mail_128.png - icons/mail_old_128.png - icons/mail_red_128.png - icons/newsfeed128.png icons/outlook.png icons/question.png icons/plugins_128.png icons/png/add.png + icons/png/add-identity.png + icons/png/anonymous.png icons/png/attach-image.png icons/png/attach.png + icons/png/arrow.png icons/png/cert.png icons/png/channels-notify.png icons/png/channels.png @@ -81,14 +77,12 @@ icons/png/chat-lobbies.png icons/png/circles.png icons/png/digital-key.png + icons/png/dock.png icons/png/empty-circle.png icons/png/enter.png icons/png/exit.png icons/png/feedreader-notify.png icons/png/feedreader.png - icons/png/feed-other.png - icons/png/feed-popular.png - icons/png/feed-subscribed.png icons/png/filesharing-notify.png icons/png/filesharing.png icons/png/font.png @@ -105,6 +99,7 @@ icons/png/leave2.png icons/png/messages-notify.png icons/png/messages.png + icons/png/menu.png icons/png/microphone_mute.png icons/png/microphone.png icons/png/netgraph.png @@ -116,7 +111,7 @@ icons/png/newsfeed-notify.png icons/png/newsfeed.png icons/png/options.png - icons/png/pencil-edit-button.png + icons/png/pencil-edit-button.png icons/png/people-notify.png icons/png/people.png icons/png/person.png @@ -124,6 +119,7 @@ icons/png/phone.png icons/png/posted-notify.png icons/png/posted.png + icons/png/profile.png icons/png/search.png icons/png/send-message-blocked.png icons/png/send-message.png @@ -134,10 +130,9 @@ icons/png/thumbs-down.png icons/png/thumbs-neutral.png icons/png/thumbs-up.png - icons/png/typing.png + icons/png/typing.png + icons/png/undock.png icons/png/video.png - icons/posted_128.png - icons/posted_red_128.png icons/quit_128.png icons/search_red_128.png icons/security_high_128.png @@ -257,9 +252,6 @@ icons/yandex.png icons/png/markdown-mark.png icons/png/channel.png - icons/png/channel-other.png - icons/png/channel-popular.png - icons/png/channel-subscribed.png icons/png/circles-black.png icons/png/circles-gray.png icons/png/circles-notify.png @@ -287,9 +279,11 @@ icons/png/newsfeed2.png icons/png/postedlinks.png icons/png/people2.png + icons/png/photo.png icons/png/bandwidth.png icons/png/options2.png icons/png/exit2.png + icons/png/wiki.png icons/svg/addstickers.svg icons/png/addstickers.png icons/textedit/bold.png @@ -311,5 +305,33 @@ icons/png/add-file.png icons/png/add-image.png icons/png/attachements.png + icons/png/arrow-right.png + icons/png/arrow-left.png + icons/png/next-unread.png + icons/mail/compose.png + icons/mail/downloadall.png + icons/mail/delete.png + icons/mail/tags.png + icons/mail/quote.png + icons/mail/send.png + icons/mail/contacts.png + icons/mail/save.png + icons/mail/filter24.png + icons/mail/foward.png + icons/mail/reply.png + icons/mail/reply-all.png + icons/mail/attach16.png + icons/mail/attach24.png + icons/mail/write-mail.png + icons/textedit/align.png + icons/textedit/font-decrease.png + icons/textedit/smile.png + icons/textedit/font-increase.png + icons/textedit/code.png + icons/fullscreen.png + icons/fullscreen-exit.png + icons/notification.png + icons/wire.png + icons/wire-circle.png diff --git a/retroshare-gui/src/gui/icons/channels_128.png b/retroshare-gui/src/gui/icons/channels_128.png deleted file mode 100644 index ed21268c2..000000000 Binary files a/retroshare-gui/src/gui/icons/channels_128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/icons/channels_red_128.png b/retroshare-gui/src/gui/icons/channels_red_128.png deleted file mode 100644 index 6d384db9d..000000000 Binary files a/retroshare-gui/src/gui/icons/channels_red_128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/icons/chat_128.png b/retroshare-gui/src/gui/icons/chat_128.png deleted file mode 100644 index 7212a977d..000000000 Binary files a/retroshare-gui/src/gui/icons/chat_128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/icons/chat_red_128.png b/retroshare-gui/src/gui/icons/chat_red_128.png deleted file mode 100644 index 47860491e..000000000 Binary files a/retroshare-gui/src/gui/icons/chat_red_128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/icons/circles_128.png b/retroshare-gui/src/gui/icons/circles_128.png deleted file mode 100644 index d2664a82c..000000000 Binary files a/retroshare-gui/src/gui/icons/circles_128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/icons/circles_new_128.png b/retroshare-gui/src/gui/icons/circles_new_128.png deleted file mode 100644 index 7c2c1099c..000000000 Binary files a/retroshare-gui/src/gui/icons/circles_new_128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/icons/collections.png b/retroshare-gui/src/gui/icons/collections.png new file mode 100644 index 000000000..cb02eb5d3 Binary files /dev/null and b/retroshare-gui/src/gui/icons/collections.png differ diff --git a/retroshare-gui/src/gui/icons/folder.png b/retroshare-gui/src/gui/icons/folder.png new file mode 100644 index 000000000..01f942628 Binary files /dev/null and b/retroshare-gui/src/gui/icons/folder.png differ diff --git a/retroshare-gui/src/gui/icons/folderopen.png b/retroshare-gui/src/gui/icons/folderopen.png new file mode 100644 index 000000000..544e5f0b7 Binary files /dev/null and b/retroshare-gui/src/gui/icons/folderopen.png differ diff --git a/retroshare-gui/src/gui/icons/fullscreen-exit.png b/retroshare-gui/src/gui/icons/fullscreen-exit.png new file mode 100644 index 000000000..eb0d29385 Binary files /dev/null and b/retroshare-gui/src/gui/icons/fullscreen-exit.png differ diff --git a/retroshare-gui/src/gui/icons/fullscreen.png b/retroshare-gui/src/gui/icons/fullscreen.png new file mode 100644 index 000000000..3b14cbe35 Binary files /dev/null and b/retroshare-gui/src/gui/icons/fullscreen.png differ diff --git a/retroshare-gui/src/gui/icons/knews_128.png b/retroshare-gui/src/gui/icons/knews_128.png deleted file mode 100644 index 97fd4eb6f..000000000 Binary files a/retroshare-gui/src/gui/icons/knews_128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/icons/knews_red_128.png b/retroshare-gui/src/gui/icons/knews_red_128.png deleted file mode 100644 index 82cde6d33..000000000 Binary files a/retroshare-gui/src/gui/icons/knews_red_128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/icons/konversation128.png b/retroshare-gui/src/gui/icons/konversation128.png deleted file mode 100644 index 4398fd869..000000000 Binary files a/retroshare-gui/src/gui/icons/konversation128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/icons/konversation_128.png b/retroshare-gui/src/gui/icons/konversation_128.png deleted file mode 100644 index 4398fd869..000000000 Binary files a/retroshare-gui/src/gui/icons/konversation_128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/icons/konversation_red_128.png b/retroshare-gui/src/gui/icons/konversation_red_128.png deleted file mode 100644 index 1f248cbb8..000000000 Binary files a/retroshare-gui/src/gui/icons/konversation_red_128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/icons/ktorrent_128.png b/retroshare-gui/src/gui/icons/ktorrent_128.png deleted file mode 100644 index 764568d35..000000000 Binary files a/retroshare-gui/src/gui/icons/ktorrent_128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/icons/ktorrent_red_128.png b/retroshare-gui/src/gui/icons/ktorrent_red_128.png deleted file mode 100644 index 92a45bf54..000000000 Binary files a/retroshare-gui/src/gui/icons/ktorrent_red_128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/icons/mail/attach16.png b/retroshare-gui/src/gui/icons/mail/attach16.png new file mode 100644 index 000000000..3d983309a Binary files /dev/null and b/retroshare-gui/src/gui/icons/mail/attach16.png differ diff --git a/retroshare-gui/src/gui/icons/mail/attach24.png b/retroshare-gui/src/gui/icons/mail/attach24.png new file mode 100644 index 000000000..37ced59a7 Binary files /dev/null and b/retroshare-gui/src/gui/icons/mail/attach24.png differ diff --git a/retroshare-gui/src/gui/icons/mail/compose.png b/retroshare-gui/src/gui/icons/mail/compose.png new file mode 100644 index 000000000..6be97411b Binary files /dev/null and b/retroshare-gui/src/gui/icons/mail/compose.png differ diff --git a/retroshare-gui/src/gui/icons/mail/contacts.png b/retroshare-gui/src/gui/icons/mail/contacts.png new file mode 100644 index 000000000..8c0705737 Binary files /dev/null and b/retroshare-gui/src/gui/icons/mail/contacts.png differ diff --git a/retroshare-gui/src/gui/icons/mail/delete.png b/retroshare-gui/src/gui/icons/mail/delete.png new file mode 100644 index 000000000..4ed9f4c4a Binary files /dev/null and b/retroshare-gui/src/gui/icons/mail/delete.png differ diff --git a/retroshare-gui/src/gui/icons/mail/downloadall.png b/retroshare-gui/src/gui/icons/mail/downloadall.png new file mode 100644 index 000000000..b4d31d3d0 Binary files /dev/null and b/retroshare-gui/src/gui/icons/mail/downloadall.png differ diff --git a/retroshare-gui/src/gui/icons/mail/filter.png b/retroshare-gui/src/gui/icons/mail/filter.png new file mode 100644 index 000000000..0956d2856 Binary files /dev/null and b/retroshare-gui/src/gui/icons/mail/filter.png differ diff --git a/retroshare-gui/src/gui/icons/mail/filter24.png b/retroshare-gui/src/gui/icons/mail/filter24.png new file mode 100644 index 000000000..d62b224c1 Binary files /dev/null and b/retroshare-gui/src/gui/icons/mail/filter24.png differ diff --git a/retroshare-gui/src/gui/icons/mail/foward.png b/retroshare-gui/src/gui/icons/mail/foward.png new file mode 100644 index 000000000..2895d8e26 Binary files /dev/null and b/retroshare-gui/src/gui/icons/mail/foward.png differ diff --git a/retroshare-gui/src/gui/icons/mail/quote.png b/retroshare-gui/src/gui/icons/mail/quote.png new file mode 100644 index 000000000..9b454de83 Binary files /dev/null and b/retroshare-gui/src/gui/icons/mail/quote.png differ diff --git a/retroshare-gui/src/gui/icons/mail/reply-all.png b/retroshare-gui/src/gui/icons/mail/reply-all.png new file mode 100644 index 000000000..712f76eb8 Binary files /dev/null and b/retroshare-gui/src/gui/icons/mail/reply-all.png differ diff --git a/retroshare-gui/src/gui/icons/mail/reply.png b/retroshare-gui/src/gui/icons/mail/reply.png new file mode 100644 index 000000000..29772167d Binary files /dev/null and b/retroshare-gui/src/gui/icons/mail/reply.png differ diff --git a/retroshare-gui/src/gui/icons/mail/save.png b/retroshare-gui/src/gui/icons/mail/save.png new file mode 100644 index 000000000..d3497a718 Binary files /dev/null and b/retroshare-gui/src/gui/icons/mail/save.png differ diff --git a/retroshare-gui/src/gui/icons/mail/send.png b/retroshare-gui/src/gui/icons/mail/send.png new file mode 100644 index 000000000..973abaef1 Binary files /dev/null and b/retroshare-gui/src/gui/icons/mail/send.png differ diff --git a/retroshare-gui/src/gui/icons/mail/tags.png b/retroshare-gui/src/gui/icons/mail/tags.png new file mode 100644 index 000000000..d02a63025 Binary files /dev/null and b/retroshare-gui/src/gui/icons/mail/tags.png differ diff --git a/retroshare-gui/src/gui/icons/mail/write-mail.png b/retroshare-gui/src/gui/icons/mail/write-mail.png new file mode 100644 index 000000000..a64236d4f Binary files /dev/null and b/retroshare-gui/src/gui/icons/mail/write-mail.png differ diff --git a/retroshare-gui/src/gui/icons/mail_128.png b/retroshare-gui/src/gui/icons/mail_128.png deleted file mode 100644 index 1fe0adacf..000000000 Binary files a/retroshare-gui/src/gui/icons/mail_128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/icons/mail_old_128.png b/retroshare-gui/src/gui/icons/mail_old_128.png deleted file mode 100644 index 217e18128..000000000 Binary files a/retroshare-gui/src/gui/icons/mail_old_128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/icons/mail_red_128.png b/retroshare-gui/src/gui/icons/mail_red_128.png deleted file mode 100644 index 115542019..000000000 Binary files a/retroshare-gui/src/gui/icons/mail_red_128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/icons/newsfeed128.png b/retroshare-gui/src/gui/icons/newsfeed128.png deleted file mode 100644 index 19c256c9f..000000000 Binary files a/retroshare-gui/src/gui/icons/newsfeed128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/icons/notification.png b/retroshare-gui/src/gui/icons/notification.png new file mode 100644 index 000000000..88f837051 Binary files /dev/null and b/retroshare-gui/src/gui/icons/notification.png differ diff --git a/retroshare-gui/src/gui/icons/png/add-identity.png b/retroshare-gui/src/gui/icons/png/add-identity.png new file mode 100644 index 000000000..7eaf0d5c5 Binary files /dev/null and b/retroshare-gui/src/gui/icons/png/add-identity.png differ diff --git a/retroshare-gui/src/gui/icons/png/add-image.png b/retroshare-gui/src/gui/icons/png/add-image.png index 68c176be4..587a67c09 100644 Binary files a/retroshare-gui/src/gui/icons/png/add-image.png and b/retroshare-gui/src/gui/icons/png/add-image.png differ diff --git a/retroshare-gui/src/gui/icons/png/addstickers.png b/retroshare-gui/src/gui/icons/png/addstickers.png index 91c4ee4c3..093a92c28 100644 Binary files a/retroshare-gui/src/gui/icons/png/addstickers.png and b/retroshare-gui/src/gui/icons/png/addstickers.png differ diff --git a/retroshare-gui/src/gui/icons/png/anonymous.png b/retroshare-gui/src/gui/icons/png/anonymous.png new file mode 100644 index 000000000..06d37d941 Binary files /dev/null and b/retroshare-gui/src/gui/icons/png/anonymous.png differ diff --git a/retroshare-gui/src/gui/icons/png/arrow-left.png b/retroshare-gui/src/gui/icons/png/arrow-left.png new file mode 100644 index 000000000..d736eeeab Binary files /dev/null and b/retroshare-gui/src/gui/icons/png/arrow-left.png differ diff --git a/retroshare-gui/src/gui/icons/png/arrow-right.png b/retroshare-gui/src/gui/icons/png/arrow-right.png new file mode 100644 index 000000000..7daac15e4 Binary files /dev/null and b/retroshare-gui/src/gui/icons/png/arrow-right.png differ diff --git a/retroshare-gui/src/gui/icons/png/arrow.png b/retroshare-gui/src/gui/icons/png/arrow.png new file mode 100644 index 000000000..e23853802 Binary files /dev/null and b/retroshare-gui/src/gui/icons/png/arrow.png differ diff --git a/retroshare-gui/src/gui/icons/png/attachements.png b/retroshare-gui/src/gui/icons/png/attachements.png index 1d0941a5d..5fcfd0ae3 100644 Binary files a/retroshare-gui/src/gui/icons/png/attachements.png and b/retroshare-gui/src/gui/icons/png/attachements.png differ diff --git a/retroshare-gui/src/gui/icons/png/dock.png b/retroshare-gui/src/gui/icons/png/dock.png new file mode 100644 index 000000000..2e191961b Binary files /dev/null and b/retroshare-gui/src/gui/icons/png/dock.png differ diff --git a/retroshare-gui/src/gui/icons/png/font.png b/retroshare-gui/src/gui/icons/png/font.png index 3c71acc5f..f9b3dd48c 100644 Binary files a/retroshare-gui/src/gui/icons/png/font.png and b/retroshare-gui/src/gui/icons/png/font.png differ diff --git a/retroshare-gui/src/gui/icons/png/highlight.png b/retroshare-gui/src/gui/icons/png/highlight.png index 93ba8f9af..040a2c330 100644 Binary files a/retroshare-gui/src/gui/icons/png/highlight.png and b/retroshare-gui/src/gui/icons/png/highlight.png differ diff --git a/retroshare-gui/src/gui/icons/png/leave.png b/retroshare-gui/src/gui/icons/png/leave.png index df5af7ced..6cb7c154e 100644 Binary files a/retroshare-gui/src/gui/icons/png/leave.png and b/retroshare-gui/src/gui/icons/png/leave.png differ diff --git a/retroshare-gui/src/gui/icons/png/menu.png b/retroshare-gui/src/gui/icons/png/menu.png new file mode 100644 index 000000000..3de630118 Binary files /dev/null and b/retroshare-gui/src/gui/icons/png/menu.png differ diff --git a/retroshare-gui/src/gui/icons/png/message.png b/retroshare-gui/src/gui/icons/png/message.png index ae9da7e01..da8a3b312 100644 Binary files a/retroshare-gui/src/gui/icons/png/message.png and b/retroshare-gui/src/gui/icons/png/message.png differ diff --git a/retroshare-gui/src/gui/icons/png/next-unread.png b/retroshare-gui/src/gui/icons/png/next-unread.png new file mode 100644 index 000000000..812a12d7d Binary files /dev/null and b/retroshare-gui/src/gui/icons/png/next-unread.png differ diff --git a/retroshare-gui/src/gui/icons/png/photo.png b/retroshare-gui/src/gui/icons/png/photo.png new file mode 100644 index 000000000..90ecc125e Binary files /dev/null and b/retroshare-gui/src/gui/icons/png/photo.png differ diff --git a/retroshare-gui/src/gui/icons/png/profile.png b/retroshare-gui/src/gui/icons/png/profile.png new file mode 100644 index 000000000..833cbbee7 Binary files /dev/null and b/retroshare-gui/src/gui/icons/png/profile.png differ diff --git a/retroshare-gui/src/gui/icons/png/search.png b/retroshare-gui/src/gui/icons/png/search.png index 5cd7be556..e05042694 100644 Binary files a/retroshare-gui/src/gui/icons/png/search.png and b/retroshare-gui/src/gui/icons/png/search.png differ diff --git a/retroshare-gui/src/gui/icons/png/send-message-blocked.png b/retroshare-gui/src/gui/icons/png/send-message-blocked.png index c344e8820..6a68824b1 100644 Binary files a/retroshare-gui/src/gui/icons/png/send-message-blocked.png and b/retroshare-gui/src/gui/icons/png/send-message-blocked.png differ diff --git a/retroshare-gui/src/gui/icons/png/send-message.png b/retroshare-gui/src/gui/icons/png/send-message.png index 4aaece137..7b6895d33 100644 Binary files a/retroshare-gui/src/gui/icons/png/send-message.png and b/retroshare-gui/src/gui/icons/png/send-message.png differ diff --git a/retroshare-gui/src/gui/icons/png/smiley.png b/retroshare-gui/src/gui/icons/png/smiley.png index 12e7f869d..eb6d3292c 100644 Binary files a/retroshare-gui/src/gui/icons/png/smiley.png and b/retroshare-gui/src/gui/icons/png/smiley.png differ diff --git a/retroshare-gui/src/gui/icons/png/undock.png b/retroshare-gui/src/gui/icons/png/undock.png new file mode 100644 index 000000000..10052db9a Binary files /dev/null and b/retroshare-gui/src/gui/icons/png/undock.png differ diff --git a/retroshare-gui/src/gui/icons/png/wiki.png b/retroshare-gui/src/gui/icons/png/wiki.png new file mode 100644 index 000000000..a85ba489a Binary files /dev/null and b/retroshare-gui/src/gui/icons/png/wiki.png differ diff --git a/retroshare-gui/src/gui/icons/posted_128.png b/retroshare-gui/src/gui/icons/posted_128.png deleted file mode 100644 index 94c5a50a5..000000000 Binary files a/retroshare-gui/src/gui/icons/posted_128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/icons/posted_red_128.png b/retroshare-gui/src/gui/icons/posted_red_128.png deleted file mode 100644 index 70125b257..000000000 Binary files a/retroshare-gui/src/gui/icons/posted_red_128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/icons/svg/anonymous.svg b/retroshare-gui/src/gui/icons/svg/anonymous.svg new file mode 100644 index 000000000..ba7dc60a3 --- /dev/null +++ b/retroshare-gui/src/gui/icons/svg/anonymous.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/retroshare-gui/src/gui/icons/svg/attachments.svg b/retroshare-gui/src/gui/icons/svg/attachments.svg new file mode 100644 index 000000000..4aed39265 --- /dev/null +++ b/retroshare-gui/src/gui/icons/svg/attachments.svg @@ -0,0 +1,65 @@ + +image/svg+xml \ No newline at end of file diff --git a/retroshare-gui/src/gui/icons/svg/design.svg b/retroshare-gui/src/gui/icons/svg/design.svg new file mode 100644 index 000000000..267da782f --- /dev/null +++ b/retroshare-gui/src/gui/icons/svg/design.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/retroshare-gui/src/gui/icons/svg/dock.svg b/retroshare-gui/src/gui/icons/svg/dock.svg new file mode 100644 index 000000000..2847da24c --- /dev/null +++ b/retroshare-gui/src/gui/icons/svg/dock.svg @@ -0,0 +1,95 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/retroshare-gui/src/gui/icons/svg/gridlayout.svg b/retroshare-gui/src/gui/icons/svg/gridlayout.svg new file mode 100644 index 000000000..96e7089ec --- /dev/null +++ b/retroshare-gui/src/gui/icons/svg/gridlayout.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/retroshare-gui/src/gui/icons/svg/highlight.svg b/retroshare-gui/src/gui/icons/svg/highlight.svg index 63ceeaae8..c2d5a7406 100644 --- a/retroshare-gui/src/gui/icons/svg/highlight.svg +++ b/retroshare-gui/src/gui/icons/svg/highlight.svg @@ -11,7 +11,7 @@ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" id="svg4155" version="1.1" - inkscape:version="0.91 r13725" + inkscape:version="0.92.4 (5da689c313, 2019-01-14)" xml:space="preserve" width="80" height="80" @@ -19,7 +19,7 @@ sodipodi:docname="highlight.svg">image/svg+xml \ No newline at end of file + style="fill:#039bd5;fill-opacity:1" /> \ No newline at end of file diff --git a/retroshare-gui/src/gui/icons/svg/listlayout.svg b/retroshare-gui/src/gui/icons/svg/listlayout.svg new file mode 100644 index 000000000..153862285 --- /dev/null +++ b/retroshare-gui/src/gui/icons/svg/listlayout.svg @@ -0,0 +1,2 @@ + + diff --git a/retroshare-gui/src/gui/icons/svg/message.svg b/retroshare-gui/src/gui/icons/svg/message.svg index 81fcd8254..21916df36 100644 --- a/retroshare-gui/src/gui/icons/svg/message.svg +++ b/retroshare-gui/src/gui/icons/svg/message.svg @@ -12,18 +12,18 @@ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" id="svg4155" version="1.1" - inkscape:version="0.92.3 (2405546, 2018-03-11)" + inkscape:version="0.92.4 (5da689c313, 2019-01-14)" xml:space="preserve" width="80" height="80" viewBox="0 0 80 80" sodipodi:docname="message.svg" - inkscape:export-filename="C:\Users\Mustermann\Documents\GitHub\RetroShare\retroshare-gui\src\gui\icons\png\message.png" + inkscape:export-filename="C:\Users\ProBook\Documents\GitHub\RetroShare\retroshare-gui\src\gui\icons\png\message.png" inkscape:export-xdpi="153.60001" inkscape:export-ydpi="153.60001">image/svg+xml \ No newline at end of file + style="fill:#039bd5;fill-opacity:1;stroke-width:1.3458221" /> \ No newline at end of file diff --git a/retroshare-gui/src/gui/icons/svg/picture.svg b/retroshare-gui/src/gui/icons/svg/picture.svg new file mode 100644 index 000000000..76712ca1a --- /dev/null +++ b/retroshare-gui/src/gui/icons/svg/picture.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/retroshare-gui/src/gui/icons/svg/profile.svg b/retroshare-gui/src/gui/icons/svg/profile.svg index c915ebd49..dd7d555f2 100644 --- a/retroshare-gui/src/gui/icons/svg/profile.svg +++ b/retroshare-gui/src/gui/icons/svg/profile.svg @@ -11,7 +11,7 @@ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" id="svg4155" version="1.1" - inkscape:version="0.91 r13725" + inkscape:version="0.92.4 (5da689c313, 2019-01-14)" xml:space="preserve" width="80" height="80" @@ -19,7 +19,7 @@ sodipodi:docname="profile.svg">image/svg+xml \ No newline at end of file diff --git a/retroshare-gui/src/gui/icons/svg/ratio-1-1.svg b/retroshare-gui/src/gui/icons/svg/ratio-1-1.svg new file mode 100644 index 000000000..07dd49dc7 --- /dev/null +++ b/retroshare-gui/src/gui/icons/svg/ratio-1-1.svg @@ -0,0 +1,97 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/retroshare-gui/src/gui/icons/svg/ratio-16-9.svg b/retroshare-gui/src/gui/icons/svg/ratio-16-9.svg new file mode 100644 index 000000000..d7e3526c4 --- /dev/null +++ b/retroshare-gui/src/gui/icons/svg/ratio-16-9.svg @@ -0,0 +1,2 @@ + + diff --git a/retroshare-gui/src/gui/icons/svg/ratio-3-4.svg b/retroshare-gui/src/gui/icons/svg/ratio-3-4.svg new file mode 100644 index 000000000..8a94fe380 --- /dev/null +++ b/retroshare-gui/src/gui/icons/svg/ratio-3-4.svg @@ -0,0 +1,2 @@ + + diff --git a/retroshare-gui/src/gui/icons/svg/ratio-auto.svg b/retroshare-gui/src/gui/icons/svg/ratio-auto.svg new file mode 100644 index 000000000..f995555ad --- /dev/null +++ b/retroshare-gui/src/gui/icons/svg/ratio-auto.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/retroshare-gui/src/gui/icons/svg/search.svg b/retroshare-gui/src/gui/icons/svg/search.svg index 811707601..f1acaa561 100644 --- a/retroshare-gui/src/gui/icons/svg/search.svg +++ b/retroshare-gui/src/gui/icons/svg/search.svg @@ -11,12 +11,15 @@ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" id="svg4925" version="1.1" - inkscape:version="0.91 r13725" + inkscape:version="0.92.4 (5da689c313, 2019-01-14)" xml:space="preserve" width="80" height="80" viewBox="0 0 80 80" - sodipodi:docname="search.svg">image/svg+xml \ No newline at end of file + style="fill:#039bd5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.43466902" + d="m 26.664851,59.401639 c -5.582296,0 -10.830601,-2.174527 -14.777662,-6.121445 -8.1483473,-8.148489 -8.1483473,-21.406839 0,-29.555327 3.947061,-3.947635 9.195366,-6.121446 14.777662,-6.121446 5.582441,0 10.830747,2.173811 14.777665,6.121446 8.148488,8.148488 8.148488,21.406838 0,29.555327 -3.946918,3.946918 -9.195224,6.121445 -14.777665,6.121445 z m 0,-5.738675 c 3.879776,0 7.759551,-1.480721 10.719703,-4.440874 5.920879,-5.920162 5.920879,-15.518958 0,-21.439263 -2.960152,-2.960726 -6.839927,-4.440731 -10.719703,-4.440731 -3.879774,0 -7.75955,1.480005 -10.719558,4.440731 -5.920879,5.920305 -5.920879,15.519101 0,21.439263 2.960008,2.960153 6.839784,4.440874 10.719558,4.440874" /> \ No newline at end of file diff --git a/retroshare-gui/src/gui/icons/svg/undock.svg b/retroshare-gui/src/gui/icons/svg/undock.svg new file mode 100644 index 000000000..1c8d25c50 --- /dev/null +++ b/retroshare-gui/src/gui/icons/svg/undock.svg @@ -0,0 +1,95 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/retroshare-gui/src/gui/icons/svg/video-camera.svg b/retroshare-gui/src/gui/icons/svg/video-camera.svg new file mode 100644 index 000000000..81d7d9333 --- /dev/null +++ b/retroshare-gui/src/gui/icons/svg/video-camera.svg @@ -0,0 +1,114 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/retroshare-gui/src/gui/icons/svg/wiki.svg b/retroshare-gui/src/gui/icons/svg/wiki.svg new file mode 100644 index 000000000..ed4ca738e --- /dev/null +++ b/retroshare-gui/src/gui/icons/svg/wiki.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/retroshare-gui/src/gui/icons/textedit/align.png b/retroshare-gui/src/gui/icons/textedit/align.png new file mode 100644 index 000000000..df3e4b3e3 Binary files /dev/null and b/retroshare-gui/src/gui/icons/textedit/align.png differ diff --git a/retroshare-gui/src/gui/icons/textedit/code.png b/retroshare-gui/src/gui/icons/textedit/code.png new file mode 100644 index 000000000..a6a89266e Binary files /dev/null and b/retroshare-gui/src/gui/icons/textedit/code.png differ diff --git a/retroshare-gui/src/gui/icons/textedit/font-decrease.png b/retroshare-gui/src/gui/icons/textedit/font-decrease.png new file mode 100644 index 000000000..8c01ee1a3 Binary files /dev/null and b/retroshare-gui/src/gui/icons/textedit/font-decrease.png differ diff --git a/retroshare-gui/src/gui/icons/textedit/font-increase.png b/retroshare-gui/src/gui/icons/textedit/font-increase.png new file mode 100644 index 000000000..495444546 Binary files /dev/null and b/retroshare-gui/src/gui/icons/textedit/font-increase.png differ diff --git a/retroshare-gui/src/gui/icons/textedit/smile.png b/retroshare-gui/src/gui/icons/textedit/smile.png new file mode 100644 index 000000000..38b3e54eb Binary files /dev/null and b/retroshare-gui/src/gui/icons/textedit/smile.png differ diff --git a/retroshare-gui/src/gui/icons/wire-circle.png b/retroshare-gui/src/gui/icons/wire-circle.png new file mode 100644 index 000000000..128159150 Binary files /dev/null and b/retroshare-gui/src/gui/icons/wire-circle.png differ diff --git a/retroshare-gui/src/gui/icons/wire.png b/retroshare-gui/src/gui/icons/wire.png new file mode 100644 index 000000000..ed0c82334 Binary files /dev/null and b/retroshare-gui/src/gui/icons/wire.png differ diff --git a/retroshare-gui/src/gui/im_history/ImHistoryBrowser.cpp b/retroshare-gui/src/gui/im_history/ImHistoryBrowser.cpp index 4615c06e4..70b5b4b0d 100644 --- a/retroshare-gui/src/gui/im_history/ImHistoryBrowser.cpp +++ b/retroshare-gui/src/gui/im_history/ImHistoryBrowser.cpp @@ -32,6 +32,7 @@ #include "IMHistoryItemDelegate.h" #include "IMHistoryItemPainter.h" #include "util/HandleRichText.h" +#include "gui/common/FilesDefs.h" #include "rshare.h" #include @@ -96,7 +97,7 @@ ImHistoryBrowser::ImHistoryBrowser(const ChatId &chatId, QTextEdit *edit, QWidge /* Invoke Qt Designer generated QObject setup routine */ ui.setupUi(this); - ui.headerFrame->setHeaderImage(QPixmap(":/images/user/agt_forum64.png")); + ui.headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/images/user/agt_forum64.png")); ui.headerFrame->setHeaderText(tr("Message History")); m_chatId = chatId; @@ -418,16 +419,23 @@ void ImHistoryBrowser::customContextMenuRequested(QPoint /*pos*/) void ImHistoryBrowser::copyMessage() { - QListWidgetItem *currentItem = ui.listWidget->currentItem(); - if (currentItem) { - uint32_t msgId = currentItem->data(ROLE_MSGID).toString().toInt(); - HistoryMsg msg; - if (rsHistory->getMessage(msgId, msg)) { - QTextDocument doc; - doc.setHtml(QString::fromUtf8(msg.message.c_str())); - QApplication::clipboard()->setText(doc.toPlainText()); - } - } + QListWidgetItem *currentItem = ui.listWidget->currentItem(); + if (currentItem) { + uint32_t msgId = currentItem->data(ROLE_MSGID).toString().toInt(); + HistoryMsg msg; + if (rsHistory->getMessage(msgId, msg)) { + RsIdentityDetails details; + QString name = (rsIdentity->getIdDetails(RsGxsId(msg.peerName), details)) + ? QString::fromUtf8(details.mNickname.c_str()) + : QString::fromUtf8(msg.peerName.c_str()); + QDateTime date = msg.incoming + ? QDateTime::fromTime_t(msg.sendTime) + : QDateTime::fromTime_t(msg.recvTime); + QTextDocument doc; + doc.setHtml(QString::fromUtf8(msg.message.c_str())); + QApplication::clipboard()->setText("> " + date.toString() + " " + name + ":" + doc.toPlainText()); + } + } } void ImHistoryBrowser::removeMessages() diff --git a/retroshare-gui/src/gui/images.qrc b/retroshare-gui/src/gui/images.qrc index a54d7fa26..248c5e1fd 100644 --- a/retroshare-gui/src/gui/images.qrc +++ b/retroshare-gui/src/gui/images.qrc @@ -10,8 +10,6 @@ images/switch01.png images/switch10.png images/switch11.png - images/add_chat24.png - images/RSS_004_32.png images/mail-encrypted-full.png images/mail-signed-full.png images/document-edit-sign.png @@ -19,8 +17,6 @@ images/inbox_22.png images/blue_lock.png images/blue_lock_open.png - images/d-chat64.png - images/edit_24.png images/streaming.png images/white-bubble-64.png images/orange-bubble-64.png @@ -33,7 +29,6 @@ images/browsable_128_blue.png images/blank_128_green.png images/blank_128_blue.png - images/door_in.png images/tor-logo.png images/help/addafriend.png images/help/addfriendkey.png @@ -41,50 +36,28 @@ icons/help_64.png images/help/natgreen.png images/help/natred.png - images/attach.png images/pgp.png images/rs_wizard.png images/avatar_request.png images/avatar_refused.png images/avatar_request_unknown.png - images/backblue.png - images/backchat.png images/buttonframe.png - images/btn1.png - images/btn2.png - images/btn3.png - images/btn4.png - images/btn_26.png - images/btn_26_hover.png - images/btn_26_pressed.png - images/btn_27.png - images/btn_27_hover.png images/btn_blue.png images/btn_blue_hover.png - images/btn_green.png - images/btn_green_hover.png - images/btn_green_pressed.png images/connect_creating.png images/dht16.png images/dht32.png images/edit-clear-history.png - images/edit_16.png images/feedback_arrow.png images/feedback_arrow_hover.png images/firewall_24.png images/list_bullet_arrow.png images/rc_combined.png images/combobox_arrow.png - images/arrow-left.png - images/arrow-right.png help/authors.html help/licence.html help/thanks.html images/accepted16.png - images/add_image24.png - images/add_channel64.png - images/add_channel24.png - images/add_channel32.png images/admin-16.png images/admin-32.png images/admin-24.png @@ -104,30 +77,7 @@ images/avatarstatus-bg-away-70.png images/avatarstatus-bg-busy-70.png images/avatarstatus-bg-offline-70.png - images/browse-looking.gif - images/back.png - images/backgroundl.png - images/blockdevice.png - images/blockdevice2.png - images/configure.png - images/channels.png - images/turtle.png - images/channels16.png - images/channels24.png - images/channels32.png - images/channels_new.png - images/channelsred.png - images/channelsyellow.png - images/channelsgreen.png - images/channelsblue.png images/copyrslink.png - images/contacts24.png - images/contactsclosed24.png - images/connection.png - images/contact_new.png - images/contact_new22.png - images/contact.png - images/contact22.png images/Client0.png images/Client1.png images/Client2.png @@ -146,8 +96,6 @@ images/FileTypePicture.png images/FileTypeProgram.png images/FileTypeVideo.png - images/fonts.png - images/folder16.png images/foldermail.png images/folderopen.png images/fileopen.png @@ -156,58 +104,25 @@ images/graph-notdownload.png images/graph-checking.png images/graph-blue.png - images/add-share24.png - images/add_24x24.png - images/advsearch_24x24.png - images/amsn16.png - images/attachment.png images/avatar_background.png - images/backgroundimage.png images/button_cancel.png - images/calendar.png images/chat.png - images/chat_22.png - images/chat_24.png - images/chat_32.png - images/chat_64.png - images/chat_x24.png - images/chat_red24.png images/cancel.png images/close-down.png - images/close_normal.png - images/console-small-down.png - images/console-small-hover.png - images/console-small-up.png - images/contact_new128.png images/copy.png images/delete.png images/deleteall.png - images/deletemail-pressed.png - images/deletemail24.png images/directoryadd_24x24_shadow.png images/directoryremove_24x24_shadow.png images/directoryselect_24x24_shadow.png images/diskSave.png images/document_save.png images/down.png - images/down_24x24.png images/download.png - images/download16.png images/edit_add24.png - images/edit-bold.png - images/edit-italic.png - images/edit-underline.png images/edit_remove24.png - images/encrypted16.png - images/encrypted22.png - images/encrypted32.png - images/encrypted48.png images/disabled_plugin_48.png - images/evolution.png - images/exit_24x24.png - images/exit_32.png images/expand_frame.png - images/exportpeers_16x16.png images/extension_32.png images/filefind.png images/fileinfo.png @@ -227,7 +142,6 @@ images/fileshare64.png images/find.png images/find-16.png - images/emoticons/kopete/kopete020.png images/flags/af.png images/flags/bg.png images/flags/zh_CN.png @@ -261,41 +175,22 @@ images/folder-outbox.png images/folder-sent.png images/folder-trash.png - images/folder_doments.png - images/folder_green.png - images/folder_red.png - images/folder_grey.png - images/folder_yellow.png - images/folder_open.png - images/folder_video.png - images/folder_blueshared.png - images/forward.png - images/friendsfolder24.png - images/kgames.png images/go-down.png images/go-top.png images/go-up.png images/go-bottom.png images/graph-area.png images/graph-line.png - images/groupchat.png - images/genbackground.png - images/gohome.png images/gpgp_key_generate.png - images/help.png - images/help24.png images/hide_toolbox_frame.png images/hide_frame.png images/highlight.png - images/hi16-app-ktorrent.png - images/hi24-app-ktorrent.png images/hot_0.png images/hot_1.png images/hot_2.png images/hot_3.png images/hot_4.png images/hot_5.png - images/image16.png images/imageblocked_24.png images/info16.png images/im-user.png @@ -304,40 +199,16 @@ images/im-user-busy.png images/im-user-inactive.png images/informations_24x24.png + images/junk.png + images/junk_on.png + images/junk_off.png images/connect_friend.png images/kalarm.png images/kbackgammon.png - images/kblogger.png - images/hi48-app-kblogger.png - images/hi64-app-kblogger.png - images/transfers_new.png images/kcmsystem24.png - images/kdmconfig.png - images/konsole.png - images/newsfeed128.png - images/newsfeed128_notify.png - images/konversation.png - images/konversation16.png - images/konversation128.png - images/konv_message2.png - images/konv_message3.png - images/konv_message64.png - images/konversation64.png - images/forums_new.png - images/ksysguard.png - images/ksysguard32.png - images/ktorrent.png - images/ktorrent32.png - images/knewsticker24.png - images/library.png - images/loadcert16.png images/ledoff1.png images/ledon1.png images/locale.png - images/looknfeel.png - images/lphoto.png - images/lphoto16.png - images/lphoto24.png images/logo/logo_16.png images/logo/logo_24.png images/logo/logo_24_0.png @@ -352,21 +223,8 @@ images/logo/logo_info.png images/logo/logo_splash.png images/logo/logo_web_nobackground.png - images/logobar/logo_bar_fill.png - images/logobar/logo_bar_start.png - images/logobar/rslogo.png - images/logobar/rslogo2.png - images/mail-message-new.png images/mail-signed.png images/mail-signature-unknown.png - images/mail_delete.png - images/mail_get.png - images/mail_reply.png - images/mail_replyall.png - images/mail_forward.png - images/mail_send.png - images/mail_new.png - images/mail_send24.png images/mailforward24-hover.png images/message-mail.png images/message-mail-read.png @@ -382,19 +240,10 @@ images/message-state-header.png images/message-state-new.png images/message-news.png - images/message_new.png images/message.png - images/messenger.png images/mute-off-16.png images/mute-on-16.png - images/my_documents_16.png - images/my_documents_22.png - images/network.png - images/network16.png - images/network32.png images/new-mail-alert.png - images/new_forum16.png - images/newmsg.png images/no_avatar_70.png images/no_avatar_background.png images/openimage.png @@ -403,7 +252,6 @@ images/pasterslink.png images/package_games1.png images/pin32.png - images/print24.png images/priorityauto.png images/priorityhigh.png images/prioritylow.png @@ -412,17 +260,10 @@ images/preview.png images/player_play.png images/quick_restart24.png - images/quote_24.png images/redled.png images/greenled.png images/grayled.png images/yellowled.png - images/rate-1.png - images/rate-2.png - images/rate-3.png - images/rate-4.png - images/rate-5.png - images/rating.png images/records.png images/replymail-pressed.png images/replymail24.png @@ -439,7 +280,6 @@ images/save24.png images/send24.png images/settings.png - images/settings16.png images/show_toolbox_frame.png images/start.png images/stop.png @@ -449,20 +289,15 @@ images/StatisticsDetail.png images/status_unknown.png images/startall.png - images/server_24x24.png images/sort_incr.png images/sort_decrease.png images/sound.png images/tab-dock.png images/tab-undock.png images/tab-new.png - images/tag24.png images/transferupdown.png images/tools_wizard.png - images/typing.png - images/trustsettings.png images/uploads.png - images/headerFrame.png images/loader/indicator-16.gif images/loader/indicator-32.gif images/loader/circleball-16.gif @@ -473,44 +308,6 @@ images/mimetypes/source_c.png images/mimetypes/source_cpp.png images/mimetypes/source_h.png - images/view-certificate-copy-32.png - images/view-certificate-export-32.png - images/textedit/textbold.png - images/textedit/textitalic.png - images/textedit/textunder.png - images/textedit/textjustify.png - images/textedit/textcenter.png - images/textedit/textleft.png - images/textedit/textright.png - images/textedit/editcopy.png - images/textedit/editcut.png - images/textedit/editpaste.png - images/textedit/editredo.png - images/textedit/editundo.png - images/textedit/edit-image-face-add.png - images/textedit/exportpdf.png - images/textedit/filenew.png - images/textedit/fileopen.png - images/textedit/fileprint.png - images/textedit/filesave.png - images/textedit/format-text-color.png - images/textedit/format-text-strikethrough.png - images/textedit/format-indent-less.png - images/textedit/format-indent-more.png - images/textedit/insert-link.png - images/textedit/format-list-ordered.png - images/textedit/format-list-unordered.png - images/textedit/zoomin.png - images/textedit/zoomout.png - images/textedit/format_font_size_more.png - images/textedit/format_font_size_less.png - images/textedit/hi22-action-format-text-blockquote.png - images/textedit/hi22-action-format-text-code.png - images/textedit/hi22-action-insert-more-mark.png - images/toaster/chat.png - images/toaster/hangup.png - images/toaster/pickup.png - images/toaster/backgroundtoaster.png images/thumb-default-video.png images/user/add_user24.png images/user/add_user48.png @@ -556,24 +353,11 @@ images/up1down0.png images/underconstruction.png images/user.png - images/view-certificate-sign-32.png images/view_calendar_day.png images/view_calendar_week.png images/view_calendar_month.png images/view_calendar_list.png images/view_split_top_bottom.png - images/vote_up.png - images/vote_down.png - images/vote_neutral.png - images/window_fullscreen.png - images/window_nofullscreen.png - images/identity/identities_32.png - images/identity/identity_64.png - images/identity/identity_create_32.png - images/identity/identity_create_64.png - images/identity/identity_delete_32.png - images/identity/identity_edit_32.png - images/identity/identity_edit_64.png qss/chat/standard/private/info.xml qss/chat/standard/private/incoming.htm qss/chat/standard/private/outgoing.htm @@ -633,43 +417,24 @@ qss/chat/compact/history/variants/Colored.css qss/stylesheet/qss.default qss/stylesheet/Standard.qss - images/connect/connectFriendWatermark.png - images/connect/connectFriendLogo.png - images/connect/connectFriendBanner.png - images/connect/connectFriendBanner1.png - images/connect/info16.png - images/connect/mail_send.png images/tags/pgp-known.png images/tags/pgp-unknown.png - images/tags/anon.png images/tags/dev-ambassador.png images/tags/dev-translator.png images/tags/dev-patcher.png images/tags/developer.png - images/circles/circles_32.png - images/circles/circles_64.png - images/newsfeed/news-feed-32.png - images/newsfeed/news-feed-notify-32.png images/share-icon-16.png - images/view-certificate-sign-48.png images/toasterEnable.png images/toasterDisable.png - images/library_edit.png - images/library_view.png - images/library_add.png - images/library64.png - images/library16.png - images/btn_red_pressed.png - images/btn_red_hover.png - images/btn_red.png - images/view-feeds.png - images/view-files.png images/emblem-web.png - images/rsmessenger16.png - images/rsmessenger32.png images/rsmessenger48.png images/SmileyText.png images/SimpleText.png images/ColoredText.png + images/update.png + images/folder_green.png + images/folder_red.png + images/folder_grey.png + images/folder_yellow.png diff --git a/retroshare-gui/src/gui/images/64px_help.png b/retroshare-gui/src/gui/images/64px_help.png deleted file mode 100644 index f58d478a7..000000000 Binary files a/retroshare-gui/src/gui/images/64px_help.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/Eterm.png b/retroshare-gui/src/gui/images/Eterm.png deleted file mode 100644 index b2027f5a9..000000000 Binary files a/retroshare-gui/src/gui/images/Eterm.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/Login Manager.png b/retroshare-gui/src/gui/images/Login Manager.png deleted file mode 100644 index 37371456c..000000000 Binary files a/retroshare-gui/src/gui/images/Login Manager.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/RSS_004_32.png b/retroshare-gui/src/gui/images/RSS_004_32.png deleted file mode 100644 index c6abd93dc..000000000 Binary files a/retroshare-gui/src/gui/images/RSS_004_32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/add-share24.png b/retroshare-gui/src/gui/images/add-share24.png deleted file mode 100644 index 031542f51..000000000 Binary files a/retroshare-gui/src/gui/images/add-share24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/add_24x24.png b/retroshare-gui/src/gui/images/add_24x24.png deleted file mode 100644 index bbf64ca18..000000000 Binary files a/retroshare-gui/src/gui/images/add_24x24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/add_channel24.png b/retroshare-gui/src/gui/images/add_channel24.png deleted file mode 100644 index 8ba048f74..000000000 Binary files a/retroshare-gui/src/gui/images/add_channel24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/add_channel32.png b/retroshare-gui/src/gui/images/add_channel32.png deleted file mode 100644 index 62d178e11..000000000 Binary files a/retroshare-gui/src/gui/images/add_channel32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/add_channel64.png b/retroshare-gui/src/gui/images/add_channel64.png deleted file mode 100644 index ebc17374c..000000000 Binary files a/retroshare-gui/src/gui/images/add_channel64.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/add_chat24.png b/retroshare-gui/src/gui/images/add_chat24.png deleted file mode 100644 index 95585c957..000000000 Binary files a/retroshare-gui/src/gui/images/add_chat24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/add_image24.png b/retroshare-gui/src/gui/images/add_image24.png deleted file mode 100644 index 396035fe9..000000000 Binary files a/retroshare-gui/src/gui/images/add_image24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/advsearch_24x24.png b/retroshare-gui/src/gui/images/advsearch_24x24.png deleted file mode 100644 index 94182e8fe..000000000 Binary files a/retroshare-gui/src/gui/images/advsearch_24x24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/amsn16.png b/retroshare-gui/src/gui/images/amsn16.png deleted file mode 100644 index 54edfb50e..000000000 Binary files a/retroshare-gui/src/gui/images/amsn16.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/arrow-left.png b/retroshare-gui/src/gui/images/arrow-left.png deleted file mode 100644 index 6a29679ae..000000000 Binary files a/retroshare-gui/src/gui/images/arrow-left.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/arrow-right.png b/retroshare-gui/src/gui/images/arrow-right.png deleted file mode 100644 index 207f7fcf2..000000000 Binary files a/retroshare-gui/src/gui/images/arrow-right.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/attach.png b/retroshare-gui/src/gui/images/attach.png deleted file mode 100644 index 53f31750b..000000000 Binary files a/retroshare-gui/src/gui/images/attach.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/attachment.png b/retroshare-gui/src/gui/images/attachment.png deleted file mode 100644 index 89842af21..000000000 Binary files a/retroshare-gui/src/gui/images/attachment.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/back.png b/retroshare-gui/src/gui/images/back.png deleted file mode 100644 index 78dcc7011..000000000 Binary files a/retroshare-gui/src/gui/images/back.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/backblue.png b/retroshare-gui/src/gui/images/backblue.png deleted file mode 100644 index 8ca7d7745..000000000 Binary files a/retroshare-gui/src/gui/images/backblue.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/backchat.png b/retroshare-gui/src/gui/images/backchat.png deleted file mode 100644 index 6ea9c9f49..000000000 Binary files a/retroshare-gui/src/gui/images/backchat.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/backgroundimage.png b/retroshare-gui/src/gui/images/backgroundimage.png deleted file mode 100644 index f67e5176f..000000000 Binary files a/retroshare-gui/src/gui/images/backgroundimage.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/backgroundl.png b/retroshare-gui/src/gui/images/backgroundl.png deleted file mode 100644 index 13d166dbe..000000000 Binary files a/retroshare-gui/src/gui/images/backgroundl.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/blockdevice.png b/retroshare-gui/src/gui/images/blockdevice.png deleted file mode 100644 index 77584164f..000000000 Binary files a/retroshare-gui/src/gui/images/blockdevice.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/blockdevice2.png b/retroshare-gui/src/gui/images/blockdevice2.png deleted file mode 100644 index 312d63e40..000000000 Binary files a/retroshare-gui/src/gui/images/blockdevice2.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/browse-looking.gif b/retroshare-gui/src/gui/images/browse-looking.gif deleted file mode 100644 index 6da15f60d..000000000 Binary files a/retroshare-gui/src/gui/images/browse-looking.gif and /dev/null differ diff --git a/retroshare-gui/src/gui/images/btn1.png b/retroshare-gui/src/gui/images/btn1.png deleted file mode 100644 index bbe3b7278..000000000 Binary files a/retroshare-gui/src/gui/images/btn1.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/btn2.png b/retroshare-gui/src/gui/images/btn2.png deleted file mode 100644 index 92100d787..000000000 Binary files a/retroshare-gui/src/gui/images/btn2.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/btn3.png b/retroshare-gui/src/gui/images/btn3.png deleted file mode 100644 index bbe3b7278..000000000 Binary files a/retroshare-gui/src/gui/images/btn3.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/btn4.png b/retroshare-gui/src/gui/images/btn4.png deleted file mode 100644 index bbe3b7278..000000000 Binary files a/retroshare-gui/src/gui/images/btn4.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/btn_26.png b/retroshare-gui/src/gui/images/btn_26.png deleted file mode 100644 index 51de8bc89..000000000 Binary files a/retroshare-gui/src/gui/images/btn_26.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/btn_26_hover.png b/retroshare-gui/src/gui/images/btn_26_hover.png deleted file mode 100644 index 391690431..000000000 Binary files a/retroshare-gui/src/gui/images/btn_26_hover.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/btn_26_pressed.png b/retroshare-gui/src/gui/images/btn_26_pressed.png deleted file mode 100644 index 0aa53120d..000000000 Binary files a/retroshare-gui/src/gui/images/btn_26_pressed.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/btn_27.png b/retroshare-gui/src/gui/images/btn_27.png deleted file mode 100644 index bcdf90ded..000000000 Binary files a/retroshare-gui/src/gui/images/btn_27.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/btn_27_hover.png b/retroshare-gui/src/gui/images/btn_27_hover.png deleted file mode 100644 index 2bc8680d7..000000000 Binary files a/retroshare-gui/src/gui/images/btn_27_hover.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/btn_green.png b/retroshare-gui/src/gui/images/btn_green.png deleted file mode 100644 index 3c5526233..000000000 Binary files a/retroshare-gui/src/gui/images/btn_green.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/btn_green_hover.png b/retroshare-gui/src/gui/images/btn_green_hover.png deleted file mode 100644 index bb98ebced..000000000 Binary files a/retroshare-gui/src/gui/images/btn_green_hover.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/btn_green_pressed.png b/retroshare-gui/src/gui/images/btn_green_pressed.png deleted file mode 100644 index 84644b7c4..000000000 Binary files a/retroshare-gui/src/gui/images/btn_green_pressed.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/btn_red.png b/retroshare-gui/src/gui/images/btn_red.png deleted file mode 100644 index 81cad49f5..000000000 Binary files a/retroshare-gui/src/gui/images/btn_red.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/btn_red_hover.png b/retroshare-gui/src/gui/images/btn_red_hover.png deleted file mode 100644 index 92b724c72..000000000 Binary files a/retroshare-gui/src/gui/images/btn_red_hover.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/btn_red_pressed.png b/retroshare-gui/src/gui/images/btn_red_pressed.png deleted file mode 100644 index 604c96d68..000000000 Binary files a/retroshare-gui/src/gui/images/btn_red_pressed.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/calendar.png b/retroshare-gui/src/gui/images/calendar.png deleted file mode 100644 index e91b13d73..000000000 Binary files a/retroshare-gui/src/gui/images/calendar.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/channels.png b/retroshare-gui/src/gui/images/channels.png deleted file mode 100644 index 3c53997a0..000000000 Binary files a/retroshare-gui/src/gui/images/channels.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/channels16.png b/retroshare-gui/src/gui/images/channels16.png deleted file mode 100644 index e84292c1f..000000000 Binary files a/retroshare-gui/src/gui/images/channels16.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/channels24.png b/retroshare-gui/src/gui/images/channels24.png deleted file mode 100644 index 9eb351ae7..000000000 Binary files a/retroshare-gui/src/gui/images/channels24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/channels32.png b/retroshare-gui/src/gui/images/channels32.png deleted file mode 100644 index ddb19884e..000000000 Binary files a/retroshare-gui/src/gui/images/channels32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/channels_new.png b/retroshare-gui/src/gui/images/channels_new.png deleted file mode 100644 index aa8d7a889..000000000 Binary files a/retroshare-gui/src/gui/images/channels_new.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/channelsblue.png b/retroshare-gui/src/gui/images/channelsblue.png deleted file mode 100644 index c385de26b..000000000 Binary files a/retroshare-gui/src/gui/images/channelsblue.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/channelsgreen.png b/retroshare-gui/src/gui/images/channelsgreen.png deleted file mode 100644 index 8649075b3..000000000 Binary files a/retroshare-gui/src/gui/images/channelsgreen.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/channelsred.png b/retroshare-gui/src/gui/images/channelsred.png deleted file mode 100644 index 5d4e8a35f..000000000 Binary files a/retroshare-gui/src/gui/images/channelsred.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/channelsyellow.png b/retroshare-gui/src/gui/images/channelsyellow.png deleted file mode 100644 index f6dab53cf..000000000 Binary files a/retroshare-gui/src/gui/images/channelsyellow.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/chat_128.png b/retroshare-gui/src/gui/images/chat_128.png deleted file mode 100644 index 7212a977d..000000000 Binary files a/retroshare-gui/src/gui/images/chat_128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/chat_22.png b/retroshare-gui/src/gui/images/chat_22.png deleted file mode 100644 index 16d6336f6..000000000 Binary files a/retroshare-gui/src/gui/images/chat_22.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/chat_24.png b/retroshare-gui/src/gui/images/chat_24.png deleted file mode 100644 index 3a791ba17..000000000 Binary files a/retroshare-gui/src/gui/images/chat_24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/chat_32.png b/retroshare-gui/src/gui/images/chat_32.png deleted file mode 100644 index f9c6f2496..000000000 Binary files a/retroshare-gui/src/gui/images/chat_32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/chat_48.png b/retroshare-gui/src/gui/images/chat_48.png deleted file mode 100644 index 4c06659f4..000000000 Binary files a/retroshare-gui/src/gui/images/chat_48.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/chat_64.png b/retroshare-gui/src/gui/images/chat_64.png deleted file mode 100644 index ef6dc9613..000000000 Binary files a/retroshare-gui/src/gui/images/chat_64.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/chat_red24.png b/retroshare-gui/src/gui/images/chat_red24.png deleted file mode 100644 index 2384d7ae7..000000000 Binary files a/retroshare-gui/src/gui/images/chat_red24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/chat_x24.png b/retroshare-gui/src/gui/images/chat_x24.png deleted file mode 100644 index 163d0d371..000000000 Binary files a/retroshare-gui/src/gui/images/chat_x24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/circles/circles-hi.png b/retroshare-gui/src/gui/images/circles/circles-hi.png deleted file mode 100644 index 539a8f203..000000000 Binary files a/retroshare-gui/src/gui/images/circles/circles-hi.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/circles/circles_128.png b/retroshare-gui/src/gui/images/circles/circles_128.png deleted file mode 100644 index d2664a82c..000000000 Binary files a/retroshare-gui/src/gui/images/circles/circles_128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/circles/circles_32.png b/retroshare-gui/src/gui/images/circles/circles_32.png deleted file mode 100644 index 97145faa9..000000000 Binary files a/retroshare-gui/src/gui/images/circles/circles_32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/circles/circles_64.png b/retroshare-gui/src/gui/images/circles/circles_64.png deleted file mode 100644 index 4bf54616d..000000000 Binary files a/retroshare-gui/src/gui/images/circles/circles_64.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/close_normal.png b/retroshare-gui/src/gui/images/close_normal.png deleted file mode 100644 index fb510fbea..000000000 Binary files a/retroshare-gui/src/gui/images/close_normal.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/configure.png b/retroshare-gui/src/gui/images/configure.png deleted file mode 100644 index 45b8fae8b..000000000 Binary files a/retroshare-gui/src/gui/images/configure.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/connect/connectFriendBanner.png b/retroshare-gui/src/gui/images/connect/connectFriendBanner.png deleted file mode 100644 index 94f6acfcf..000000000 Binary files a/retroshare-gui/src/gui/images/connect/connectFriendBanner.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/connect/connectFriendBanner1.png b/retroshare-gui/src/gui/images/connect/connectFriendBanner1.png deleted file mode 100644 index b38760044..000000000 Binary files a/retroshare-gui/src/gui/images/connect/connectFriendBanner1.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/connect/connectFriendLogo.png b/retroshare-gui/src/gui/images/connect/connectFriendLogo.png deleted file mode 100644 index d5ac0af38..000000000 Binary files a/retroshare-gui/src/gui/images/connect/connectFriendLogo.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/connect/connectFriendLogo2.png b/retroshare-gui/src/gui/images/connect/connectFriendLogo2.png deleted file mode 100644 index b4bacbdff..000000000 Binary files a/retroshare-gui/src/gui/images/connect/connectFriendLogo2.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/connect/connectFriendWatermark.png b/retroshare-gui/src/gui/images/connect/connectFriendWatermark.png deleted file mode 100644 index d68db6277..000000000 Binary files a/retroshare-gui/src/gui/images/connect/connectFriendWatermark.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/connect/info16.png b/retroshare-gui/src/gui/images/connect/info16.png deleted file mode 100644 index fb3b689e9..000000000 Binary files a/retroshare-gui/src/gui/images/connect/info16.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/connect/mail_send.png b/retroshare-gui/src/gui/images/connect/mail_send.png deleted file mode 100644 index 0366228e9..000000000 Binary files a/retroshare-gui/src/gui/images/connect/mail_send.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/connection.png b/retroshare-gui/src/gui/images/connection.png deleted file mode 100644 index e22cb6c7c..000000000 Binary files a/retroshare-gui/src/gui/images/connection.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/console-big-down.png b/retroshare-gui/src/gui/images/console-big-down.png deleted file mode 100644 index 4ffbbcf84..000000000 Binary files a/retroshare-gui/src/gui/images/console-big-down.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/console-big-hover.png b/retroshare-gui/src/gui/images/console-big-hover.png deleted file mode 100644 index a5163017f..000000000 Binary files a/retroshare-gui/src/gui/images/console-big-hover.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/console-big-up.png b/retroshare-gui/src/gui/images/console-big-up.png deleted file mode 100644 index c88112c81..000000000 Binary files a/retroshare-gui/src/gui/images/console-big-up.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/console-small-down.png b/retroshare-gui/src/gui/images/console-small-down.png deleted file mode 100644 index 4c5fa594f..000000000 Binary files a/retroshare-gui/src/gui/images/console-small-down.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/console-small-hover.png b/retroshare-gui/src/gui/images/console-small-hover.png deleted file mode 100644 index 9dfb61454..000000000 Binary files a/retroshare-gui/src/gui/images/console-small-hover.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/console-small-up.png b/retroshare-gui/src/gui/images/console-small-up.png deleted file mode 100644 index 551d3f923..000000000 Binary files a/retroshare-gui/src/gui/images/console-small-up.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/contact.png b/retroshare-gui/src/gui/images/contact.png deleted file mode 100644 index b86f35012..000000000 Binary files a/retroshare-gui/src/gui/images/contact.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/contact22.png b/retroshare-gui/src/gui/images/contact22.png deleted file mode 100644 index 2bde2b914..000000000 Binary files a/retroshare-gui/src/gui/images/contact22.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/contact_new.png b/retroshare-gui/src/gui/images/contact_new.png deleted file mode 100644 index 0a915b51b..000000000 Binary files a/retroshare-gui/src/gui/images/contact_new.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/contact_new128.png b/retroshare-gui/src/gui/images/contact_new128.png deleted file mode 100644 index ff60d9b48..000000000 Binary files a/retroshare-gui/src/gui/images/contact_new128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/contact_new22.png b/retroshare-gui/src/gui/images/contact_new22.png deleted file mode 100644 index c4f7eb816..000000000 Binary files a/retroshare-gui/src/gui/images/contact_new22.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/contacts24.png b/retroshare-gui/src/gui/images/contacts24.png deleted file mode 100644 index 979c51ecb..000000000 Binary files a/retroshare-gui/src/gui/images/contacts24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/contactsclosed24.png b/retroshare-gui/src/gui/images/contactsclosed24.png deleted file mode 100644 index 4698bb08a..000000000 Binary files a/retroshare-gui/src/gui/images/contactsclosed24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/d-chat64.png b/retroshare-gui/src/gui/images/d-chat64.png deleted file mode 100644 index 8de20ed54..000000000 Binary files a/retroshare-gui/src/gui/images/d-chat64.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/deletemail-pressed.png b/retroshare-gui/src/gui/images/deletemail-pressed.png deleted file mode 100644 index 8d79b7e61..000000000 Binary files a/retroshare-gui/src/gui/images/deletemail-pressed.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/deletemail24.png b/retroshare-gui/src/gui/images/deletemail24.png deleted file mode 100644 index b584ead3f..000000000 Binary files a/retroshare-gui/src/gui/images/deletemail24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/door_in.png b/retroshare-gui/src/gui/images/door_in.png deleted file mode 100644 index a65ed5884..000000000 Binary files a/retroshare-gui/src/gui/images/door_in.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/down_24x24.png b/retroshare-gui/src/gui/images/down_24x24.png deleted file mode 100644 index 92517a244..000000000 Binary files a/retroshare-gui/src/gui/images/down_24x24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/download16.png b/retroshare-gui/src/gui/images/download16.png deleted file mode 100644 index a0c30f12b..000000000 Binary files a/retroshare-gui/src/gui/images/download16.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/edit-bold.png b/retroshare-gui/src/gui/images/edit-bold.png deleted file mode 100644 index a2c44d52d..000000000 Binary files a/retroshare-gui/src/gui/images/edit-bold.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/edit-italic.png b/retroshare-gui/src/gui/images/edit-italic.png deleted file mode 100644 index a0ab94570..000000000 Binary files a/retroshare-gui/src/gui/images/edit-italic.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/edit-underline.png b/retroshare-gui/src/gui/images/edit-underline.png deleted file mode 100644 index 98a1441e1..000000000 Binary files a/retroshare-gui/src/gui/images/edit-underline.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/edit_16.png b/retroshare-gui/src/gui/images/edit_16.png deleted file mode 100644 index 42f442c2f..000000000 Binary files a/retroshare-gui/src/gui/images/edit_16.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/edit_24.png b/retroshare-gui/src/gui/images/edit_24.png deleted file mode 100644 index 1656b786f..000000000 Binary files a/retroshare-gui/src/gui/images/edit_24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/emoticons/kopete/kopete006.png b/retroshare-gui/src/gui/images/emoticons/kopete/kopete006.png deleted file mode 100644 index 53c1819fe..000000000 Binary files a/retroshare-gui/src/gui/images/emoticons/kopete/kopete006.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/emoticons/kopete/kopete007.png b/retroshare-gui/src/gui/images/emoticons/kopete/kopete007.png deleted file mode 100644 index 563532756..000000000 Binary files a/retroshare-gui/src/gui/images/emoticons/kopete/kopete007.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/emoticons/kopete/kopete008.png b/retroshare-gui/src/gui/images/emoticons/kopete/kopete008.png deleted file mode 100644 index a4788a820..000000000 Binary files a/retroshare-gui/src/gui/images/emoticons/kopete/kopete008.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/emoticons/kopete/kopete009.png b/retroshare-gui/src/gui/images/emoticons/kopete/kopete009.png deleted file mode 100644 index fc8dd7218..000000000 Binary files a/retroshare-gui/src/gui/images/emoticons/kopete/kopete009.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/emoticons/kopete/kopete010.png b/retroshare-gui/src/gui/images/emoticons/kopete/kopete010.png deleted file mode 100644 index 3be11a3de..000000000 Binary files a/retroshare-gui/src/gui/images/emoticons/kopete/kopete010.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/emoticons/kopete/kopete011.png b/retroshare-gui/src/gui/images/emoticons/kopete/kopete011.png deleted file mode 100644 index 19a7b33a9..000000000 Binary files a/retroshare-gui/src/gui/images/emoticons/kopete/kopete011.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/emoticons/kopete/kopete012.png b/retroshare-gui/src/gui/images/emoticons/kopete/kopete012.png deleted file mode 100644 index 181c3c776..000000000 Binary files a/retroshare-gui/src/gui/images/emoticons/kopete/kopete012.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/emoticons/kopete/kopete013.png b/retroshare-gui/src/gui/images/emoticons/kopete/kopete013.png deleted file mode 100644 index 41205b572..000000000 Binary files a/retroshare-gui/src/gui/images/emoticons/kopete/kopete013.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/emoticons/kopete/kopete014.png b/retroshare-gui/src/gui/images/emoticons/kopete/kopete014.png deleted file mode 100644 index c7a3c013d..000000000 Binary files a/retroshare-gui/src/gui/images/emoticons/kopete/kopete014.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/emoticons/kopete/kopete015.png b/retroshare-gui/src/gui/images/emoticons/kopete/kopete015.png deleted file mode 100644 index dc113eac5..000000000 Binary files a/retroshare-gui/src/gui/images/emoticons/kopete/kopete015.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/emoticons/kopete/kopete016.png b/retroshare-gui/src/gui/images/emoticons/kopete/kopete016.png deleted file mode 100644 index d6d6de45c..000000000 Binary files a/retroshare-gui/src/gui/images/emoticons/kopete/kopete016.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/emoticons/kopete/kopete017.png b/retroshare-gui/src/gui/images/emoticons/kopete/kopete017.png deleted file mode 100644 index 74938a7e5..000000000 Binary files a/retroshare-gui/src/gui/images/emoticons/kopete/kopete017.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/emoticons/kopete/kopete018.png b/retroshare-gui/src/gui/images/emoticons/kopete/kopete018.png deleted file mode 100644 index 9138b2860..000000000 Binary files a/retroshare-gui/src/gui/images/emoticons/kopete/kopete018.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/emoticons/kopete/kopete019.png b/retroshare-gui/src/gui/images/emoticons/kopete/kopete019.png deleted file mode 100644 index 11bb359f2..000000000 Binary files a/retroshare-gui/src/gui/images/emoticons/kopete/kopete019.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/emoticons/kopete/kopete020.png b/retroshare-gui/src/gui/images/emoticons/kopete/kopete020.png deleted file mode 100644 index a113e35cf..000000000 Binary files a/retroshare-gui/src/gui/images/emoticons/kopete/kopete020.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/encrypted16.png b/retroshare-gui/src/gui/images/encrypted16.png deleted file mode 100644 index b2619730c..000000000 Binary files a/retroshare-gui/src/gui/images/encrypted16.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/encrypted22.png b/retroshare-gui/src/gui/images/encrypted22.png deleted file mode 100644 index 1cf62bf68..000000000 Binary files a/retroshare-gui/src/gui/images/encrypted22.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/encrypted32.png b/retroshare-gui/src/gui/images/encrypted32.png deleted file mode 100644 index 974cd6816..000000000 Binary files a/retroshare-gui/src/gui/images/encrypted32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/encrypted48.png b/retroshare-gui/src/gui/images/encrypted48.png deleted file mode 100644 index b09df126a..000000000 Binary files a/retroshare-gui/src/gui/images/encrypted48.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/evolution.png b/retroshare-gui/src/gui/images/evolution.png deleted file mode 100644 index a54a36647..000000000 Binary files a/retroshare-gui/src/gui/images/evolution.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/exit_24x24.png b/retroshare-gui/src/gui/images/exit_24x24.png deleted file mode 100644 index 3f77be3e0..000000000 Binary files a/retroshare-gui/src/gui/images/exit_24x24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/exit_32.png b/retroshare-gui/src/gui/images/exit_32.png deleted file mode 100644 index 609d4d8ab..000000000 Binary files a/retroshare-gui/src/gui/images/exit_32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/exportpeers_16x16.png b/retroshare-gui/src/gui/images/exportpeers_16x16.png deleted file mode 100644 index c3bd700ba..000000000 Binary files a/retroshare-gui/src/gui/images/exportpeers_16x16.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/folder.png b/retroshare-gui/src/gui/images/folder.png deleted file mode 100644 index 66704bdf3..000000000 Binary files a/retroshare-gui/src/gui/images/folder.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/folder_blueshared.png b/retroshare-gui/src/gui/images/folder_blueshared.png deleted file mode 100755 index 55c67b931..000000000 Binary files a/retroshare-gui/src/gui/images/folder_blueshared.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/folder_doments.png b/retroshare-gui/src/gui/images/folder_doments.png deleted file mode 100644 index 8a6f1eff7..000000000 Binary files a/retroshare-gui/src/gui/images/folder_doments.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/folder_music.png b/retroshare-gui/src/gui/images/folder_music.png deleted file mode 100755 index 32a6c5134..000000000 Binary files a/retroshare-gui/src/gui/images/folder_music.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/folder_open.png b/retroshare-gui/src/gui/images/folder_open.png deleted file mode 100644 index 723b13b1c..000000000 Binary files a/retroshare-gui/src/gui/images/folder_open.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/folder_video.png b/retroshare-gui/src/gui/images/folder_video.png deleted file mode 100755 index 0e45d0cb9..000000000 Binary files a/retroshare-gui/src/gui/images/folder_video.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/fonts.png b/retroshare-gui/src/gui/images/fonts.png deleted file mode 100644 index 267a732e6..000000000 Binary files a/retroshare-gui/src/gui/images/fonts.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/forums_new.png b/retroshare-gui/src/gui/images/forums_new.png deleted file mode 100644 index c3410d99a..000000000 Binary files a/retroshare-gui/src/gui/images/forums_new.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/forward.png b/retroshare-gui/src/gui/images/forward.png deleted file mode 100644 index a7d90d558..000000000 Binary files a/retroshare-gui/src/gui/images/forward.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/friendsfolder24.png b/retroshare-gui/src/gui/images/friendsfolder24.png deleted file mode 100644 index 107e8e70b..000000000 Binary files a/retroshare-gui/src/gui/images/friendsfolder24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/genbackground.png b/retroshare-gui/src/gui/images/genbackground.png deleted file mode 100644 index 24ab3725d..000000000 Binary files a/retroshare-gui/src/gui/images/genbackground.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/gohome.png b/retroshare-gui/src/gui/images/gohome.png deleted file mode 100644 index 6c45397e4..000000000 Binary files a/retroshare-gui/src/gui/images/gohome.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/groupchat.png b/retroshare-gui/src/gui/images/groupchat.png deleted file mode 100644 index 558f316bd..000000000 Binary files a/retroshare-gui/src/gui/images/groupchat.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/headerFrame.png b/retroshare-gui/src/gui/images/headerFrame.png deleted file mode 100644 index 94f6acfcf..000000000 Binary files a/retroshare-gui/src/gui/images/headerFrame.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/help.png b/retroshare-gui/src/gui/images/help.png deleted file mode 100644 index 9f0c92f23..000000000 Binary files a/retroshare-gui/src/gui/images/help.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/help/addafriend.png b/retroshare-gui/src/gui/images/help/addafriend.png index 6f37763b3..af52a79e5 100644 Binary files a/retroshare-gui/src/gui/images/help/addafriend.png and b/retroshare-gui/src/gui/images/help/addafriend.png differ diff --git a/retroshare-gui/src/gui/images/help/addfriendkey.png b/retroshare-gui/src/gui/images/help/addfriendkey.png index 55e9535f0..c9cb06011 100644 Binary files a/retroshare-gui/src/gui/images/help/addfriendkey.png and b/retroshare-gui/src/gui/images/help/addfriendkey.png differ diff --git a/retroshare-gui/src/gui/images/help24.png b/retroshare-gui/src/gui/images/help24.png deleted file mode 100644 index 2c8279b6a..000000000 Binary files a/retroshare-gui/src/gui/images/help24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/hi128-app-ktorrent.png b/retroshare-gui/src/gui/images/hi128-app-ktorrent.png deleted file mode 100644 index 764568d35..000000000 Binary files a/retroshare-gui/src/gui/images/hi128-app-ktorrent.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/hi16-app-ktorrent.png b/retroshare-gui/src/gui/images/hi16-app-ktorrent.png deleted file mode 100644 index 6bdf51399..000000000 Binary files a/retroshare-gui/src/gui/images/hi16-app-ktorrent.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/hi24-app-ktorrent.png b/retroshare-gui/src/gui/images/hi24-app-ktorrent.png deleted file mode 100644 index 052fb20bc..000000000 Binary files a/retroshare-gui/src/gui/images/hi24-app-ktorrent.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/hi32-app-ktorrent.png b/retroshare-gui/src/gui/images/hi32-app-ktorrent.png deleted file mode 100644 index fe92c486e..000000000 Binary files a/retroshare-gui/src/gui/images/hi32-app-ktorrent.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/hi48-app-kblogger.png b/retroshare-gui/src/gui/images/hi48-app-kblogger.png deleted file mode 100644 index da691264b..000000000 Binary files a/retroshare-gui/src/gui/images/hi48-app-kblogger.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/hi48-app-ktorrent.png b/retroshare-gui/src/gui/images/hi48-app-ktorrent.png deleted file mode 100644 index 9afbadc6f..000000000 Binary files a/retroshare-gui/src/gui/images/hi48-app-ktorrent.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/hi64-app-kblogger.png b/retroshare-gui/src/gui/images/hi64-app-kblogger.png deleted file mode 100644 index 2ae6d7ecb..000000000 Binary files a/retroshare-gui/src/gui/images/hi64-app-kblogger.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/hi64-app-ktorrent.png b/retroshare-gui/src/gui/images/hi64-app-ktorrent.png deleted file mode 100644 index 47bbb7ee0..000000000 Binary files a/retroshare-gui/src/gui/images/hi64-app-ktorrent.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/identity/identities_32.png b/retroshare-gui/src/gui/images/identity/identities_32.png deleted file mode 100644 index 8bd056cfe..000000000 Binary files a/retroshare-gui/src/gui/images/identity/identities_32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/identity/identities_48.png b/retroshare-gui/src/gui/images/identity/identities_48.png deleted file mode 100644 index e5322cfd0..000000000 Binary files a/retroshare-gui/src/gui/images/identity/identities_48.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/identity/identities_64.png b/retroshare-gui/src/gui/images/identity/identities_64.png deleted file mode 100644 index 3fdc7ac39..000000000 Binary files a/retroshare-gui/src/gui/images/identity/identities_64.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/identity/identity_32.png b/retroshare-gui/src/gui/images/identity/identity_32.png deleted file mode 100644 index 9b6d2856d..000000000 Binary files a/retroshare-gui/src/gui/images/identity/identity_32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/identity/identity_48.png b/retroshare-gui/src/gui/images/identity/identity_48.png deleted file mode 100644 index 4b771f0bf..000000000 Binary files a/retroshare-gui/src/gui/images/identity/identity_48.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/identity/identity_64.png b/retroshare-gui/src/gui/images/identity/identity_64.png deleted file mode 100644 index ae8ce8e50..000000000 Binary files a/retroshare-gui/src/gui/images/identity/identity_64.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/identity/identity_create_32.png b/retroshare-gui/src/gui/images/identity/identity_create_32.png deleted file mode 100644 index ce2a3ad05..000000000 Binary files a/retroshare-gui/src/gui/images/identity/identity_create_32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/identity/identity_create_64.png b/retroshare-gui/src/gui/images/identity/identity_create_64.png deleted file mode 100644 index 8fab656d5..000000000 Binary files a/retroshare-gui/src/gui/images/identity/identity_create_64.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/identity/identity_delete_32.png b/retroshare-gui/src/gui/images/identity/identity_delete_32.png deleted file mode 100644 index 8c1531ce3..000000000 Binary files a/retroshare-gui/src/gui/images/identity/identity_delete_32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/identity/identity_edit_24.png b/retroshare-gui/src/gui/images/identity/identity_edit_24.png deleted file mode 100644 index 47dcfce02..000000000 Binary files a/retroshare-gui/src/gui/images/identity/identity_edit_24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/identity/identity_edit_32.png b/retroshare-gui/src/gui/images/identity/identity_edit_32.png deleted file mode 100644 index f9040dc07..000000000 Binary files a/retroshare-gui/src/gui/images/identity/identity_edit_32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/identity/identity_edit_48.png b/retroshare-gui/src/gui/images/identity/identity_edit_48.png deleted file mode 100644 index 910bb6d55..000000000 Binary files a/retroshare-gui/src/gui/images/identity/identity_edit_48.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/identity/identity_edit_64.png b/retroshare-gui/src/gui/images/identity/identity_edit_64.png deleted file mode 100644 index 0e14fea7e..000000000 Binary files a/retroshare-gui/src/gui/images/identity/identity_edit_64.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/im-ban-kick-user.png b/retroshare-gui/src/gui/images/im-ban-kick-user.png deleted file mode 100644 index 180582c0a..000000000 Binary files a/retroshare-gui/src/gui/images/im-ban-kick-user.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/im-ban-user.png b/retroshare-gui/src/gui/images/im-ban-user.png deleted file mode 100644 index 37712914a..000000000 Binary files a/retroshare-gui/src/gui/images/im-ban-user.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/im-invisible-user.png b/retroshare-gui/src/gui/images/im-invisible-user.png deleted file mode 100644 index 15bdb5194..000000000 Binary files a/retroshare-gui/src/gui/images/im-invisible-user.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/im-kick-user.png b/retroshare-gui/src/gui/images/im-kick-user.png deleted file mode 100644 index f9f0eec97..000000000 Binary files a/retroshare-gui/src/gui/images/im-kick-user.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/image16.png b/retroshare-gui/src/gui/images/image16.png deleted file mode 100644 index b57e381d0..000000000 Binary files a/retroshare-gui/src/gui/images/image16.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/irkickflash.png b/retroshare-gui/src/gui/images/irkickflash.png deleted file mode 100644 index 0d67d3841..000000000 Binary files a/retroshare-gui/src/gui/images/irkickflash.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/junk-on24.png b/retroshare-gui/src/gui/images/junk-on24.png new file mode 100644 index 000000000..155c2a230 Binary files /dev/null and b/retroshare-gui/src/gui/images/junk-on24.png differ diff --git a/retroshare-gui/src/gui/images/junk.png b/retroshare-gui/src/gui/images/junk.png new file mode 100644 index 000000000..4fbd66128 Binary files /dev/null and b/retroshare-gui/src/gui/images/junk.png differ diff --git a/retroshare-gui/src/gui/images/mail_reply.png b/retroshare-gui/src/gui/images/junk_off.png similarity index 77% rename from retroshare-gui/src/gui/images/mail_reply.png rename to retroshare-gui/src/gui/images/junk_off.png index 471a08ebf..9b838c89f 100644 Binary files a/retroshare-gui/src/gui/images/mail_reply.png and b/retroshare-gui/src/gui/images/junk_off.png differ diff --git a/retroshare-gui/src/gui/images/textedit/hi22-action-insert-more-mark.png b/retroshare-gui/src/gui/images/junk_on.png similarity index 77% rename from retroshare-gui/src/gui/images/textedit/hi22-action-insert-more-mark.png rename to retroshare-gui/src/gui/images/junk_on.png index 64b3b0975..2a2beba9c 100644 Binary files a/retroshare-gui/src/gui/images/textedit/hi22-action-insert-more-mark.png and b/retroshare-gui/src/gui/images/junk_on.png differ diff --git a/retroshare-gui/src/gui/images/kblogger.png b/retroshare-gui/src/gui/images/kblogger.png deleted file mode 100644 index dca1970f8..000000000 Binary files a/retroshare-gui/src/gui/images/kblogger.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/kdmconfig.png b/retroshare-gui/src/gui/images/kdmconfig.png deleted file mode 100644 index 688eb8491..000000000 Binary files a/retroshare-gui/src/gui/images/kdmconfig.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/kgames.png b/retroshare-gui/src/gui/images/kgames.png deleted file mode 100644 index 2ec077b76..000000000 Binary files a/retroshare-gui/src/gui/images/kgames.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/knewsticker24.png b/retroshare-gui/src/gui/images/knewsticker24.png deleted file mode 100644 index e15b73542..000000000 Binary files a/retroshare-gui/src/gui/images/knewsticker24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/konsole.png b/retroshare-gui/src/gui/images/konsole.png deleted file mode 100644 index b74026477..000000000 Binary files a/retroshare-gui/src/gui/images/konsole.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/konv_message.png b/retroshare-gui/src/gui/images/konv_message.png deleted file mode 100644 index b11381d2c..000000000 Binary files a/retroshare-gui/src/gui/images/konv_message.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/konv_message2.png b/retroshare-gui/src/gui/images/konv_message2.png deleted file mode 100644 index da0dba1e3..000000000 Binary files a/retroshare-gui/src/gui/images/konv_message2.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/konv_message3.png b/retroshare-gui/src/gui/images/konv_message3.png deleted file mode 100644 index 40280bb50..000000000 Binary files a/retroshare-gui/src/gui/images/konv_message3.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/konv_message64.png b/retroshare-gui/src/gui/images/konv_message64.png deleted file mode 100644 index edecbfc42..000000000 Binary files a/retroshare-gui/src/gui/images/konv_message64.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/konversation.png b/retroshare-gui/src/gui/images/konversation.png deleted file mode 100644 index 7b9299718..000000000 Binary files a/retroshare-gui/src/gui/images/konversation.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/konversation128.png b/retroshare-gui/src/gui/images/konversation128.png deleted file mode 100644 index 4398fd869..000000000 Binary files a/retroshare-gui/src/gui/images/konversation128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/konversation16.png b/retroshare-gui/src/gui/images/konversation16.png deleted file mode 100644 index 18565596d..000000000 Binary files a/retroshare-gui/src/gui/images/konversation16.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/konversation64.png b/retroshare-gui/src/gui/images/konversation64.png deleted file mode 100644 index 6d3a8e8e3..000000000 Binary files a/retroshare-gui/src/gui/images/konversation64.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/ksysguard.png b/retroshare-gui/src/gui/images/ksysguard.png deleted file mode 100644 index 10c196ebf..000000000 Binary files a/retroshare-gui/src/gui/images/ksysguard.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/ksysguard32.png b/retroshare-gui/src/gui/images/ksysguard32.png deleted file mode 100755 index b30f71d2b..000000000 Binary files a/retroshare-gui/src/gui/images/ksysguard32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/ktorrent.png b/retroshare-gui/src/gui/images/ktorrent.png deleted file mode 100644 index 740badb8c..000000000 Binary files a/retroshare-gui/src/gui/images/ktorrent.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/ktorrent32.png b/retroshare-gui/src/gui/images/ktorrent32.png deleted file mode 100644 index fe92c486e..000000000 Binary files a/retroshare-gui/src/gui/images/ktorrent32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/library.png b/retroshare-gui/src/gui/images/library.png deleted file mode 100644 index a0465b33d..000000000 Binary files a/retroshare-gui/src/gui/images/library.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/library16.png b/retroshare-gui/src/gui/images/library16.png deleted file mode 100644 index ac24d7a74..000000000 Binary files a/retroshare-gui/src/gui/images/library16.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/library64.png b/retroshare-gui/src/gui/images/library64.png deleted file mode 100644 index 8aec71cf2..000000000 Binary files a/retroshare-gui/src/gui/images/library64.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/library_add.png b/retroshare-gui/src/gui/images/library_add.png deleted file mode 100644 index 9285a9cdc..000000000 Binary files a/retroshare-gui/src/gui/images/library_add.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/library_edit.png b/retroshare-gui/src/gui/images/library_edit.png deleted file mode 100644 index 21906e920..000000000 Binary files a/retroshare-gui/src/gui/images/library_edit.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/library_view.png b/retroshare-gui/src/gui/images/library_view.png deleted file mode 100644 index 86fe9a416..000000000 Binary files a/retroshare-gui/src/gui/images/library_view.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/loadcert16.png b/retroshare-gui/src/gui/images/loadcert16.png deleted file mode 100644 index aa4f287b0..000000000 Binary files a/retroshare-gui/src/gui/images/loadcert16.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/logobar/logo_bar_fill.png b/retroshare-gui/src/gui/images/logobar/logo_bar_fill.png deleted file mode 100644 index fa8257794..000000000 Binary files a/retroshare-gui/src/gui/images/logobar/logo_bar_fill.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/logobar/logo_bar_start.png b/retroshare-gui/src/gui/images/logobar/logo_bar_start.png deleted file mode 100644 index 9ebb7d1fe..000000000 Binary files a/retroshare-gui/src/gui/images/logobar/logo_bar_start.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/logobar/rslogo.png b/retroshare-gui/src/gui/images/logobar/rslogo.png deleted file mode 100644 index 18aaa95b2..000000000 Binary files a/retroshare-gui/src/gui/images/logobar/rslogo.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/logobar/rslogo2.png b/retroshare-gui/src/gui/images/logobar/rslogo2.png deleted file mode 100644 index 64266ed20..000000000 Binary files a/retroshare-gui/src/gui/images/logobar/rslogo2.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/logobar/rslogoclean.png b/retroshare-gui/src/gui/images/logobar/rslogoclean.png deleted file mode 100644 index 0ebb66fa9..000000000 Binary files a/retroshare-gui/src/gui/images/logobar/rslogoclean.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/looknfeel.png b/retroshare-gui/src/gui/images/looknfeel.png deleted file mode 100644 index 155e2d865..000000000 Binary files a/retroshare-gui/src/gui/images/looknfeel.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/lphoto.png b/retroshare-gui/src/gui/images/lphoto.png deleted file mode 100644 index c7bb01c11..000000000 Binary files a/retroshare-gui/src/gui/images/lphoto.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/lphoto16.png b/retroshare-gui/src/gui/images/lphoto16.png deleted file mode 100644 index f57102985..000000000 Binary files a/retroshare-gui/src/gui/images/lphoto16.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/lphoto24.png b/retroshare-gui/src/gui/images/lphoto24.png deleted file mode 100644 index 4eb197db1..000000000 Binary files a/retroshare-gui/src/gui/images/lphoto24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/mail-message-new.png b/retroshare-gui/src/gui/images/mail-message-new.png deleted file mode 100644 index 5754fdf7b..000000000 Binary files a/retroshare-gui/src/gui/images/mail-message-new.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/mail_delete.png b/retroshare-gui/src/gui/images/mail_delete.png deleted file mode 100644 index dc9c020f7..000000000 Binary files a/retroshare-gui/src/gui/images/mail_delete.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/mail_forward.png b/retroshare-gui/src/gui/images/mail_forward.png deleted file mode 100755 index 833360b24..000000000 Binary files a/retroshare-gui/src/gui/images/mail_forward.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/mail_get.png b/retroshare-gui/src/gui/images/mail_get.png deleted file mode 100644 index a8fc27a5c..000000000 Binary files a/retroshare-gui/src/gui/images/mail_get.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/mail_new.png b/retroshare-gui/src/gui/images/mail_new.png deleted file mode 100644 index 55bba1bdd..000000000 Binary files a/retroshare-gui/src/gui/images/mail_new.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/mail_replyall.png b/retroshare-gui/src/gui/images/mail_replyall.png deleted file mode 100755 index 6e738a11e..000000000 Binary files a/retroshare-gui/src/gui/images/mail_replyall.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/mail_send.png b/retroshare-gui/src/gui/images/mail_send.png deleted file mode 100644 index 0366228e9..000000000 Binary files a/retroshare-gui/src/gui/images/mail_send.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/mail_send24.png b/retroshare-gui/src/gui/images/mail_send24.png deleted file mode 100644 index be6d64f00..000000000 Binary files a/retroshare-gui/src/gui/images/mail_send24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/message_new.png b/retroshare-gui/src/gui/images/message_new.png deleted file mode 100644 index b1c9a4954..000000000 Binary files a/retroshare-gui/src/gui/images/message_new.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/messenger.png b/retroshare-gui/src/gui/images/messenger.png deleted file mode 100644 index cf15aadc5..000000000 Binary files a/retroshare-gui/src/gui/images/messenger.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/my_documents_16.png b/retroshare-gui/src/gui/images/my_documents_16.png deleted file mode 100644 index 571ad0851..000000000 Binary files a/retroshare-gui/src/gui/images/my_documents_16.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/my_documents_22.png b/retroshare-gui/src/gui/images/my_documents_22.png deleted file mode 100644 index 3afad57ab..000000000 Binary files a/retroshare-gui/src/gui/images/my_documents_22.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/my_documents_32.png b/retroshare-gui/src/gui/images/my_documents_32.png deleted file mode 100644 index 7b7661d2c..000000000 Binary files a/retroshare-gui/src/gui/images/my_documents_32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/network.png b/retroshare-gui/src/gui/images/network.png deleted file mode 100644 index 99a04417a..000000000 Binary files a/retroshare-gui/src/gui/images/network.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/network16.png b/retroshare-gui/src/gui/images/network16.png deleted file mode 100644 index e1f1bd7cb..000000000 Binary files a/retroshare-gui/src/gui/images/network16.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/network32.png b/retroshare-gui/src/gui/images/network32.png deleted file mode 100644 index e74a67f44..000000000 Binary files a/retroshare-gui/src/gui/images/network32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/new_forum16.png b/retroshare-gui/src/gui/images/new_forum16.png deleted file mode 100644 index 712802dde..000000000 Binary files a/retroshare-gui/src/gui/images/new_forum16.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/newmsg.png b/retroshare-gui/src/gui/images/newmsg.png deleted file mode 100644 index 6f011f878..000000000 Binary files a/retroshare-gui/src/gui/images/newmsg.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/newsfeed/news-feed-32.png b/retroshare-gui/src/gui/images/newsfeed/news-feed-32.png deleted file mode 100644 index b79cd02dc..000000000 Binary files a/retroshare-gui/src/gui/images/newsfeed/news-feed-32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/newsfeed/news-feed-notify-32.png b/retroshare-gui/src/gui/images/newsfeed/news-feed-notify-32.png deleted file mode 100644 index 4c0b74727..000000000 Binary files a/retroshare-gui/src/gui/images/newsfeed/news-feed-notify-32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/newsfeed128.png b/retroshare-gui/src/gui/images/newsfeed128.png deleted file mode 100644 index 19c256c9f..000000000 Binary files a/retroshare-gui/src/gui/images/newsfeed128.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/newsfeed128_notify.png b/retroshare-gui/src/gui/images/newsfeed128_notify.png deleted file mode 100644 index d660fe710..000000000 Binary files a/retroshare-gui/src/gui/images/newsfeed128_notify.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/print24.png b/retroshare-gui/src/gui/images/print24.png deleted file mode 100644 index 31657ea49..000000000 Binary files a/retroshare-gui/src/gui/images/print24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/quote_24.png b/retroshare-gui/src/gui/images/quote_24.png deleted file mode 100644 index 57107f679..000000000 Binary files a/retroshare-gui/src/gui/images/quote_24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/quote_24_hover.png b/retroshare-gui/src/gui/images/quote_24_hover.png deleted file mode 100644 index cc16fdbd8..000000000 Binary files a/retroshare-gui/src/gui/images/quote_24_hover.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/rate-1.png b/retroshare-gui/src/gui/images/rate-1.png deleted file mode 100644 index b24fcc309..000000000 Binary files a/retroshare-gui/src/gui/images/rate-1.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/rate-2.png b/retroshare-gui/src/gui/images/rate-2.png deleted file mode 100644 index 22e0c855d..000000000 Binary files a/retroshare-gui/src/gui/images/rate-2.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/rate-3.png b/retroshare-gui/src/gui/images/rate-3.png deleted file mode 100644 index 759f1e415..000000000 Binary files a/retroshare-gui/src/gui/images/rate-3.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/rate-4.png b/retroshare-gui/src/gui/images/rate-4.png deleted file mode 100644 index f6fb27ec6..000000000 Binary files a/retroshare-gui/src/gui/images/rate-4.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/rate-5.png b/retroshare-gui/src/gui/images/rate-5.png deleted file mode 100644 index 36b95cdbd..000000000 Binary files a/retroshare-gui/src/gui/images/rate-5.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/rating.png b/retroshare-gui/src/gui/images/rating.png deleted file mode 100644 index 6e47827b5..000000000 Binary files a/retroshare-gui/src/gui/images/rating.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/retrotoolbar.png b/retroshare-gui/src/gui/images/retrotoolbar.png deleted file mode 100644 index 4a8e6cd39..000000000 Binary files a/retroshare-gui/src/gui/images/retrotoolbar.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/rsmessenger16.png b/retroshare-gui/src/gui/images/rsmessenger16.png deleted file mode 100644 index cdc8dd66c..000000000 Binary files a/retroshare-gui/src/gui/images/rsmessenger16.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/rsmessenger32.png b/retroshare-gui/src/gui/images/rsmessenger32.png deleted file mode 100644 index d768a5624..000000000 Binary files a/retroshare-gui/src/gui/images/rsmessenger32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/server_24x24.png b/retroshare-gui/src/gui/images/server_24x24.png deleted file mode 100644 index fa4091150..000000000 Binary files a/retroshare-gui/src/gui/images/server_24x24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/settings16.png b/retroshare-gui/src/gui/images/settings16.png deleted file mode 100644 index 5f31eb45a..000000000 Binary files a/retroshare-gui/src/gui/images/settings16.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/tag24.png b/retroshare-gui/src/gui/images/tag24.png deleted file mode 100644 index 5da99c1d0..000000000 Binary files a/retroshare-gui/src/gui/images/tag24.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/tags/anon.png b/retroshare-gui/src/gui/images/tags/anon.png deleted file mode 100644 index eb90d1174..000000000 Binary files a/retroshare-gui/src/gui/images/tags/anon.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/edit-image-face-add.png b/retroshare-gui/src/gui/images/textedit/edit-image-face-add.png deleted file mode 100644 index 2a3a46313..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/edit-image-face-add.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/editcopy.png b/retroshare-gui/src/gui/images/textedit/editcopy.png deleted file mode 100644 index 1121b47d8..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/editcopy.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/editcut.png b/retroshare-gui/src/gui/images/textedit/editcut.png deleted file mode 100644 index 38e55f742..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/editcut.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/editpaste.png b/retroshare-gui/src/gui/images/textedit/editpaste.png deleted file mode 100644 index ffab15aaf..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/editpaste.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/editredo.png b/retroshare-gui/src/gui/images/textedit/editredo.png deleted file mode 100644 index 9d679fe6f..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/editredo.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/editundo.png b/retroshare-gui/src/gui/images/textedit/editundo.png deleted file mode 100644 index eee23d24a..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/editundo.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/exportpdf.png b/retroshare-gui/src/gui/images/textedit/exportpdf.png deleted file mode 100644 index eef513292..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/exportpdf.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/filenew.png b/retroshare-gui/src/gui/images/textedit/filenew.png deleted file mode 100644 index af5d12214..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/filenew.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/fileopen.png b/retroshare-gui/src/gui/images/textedit/fileopen.png deleted file mode 100644 index fc6f17e97..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/fileopen.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/fileprint.png b/retroshare-gui/src/gui/images/textedit/fileprint.png deleted file mode 100644 index ba7c02dc1..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/fileprint.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/filesave.png b/retroshare-gui/src/gui/images/textedit/filesave.png deleted file mode 100644 index 8feec99be..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/filesave.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/format-indent-less.png b/retroshare-gui/src/gui/images/textedit/format-indent-less.png deleted file mode 100644 index 23d53d029..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/format-indent-less.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/format-indent-more.png b/retroshare-gui/src/gui/images/textedit/format-indent-more.png deleted file mode 100644 index 38ff8bb2a..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/format-indent-more.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/format-list-ordered.png b/retroshare-gui/src/gui/images/textedit/format-list-ordered.png deleted file mode 100644 index 043caa363..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/format-list-ordered.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/format-list-unordered.png b/retroshare-gui/src/gui/images/textedit/format-list-unordered.png deleted file mode 100644 index d60c0f60b..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/format-list-unordered.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/format-text-color.png b/retroshare-gui/src/gui/images/textedit/format-text-color.png deleted file mode 100644 index 98f617311..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/format-text-color.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/format-text-strikethrough.png b/retroshare-gui/src/gui/images/textedit/format-text-strikethrough.png deleted file mode 100644 index 3e25ced5c..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/format-text-strikethrough.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/format_font_size_less.png b/retroshare-gui/src/gui/images/textedit/format_font_size_less.png deleted file mode 100644 index 43ab7d23a..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/format_font_size_less.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/format_font_size_more.png b/retroshare-gui/src/gui/images/textedit/format_font_size_more.png deleted file mode 100644 index 904245147..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/format_font_size_more.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/hi22-action-format-text-blockquote.png b/retroshare-gui/src/gui/images/textedit/hi22-action-format-text-blockquote.png deleted file mode 100644 index 0306a103a..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/hi22-action-format-text-blockquote.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/hi22-action-format-text-code.png b/retroshare-gui/src/gui/images/textedit/hi22-action-format-text-code.png deleted file mode 100644 index 3cb27895c..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/hi22-action-format-text-code.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/insert-link.png b/retroshare-gui/src/gui/images/textedit/insert-link.png deleted file mode 100644 index f820657ef..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/insert-link.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/textbold.png b/retroshare-gui/src/gui/images/textedit/textbold.png deleted file mode 100644 index a5535e33f..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/textbold.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/textcenter.png b/retroshare-gui/src/gui/images/textedit/textcenter.png deleted file mode 100644 index 1907b44d8..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/textcenter.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/textitalic.png b/retroshare-gui/src/gui/images/textedit/textitalic.png deleted file mode 100644 index a50211f4f..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/textitalic.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/textjustify.png b/retroshare-gui/src/gui/images/textedit/textjustify.png deleted file mode 100644 index eddb7bfa7..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/textjustify.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/textleft.png b/retroshare-gui/src/gui/images/textedit/textleft.png deleted file mode 100644 index 37a9c5e0e..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/textleft.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/textright.png b/retroshare-gui/src/gui/images/textedit/textright.png deleted file mode 100644 index 404315b71..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/textright.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/textunder.png b/retroshare-gui/src/gui/images/textedit/textunder.png deleted file mode 100644 index e51139505..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/textunder.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/zoomin.png b/retroshare-gui/src/gui/images/textedit/zoomin.png deleted file mode 100644 index 2e586fc7b..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/zoomin.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/textedit/zoomout.png b/retroshare-gui/src/gui/images/textedit/zoomout.png deleted file mode 100644 index a736d3934..000000000 Binary files a/retroshare-gui/src/gui/images/textedit/zoomout.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/toaster/backgroundtoaster.png b/retroshare-gui/src/gui/images/toaster/backgroundtoaster.png deleted file mode 100644 index 96b7a2f5c..000000000 Binary files a/retroshare-gui/src/gui/images/toaster/backgroundtoaster.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/toaster/backgroundtoasterblue.png b/retroshare-gui/src/gui/images/toaster/backgroundtoasterblue.png deleted file mode 100644 index 71f57408d..000000000 Binary files a/retroshare-gui/src/gui/images/toaster/backgroundtoasterblue.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/toaster/chat.png b/retroshare-gui/src/gui/images/toaster/chat.png deleted file mode 100644 index 1dd453348..000000000 Binary files a/retroshare-gui/src/gui/images/toaster/chat.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/toaster/hangup.png b/retroshare-gui/src/gui/images/toaster/hangup.png deleted file mode 100644 index 2febc25be..000000000 Binary files a/retroshare-gui/src/gui/images/toaster/hangup.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/toaster/pickup.png b/retroshare-gui/src/gui/images/toaster/pickup.png deleted file mode 100644 index 9da541978..000000000 Binary files a/retroshare-gui/src/gui/images/toaster/pickup.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/transfers_new.png b/retroshare-gui/src/gui/images/transfers_new.png deleted file mode 100644 index 81e95b743..000000000 Binary files a/retroshare-gui/src/gui/images/transfers_new.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/trustsettings.png b/retroshare-gui/src/gui/images/trustsettings.png deleted file mode 100644 index e7386a10c..000000000 Binary files a/retroshare-gui/src/gui/images/trustsettings.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/turtle.png b/retroshare-gui/src/gui/images/turtle.png deleted file mode 100644 index 84ece2205..000000000 Binary files a/retroshare-gui/src/gui/images/turtle.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/typing.png b/retroshare-gui/src/gui/images/typing.png deleted file mode 100644 index 28580e9a6..000000000 Binary files a/retroshare-gui/src/gui/images/typing.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/update.png b/retroshare-gui/src/gui/images/update.png new file mode 100644 index 000000000..cdbd86b4b Binary files /dev/null and b/retroshare-gui/src/gui/images/update.png differ diff --git a/retroshare-gui/src/gui/images/view-certificate-copy-32.png b/retroshare-gui/src/gui/images/view-certificate-copy-32.png deleted file mode 100644 index c5bea2449..000000000 Binary files a/retroshare-gui/src/gui/images/view-certificate-copy-32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/view-certificate-export-32.png b/retroshare-gui/src/gui/images/view-certificate-export-32.png deleted file mode 100644 index f4075b9d6..000000000 Binary files a/retroshare-gui/src/gui/images/view-certificate-export-32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/view-certificate-sign-32.png b/retroshare-gui/src/gui/images/view-certificate-sign-32.png deleted file mode 100644 index 1ea763f14..000000000 Binary files a/retroshare-gui/src/gui/images/view-certificate-sign-32.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/view-certificate-sign-48.png b/retroshare-gui/src/gui/images/view-certificate-sign-48.png deleted file mode 100644 index 012e8aea8..000000000 Binary files a/retroshare-gui/src/gui/images/view-certificate-sign-48.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/view-feeds.png b/retroshare-gui/src/gui/images/view-feeds.png deleted file mode 100644 index 5b0d8bf82..000000000 Binary files a/retroshare-gui/src/gui/images/view-feeds.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/view-files.png b/retroshare-gui/src/gui/images/view-files.png deleted file mode 100644 index 6b36ae15b..000000000 Binary files a/retroshare-gui/src/gui/images/view-files.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/vote_down.png b/retroshare-gui/src/gui/images/vote_down.png deleted file mode 100644 index 369abdbda..000000000 Binary files a/retroshare-gui/src/gui/images/vote_down.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/vote_neutral.png b/retroshare-gui/src/gui/images/vote_neutral.png deleted file mode 100644 index a68a43d75..000000000 Binary files a/retroshare-gui/src/gui/images/vote_neutral.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/vote_up.png b/retroshare-gui/src/gui/images/vote_up.png deleted file mode 100644 index 9148aee7c..000000000 Binary files a/retroshare-gui/src/gui/images/vote_up.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/window_fullscreen.png b/retroshare-gui/src/gui/images/window_fullscreen.png deleted file mode 100644 index 18a00007a..000000000 Binary files a/retroshare-gui/src/gui/images/window_fullscreen.png and /dev/null differ diff --git a/retroshare-gui/src/gui/images/window_nofullscreen.png b/retroshare-gui/src/gui/images/window_nofullscreen.png deleted file mode 100644 index bfd4fa597..000000000 Binary files a/retroshare-gui/src/gui/images/window_nofullscreen.png and /dev/null differ diff --git a/retroshare-gui/src/gui/msgs/MessageComposer.cpp b/retroshare-gui/src/gui/msgs/MessageComposer.cpp index 52da5b10f..db3083686 100644 --- a/retroshare-gui/src/gui/msgs/MessageComposer.cpp +++ b/retroshare-gui/src/gui/msgs/MessageComposer.cpp @@ -18,6 +18,7 @@ * * *******************************************************************************/ +#include "gui/common/FilesDefs.h" #include #include #include @@ -223,16 +224,16 @@ MessageComposer::MessageComposer(QWidget *parent, Qt::WindowFlags flags) QActionGroup *grp = new QActionGroup(this); connect(grp, SIGNAL(triggered(QAction *)), this, SLOT(textAlign(QAction *))); - actionAlignLeft = new QAction(QIcon(":/images/textedit/textleft.png"), tr("&Left"), grp); + actionAlignLeft = new QAction(QIcon(""), tr("&Left"), grp); actionAlignLeft->setShortcut(Qt::CTRL + Qt::Key_L); actionAlignLeft->setCheckable(true); - actionAlignCenter = new QAction(QIcon(":/images/textedit/textcenter.png"), tr("C&enter"), grp); + actionAlignCenter = new QAction(QIcon(""), tr("C&enter"), grp); actionAlignCenter->setShortcut(Qt::CTRL + Qt::Key_E); actionAlignCenter->setCheckable(true); - actionAlignRight = new QAction(QIcon(":/images/textedit/textright.png"), tr("&Right"), grp); + actionAlignRight = new QAction(QIcon(""), tr("&Right"), grp); actionAlignRight->setShortcut(Qt::CTRL + Qt::Key_R); actionAlignRight->setCheckable(true); - actionAlignJustify = new QAction(QIcon(":/images/textedit/textjustify.png"), tr("&Justify"), grp); + actionAlignJustify = new QAction(QIcon(""), tr("&Justify"), grp); actionAlignJustify->setShortcut(Qt::CTRL + Qt::Key_J); actionAlignJustify->setCheckable(true); @@ -280,8 +281,6 @@ MessageComposer::MessageComposer(QWidget *parent, Qt::WindowFlags flags) connect(ui.comboSize, SIGNAL(activated(const QString &)),this, SLOT(textSize(const QString &))); ui.comboSize->setCurrentIndex(ui.comboSize->findText(QString::number(QApplication::font().pointSize()))); - ui.textalignmentbtn->setIcon(QIcon(QString(":/images/textedit/textcenter.png"))); - QMenu * alignmentmenu = new QMenu(); alignmentmenu->addAction(actionAlignLeft); alignmentmenu->addAction(actionAlignCenter); @@ -698,7 +697,7 @@ void MessageComposer::contextMenuFileList(QPoint) { QMenu contextMnu(this); - QAction *action = contextMnu.addAction(QIcon(":/images/pasterslink.png"), tr("Paste RetroShare Link"), this, SLOT(pasteRecommended())); + QAction *action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/pasterslink.png"), tr("Paste RetroShare Link"), this, SLOT(pasteRecommended())); action->setDisabled(RSLinkClipboard::empty(RetroShareLink::TYPE_FILE)); contextMnu.exec(QCursor::pos()); @@ -807,7 +806,7 @@ void MessageComposer::peerStatusChanged(const QString& peer_id, int status) { QTableWidgetItem *item = ui.recipientWidget->item(row, COLUMN_RECIPIENT_ICON); if (item) - item->setIcon(QIcon(StatusDefs::imageUser(status))); + item->setIcon(FilesDefs::getIconFromQtResourcePath(StatusDefs::imageUser(status))); } } } @@ -1208,6 +1207,12 @@ MessageComposer *MessageComposer::replyMsg(const std::string &msgId, bool all) // needed to send system flags with reply msgComposer->msgFlags = (msgInfo.msgflags & RS_MSG_SYSTEM); + MsgTagInfo tagInfo; + rsMail->getMessageTag(msgId, tagInfo); + + msgComposer->m_tagIds = tagInfo.tagIds; + msgComposer->showTagLabels(); + msgComposer->calculateTitle(); /* window will destroy itself! */ @@ -1602,7 +1607,7 @@ void MessageComposer::setRecipientToRow(int row, enumType type, destinationType switch(dest_type) { case PEER_TYPE_GROUP: { - icon = QIcon(IMAGE_GROUP16); + icon = FilesDefs::getIconFromQtResourcePath(IMAGE_GROUP16); RsGroupInfo groupInfo; if (rsPeers->getGroupInfo(RsNodeGroupId(id), groupInfo)) { @@ -1648,7 +1653,7 @@ void MessageComposer::setRecipientToRow(int row, enumType type, destinationType // No check of return value. Non existing status info is handled as offline. rsStatus->getStatus(RsPeerId(id), peerStatusInfo); - icon = QIcon(StatusDefs::imageUser(peerStatusInfo.status)); + icon = FilesDefs::getIconFromQtResourcePath(StatusDefs::imageUser(peerStatusInfo.status)); } break ; default: @@ -1892,19 +1897,19 @@ void MessageComposer::setupFileActions() QAction *a; - a = new QAction(QIcon(":/images/textedit/filenew.png"), tr("&New"), this); + a = new QAction(QIcon(""), tr("&New"), this); a->setShortcut(QKeySequence::New); connect(a, SIGNAL(triggered()), this, SLOT(fileNew())); menu->addAction(a); - a = new QAction(QIcon(":/images/textedit/fileopen.png"), tr("&Open..."), this); + a = new QAction(QIcon(""), tr("&Open..."), this); a->setShortcut(QKeySequence::Open); connect(a, SIGNAL(triggered()), this, SLOT(fileOpen())); menu->addAction(a); menu->addSeparator(); - actionSave = a = new QAction(QIcon(":/images/textedit/filesave.png"), tr("&Save"), this); + actionSave = a = new QAction(QIcon(""), tr("&Save"), this); a->setShortcut(QKeySequence::Save); connect(a, SIGNAL(triggered()), this, SLOT(saveasDraft())); a->setEnabled(false); @@ -1919,16 +1924,16 @@ void MessageComposer::setupFileActions() menu->addAction(a); menu->addSeparator(); - a = new QAction(QIcon(":/images/textedit/fileprint.png"), tr("&Print..."), this); + a = new QAction(QIcon(""), tr("&Print..."), this); a->setShortcut(QKeySequence::Print); connect(a, SIGNAL(triggered()), this, SLOT(filePrint())); menu->addAction(a); - /*a = new QAction(QIcon(":/images/textedit/fileprint.png"), tr("Print Preview..."), this); + /*a = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/textedit/fileprint.png"), tr("Print Preview..."), this); connect(a, SIGNAL(triggered()), this, SLOT(filePrintPreview())); menu->addAction(a);*/ - a = new QAction(QIcon(":/images/textedit/exportpdf.png"), tr("&Export PDF..."), this); + a = new QAction(QIcon(""), tr("&Export PDF..."), this); a->setShortcut(Qt::CTRL + Qt::Key_D); connect(a, SIGNAL(triggered()), this, SLOT(filePrintPdf())); menu->addAction(a); @@ -1947,20 +1952,20 @@ void MessageComposer::setupEditActions() menuBar()->addMenu(menu); QAction *a; - a = actionUndo = new QAction(QIcon(":/images/textedit/editundo.png"), tr("&Undo"), this); + a = actionUndo = new QAction(QIcon(""), tr("&Undo"), this); a->setShortcut(QKeySequence::Undo); menu->addAction(a); - a = actionRedo = new QAction(QIcon(":/images/textedit/editredo.png"), tr("&Redo"), this); + a = actionRedo = new QAction(QIcon(""), tr("&Redo"), this); a->setShortcut(QKeySequence::Redo); menu->addAction(a); menu->addSeparator(); - a = actionCut = new QAction(QIcon(":/images/textedit/editcut.png"), tr("Cu&t"), this); + a = actionCut = new QAction(QIcon(""), tr("Cu&t"), this); a->setShortcut(QKeySequence::Cut); menu->addAction(a); - a = actionCopy = new QAction(QIcon(":/images/textedit/editcopy.png"), tr("&Copy"), this); + a = actionCopy = new QAction(QIcon(""), tr("&Copy"), this); a->setShortcut(QKeySequence::Copy); menu->addAction(a); - a = actionPaste = new QAction(QIcon(":/images/textedit/editpaste.png"), tr("&Paste"), this); + a = actionPaste = new QAction(QIcon(""), tr("&Paste"), this); a->setShortcut(QKeySequence::Paste); menu->addAction(a); actionPaste->setEnabled(!QApplication::clipboard()->text().isEmpty()); @@ -2017,7 +2022,7 @@ void MessageComposer::setupContactActions() connect(mActionAddBCC, SIGNAL(triggered(bool)), this, SLOT(addBcc())); mActionAddRecommend = new QAction(tr("Add as Recommend"), this); connect(mActionAddRecommend, SIGNAL(triggered(bool)), this, SLOT(addRecommend())); - mActionContactDetails = new QAction(QIcon(IMAGE_FRIENDINFO), tr("Details"), this); + mActionContactDetails = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_FRIENDINFO), tr("Details"), this); connect(mActionContactDetails, SIGNAL(triggered(bool)), this, SLOT(contactDetails())); ui.friendSelectionWidget->addContextMenuAction(mActionAddTo); @@ -2431,9 +2436,9 @@ void MessageComposer::on_contactsdockWidget_visibilityChanged(bool visible) void MessageComposer::updatecontactsviewicons() { if(!ui.contactsdockWidget->isVisible()){ - ui.actionContactsView->setIcon(QIcon(":/images/contactsclosed24.png")); + ui.actionContactsView->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/mail/contacts.png")); }else{ - ui.actionContactsView->setIcon(QIcon(":/images/contacts24.png")); + ui.actionContactsView->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/mail/contacts.png")); } } diff --git a/retroshare-gui/src/gui/msgs/MessageComposer.ui b/retroshare-gui/src/gui/msgs/MessageComposer.ui index fb99b5b23..134d94ef9 100644 --- a/retroshare-gui/src/gui/msgs/MessageComposer.ui +++ b/retroshare-gui/src/gui/msgs/MessageComposer.ui @@ -14,8 +14,8 @@ Compose - - :/images/folder-draft.png:/images/folder-draft.png + + :/icons/mail/compose.png:/icons/mail/compose.png @@ -323,8 +323,8 @@ - - :/images/textedit/format_font_size_more.png:/images/textedit/format_font_size_more.png + + :/icons/textedit/font-increase.png:/icons/textedit/font-increase.png @@ -355,8 +355,8 @@ - - :/images/textedit/format_font_size_less.png:/images/textedit/format_font_size_less.png + + :/icons/textedit/font-decrease.png:/icons/textedit/font-decrease.png @@ -393,8 +393,8 @@ - - :/images/textedit/textbold.png:/images/textedit/textbold.png + + :/icons/textedit/bold.png:/icons/textedit/bold.png @@ -434,8 +434,8 @@ - - :/images/textedit/textitalic.png:/images/textedit/textitalic.png + + :/icons/textedit/italic.png:/icons/textedit/italic.png @@ -475,8 +475,8 @@ - - :/images/add_image24.png:/images/add_image24.png + + :/icons/textedit/photo-of-a-landscape.png:/icons/textedit/photo-of-a-landscape.png @@ -512,6 +512,10 @@ + + + :/icons/textedit/align.png:/icons/textedit/align.png + 24 @@ -519,7 +523,7 @@ - QToolButton::MenuButtonPopup + QToolButton::InstantPopup true @@ -538,13 +542,13 @@ - - :/images/textedit/hi22-action-format-text-code.png:/images/textedit/hi22-action-format-text-code.png + + :/icons/textedit/code.png:/icons/textedit/code.png - 22 - 22 + 24 + 24 @@ -589,8 +593,8 @@ - - :/images/textedit/textunder.png:/images/textedit/textunder.png + + :/icons/textedit/underline.png:/icons/textedit/underline.png @@ -612,8 +616,8 @@ Qt::NoFocus - - :/emojione/1F603.png:/emojione/1F603.png + + :/icons/textedit/smile.png:/icons/textedit/smile.png @@ -676,20 +680,20 @@ - - :/images/textedit/format-list-unordered.png:/images/textedit/format-list-unordered.png + + :/icons/textedit/numberd-list.png:/icons/textedit/numberd-list.png - 22 - 22 + 24 + 24 false - QToolButton::MenuButtonPopup + QToolButton::InstantPopup true @@ -881,8 +885,8 @@ border-image: url(:/images/closepressed.png) Tags - - :/images/tag24.png:/images/tag24.png + + :/icons/mail/tags.png:/icons/mail/tags.png true @@ -1154,8 +1158,8 @@ border-image: url(:/images/closepressed.png) - - :/images/send24.png:/images/send24.png + + :/icons/mail/send.png:/icons/mail/send.png Send @@ -1165,18 +1169,14 @@ border-image: url(:/images/closepressed.png) - - - :/images/replymail24.png:/images/replymail24.png - Reply - - :/images/contacts24.png:/images/contacts24.png + + :/icons/mail/contacts.png:/icons/mail/contacts.png Contacts @@ -1187,8 +1187,8 @@ border-image: url(:/images/closepressed.png) - - :/images/save24.png:/images/save24.png + + :/icons/mail/save.png:/icons/mail/save.png Save @@ -1199,8 +1199,8 @@ border-image: url(:/images/closepressed.png) - - :/images/attach.png:/images/attach.png + + :/icons/png/attachements.png:/icons/png/attachements.png Attach @@ -1214,8 +1214,8 @@ border-image: url(:/images/closepressed.png) true - - :/images/quote_24.png:/images/quote_24.png + + :/icons/mail/quote.png:/icons/mail/quote.png Quote @@ -1263,6 +1263,7 @@ border-image: url(:/images/closepressed.png) + diff --git a/retroshare-gui/src/gui/msgs/MessageModel.cpp b/retroshare-gui/src/gui/msgs/MessageModel.cpp index 441b02bbc..5ddef2283 100644 --- a/retroshare-gui/src/gui/msgs/MessageModel.cpp +++ b/retroshare-gui/src/gui/msgs/MessageModel.cpp @@ -27,6 +27,7 @@ #include #include "gui/common/TagDefs.h" +#include "gui/common/FilesDefs.h" #include "util/HandleRichText.h" #include "util/DateTime.h" #include "gui/gxs/GxsIdDetails.h" @@ -41,6 +42,9 @@ #define IMAGE_STAR_ON ":/images/star-on-16.png" #define IMAGE_STAR_OFF ":/images/star-off-16.png" +#define IMAGE_SPAM ":/images/junk.png" +#define IMAGE_SPAM_ON ":/images/junk_on.png" +#define IMAGE_SPAM_OFF ":/images/junk_off.png" std::ostream& operator<<(std::ostream& o, const QModelIndex& i);// defined elsewhere @@ -171,9 +175,10 @@ QVariant RsMessageModel::headerData(int section, Qt::Orientation orientation, in if(role == Qt::DecorationRole) switch(section) { - case COLUMN_THREAD_STAR: return QIcon(IMAGE_STAR_ON); - case COLUMN_THREAD_READ: return QIcon(":/images/message-state-header.png"); - case COLUMN_THREAD_ATTACHMENT: return QIcon(":/images/attachment.png"); + case COLUMN_THREAD_STAR: return FilesDefs::getIconFromQtResourcePath(IMAGE_STAR_ON); + case COLUMN_THREAD_SPAM: return FilesDefs::getIconFromQtResourcePath(IMAGE_SPAM); + case COLUMN_THREAD_READ: return FilesDefs::getIconFromQtResourcePath(":/images/message-state-header.png"); + case COLUMN_THREAD_ATTACHMENT: return FilesDefs::getIconFromQtResourcePath(":/icons/png/attachements.png"); default: return QVariant(); } @@ -188,6 +193,7 @@ QVariant RsMessageModel::headerData(int section, Qt::Orientation orientation, in case COLUMN_THREAD_DATE: return tr("Click to sort by date"); case COLUMN_THREAD_TAGS: return tr("Click to sort by tags"); case COLUMN_THREAD_STAR: return tr("Click to sort by star"); + case COLUMN_THREAD_SPAM: return tr("Click to sort by junk status"); default: return QVariant(); } @@ -335,7 +341,9 @@ bool RsMessageModel::passesFilter(const Rs::Msgs::MsgInfoSummary& fmpe,int colum (mQuickViewFilter==QUICK_VIEW_ALL) || (std::find(fmpe.msgtags.begin(),fmpe.msgtags.end(),mQuickViewFilter) != fmpe.msgtags.end()) || (mQuickViewFilter==QUICK_VIEW_STARRED && (fmpe.msgflags & RS_MSG_STAR)) - || (mQuickViewFilter==QUICK_VIEW_SYSTEM && (fmpe.msgflags & RS_MSG_SYSTEM)); + || (mQuickViewFilter==QUICK_VIEW_SYSTEM && (fmpe.msgflags & RS_MSG_SYSTEM)) + || (mQuickViewFilter==QUICK_VIEW_SPAM && (fmpe.msgflags & RS_MSG_SPAM)) + || (mQuickViewFilter==QUICK_VIEW_ATTACHMENT && (fmpe.count >= 1)); #ifdef DEBUG_MESSAGE_MODEL std::cerr << "Passes filter: type=" << mFilterType << " s=\"" << s.toStdString() << "MsgFlags=" << fmpe.msgflags << " msgtags=" ; foreach(uint32_t i,fmpe.msgtags) std::cerr << i << " " ; @@ -433,6 +441,8 @@ QVariant RsMessageModel::sortRole(const Rs::Msgs::MsgInfoSummary& fmpe,int colum case COLUMN_THREAD_STAR: return QVariant((fmpe.msgflags & RS_MSG_STAR)? 1:0); + case COLUMN_THREAD_SPAM: return QVariant((fmpe.msgflags & RS_MSG_SPAM)? 1:0); + case COLUMN_THREAD_AUTHOR:{ QString name; @@ -451,7 +461,8 @@ QVariant RsMessageModel::displayRole(const Rs::Msgs::MsgInfoSummary& fmpe,int co case COLUMN_THREAD_SUBJECT: return QVariant(QString::fromUtf8(fmpe.title.c_str())); case COLUMN_THREAD_ATTACHMENT:return QVariant(QString::number(fmpe.count)); - case COLUMN_THREAD_STAR: + case COLUMN_THREAD_STAR: + case COLUMN_THREAD_SPAM: case COLUMN_THREAD_READ:return QVariant(); case COLUMN_THREAD_DATE:{ QDateTime qtime; @@ -486,7 +497,16 @@ QVariant RsMessageModel::displayRole(const Rs::Msgs::MsgInfoSummary& fmpe,int co } return text; } - case COLUMN_THREAD_AUTHOR: return QVariant(); + case COLUMN_THREAD_AUTHOR:{ + QString name; + RsGxsId id = RsGxsId(fmpe.srcId.toStdString()); + + if(id.isNull()) + return QVariant(tr("[Notification]")); + if(GxsIdTreeItemDelegate::computeName(id,name)) + return name; + return QVariant(tr("[Unknown]")); + } default: return QVariant("[ TODO ]"); @@ -509,42 +529,44 @@ QVariant RsMessageModel::userRole(const Rs::Msgs::MsgInfoSummary& fmpe,int col) QVariant RsMessageModel::decorationRole(const Rs::Msgs::MsgInfoSummary& fmpe,int col) const { - if(col == COLUMN_THREAD_READ) - if(fmpe.msgflags & (RS_MSG_NEW | RS_MSG_UNREAD_BY_USER)) - return QIcon(":/images/message-state-unread.png"); - else - return QIcon(":/images/message-state-read.png"); + bool exist=false; + switch(col) + { + case COLUMN_THREAD_READ: + return (fmpe.msgflags & (RS_MSG_NEW | RS_MSG_UNREAD_BY_USER)) + ? FilesDefs::getIconFromQtResourcePath(":/images/message-state-unread.png") + : FilesDefs::getIconFromQtResourcePath(":/images/message-state-read.png"); + case COLUMN_THREAD_SUBJECT: + { + if(fmpe.msgflags & RS_MSG_NEW ) return FilesDefs::getIconFromQtResourcePath(":/images/message-state-new.png"); + if(fmpe.msgflags & RS_MSG_USER_REQUEST) return FilesDefs::getIconFromQtResourcePath(":/images/user/user_request16.png"); + if(fmpe.msgflags & RS_MSG_FRIEND_RECOMMENDATION) return FilesDefs::getIconFromQtResourcePath(":/images/user/friend_suggestion16.png"); + if(fmpe.msgflags & RS_MSG_PUBLISH_KEY) return FilesDefs::getIconFromQtResourcePath(":/images/share-icon-16.png"); - if(col == COLUMN_THREAD_SUBJECT) - { - if(fmpe.msgflags & RS_MSG_NEW ) return QIcon(":/images/message-state-new.png"); - if(fmpe.msgflags & RS_MSG_USER_REQUEST) return QIcon(":/images/user/user_request16.png"); - if(fmpe.msgflags & RS_MSG_FRIEND_RECOMMENDATION) return QIcon(":/images/user/friend_suggestion16.png"); - if(fmpe.msgflags & RS_MSG_PUBLISH_KEY) return QIcon(":/images/share-icon-16.png"); + if(fmpe.msgflags & RS_MSG_UNREAD_BY_USER) + { + if((fmpe.msgflags & (RS_MSG_REPLIED | RS_MSG_FORWARDED)) == RS_MSG_REPLIED) return FilesDefs::getIconFromQtResourcePath(":/images/message-mail-replied.png"); + if((fmpe.msgflags & (RS_MSG_REPLIED | RS_MSG_FORWARDED)) == RS_MSG_FORWARDED) return FilesDefs::getIconFromQtResourcePath(":/images/message-mail-forwarded.png"); + if((fmpe.msgflags & (RS_MSG_REPLIED | RS_MSG_FORWARDED)) == (RS_MSG_REPLIED | RS_MSG_FORWARDED)) return FilesDefs::getIconFromQtResourcePath(":/images/message-mail-replied-forw.png"); - if(fmpe.msgflags & RS_MSG_UNREAD_BY_USER) - { - if((fmpe.msgflags & (RS_MSG_REPLIED | RS_MSG_FORWARDED)) == RS_MSG_REPLIED) return QIcon(":/images/message-mail-replied.png"); - if((fmpe.msgflags & (RS_MSG_REPLIED | RS_MSG_FORWARDED)) == RS_MSG_FORWARDED) return QIcon(":/images/message-mail-forwarded.png"); - if((fmpe.msgflags & (RS_MSG_REPLIED | RS_MSG_FORWARDED)) == (RS_MSG_REPLIED | RS_MSG_FORWARDED)) return QIcon(":/images/message-mail-replied-forw.png"); + return FilesDefs::getIconFromQtResourcePath(":/images/message-mail.png"); + } + if((fmpe.msgflags & (RS_MSG_REPLIED | RS_MSG_FORWARDED)) == RS_MSG_REPLIED) return FilesDefs::getIconFromQtResourcePath(":/images/message-mail-replied-read.png"); + if((fmpe.msgflags & (RS_MSG_REPLIED | RS_MSG_FORWARDED)) == RS_MSG_FORWARDED) return FilesDefs::getIconFromQtResourcePath(":/images/message-mail-forwarded-read.png"); + if((fmpe.msgflags & (RS_MSG_REPLIED | RS_MSG_FORWARDED)) == (RS_MSG_REPLIED | RS_MSG_FORWARDED)) return FilesDefs::getIconFromQtResourcePath(":/images/message-mail-replied-forw-read.png"); - return QIcon(":/images/message-mail.png"); - } - if((fmpe.msgflags & (RS_MSG_REPLIED | RS_MSG_FORWARDED)) == RS_MSG_REPLIED) return QIcon(":/images/message-mail-replied-read.png"); - if((fmpe.msgflags & (RS_MSG_REPLIED | RS_MSG_FORWARDED)) == RS_MSG_FORWARDED) return QIcon(":/images/message-mail-forwarded-read.png"); - if((fmpe.msgflags & (RS_MSG_REPLIED | RS_MSG_FORWARDED)) == (RS_MSG_REPLIED | RS_MSG_FORWARDED)) return QIcon(":/images/message-mail-replied-forw-read.png"); + return FilesDefs::getIconFromQtResourcePath(":/images/message-mail-read.png"); + } - return QIcon(":/images/message-mail-read.png"); - } + case COLUMN_THREAD_STAR: + return FilesDefs::getIconFromQtResourcePath((fmpe.msgflags & RS_MSG_STAR) ? (IMAGE_STAR_ON ): (IMAGE_STAR_OFF)); - if(col == COLUMN_THREAD_STAR) - return QIcon((fmpe.msgflags & RS_MSG_STAR) ? (IMAGE_STAR_ON ): (IMAGE_STAR_OFF)); - - bool isNew = fmpe.msgflags & (RS_MSG_NEW | RS_MSG_UNREAD_BY_USER); - - if(col == COLUMN_THREAD_READ) - return QIcon(isNew ? ":/images/message-state-unread.png": ":/images/message-state-read.png"); + case COLUMN_THREAD_SPAM: + return FilesDefs::getIconFromQtResourcePath((fmpe.msgflags & RS_MSG_SPAM) ? (IMAGE_SPAM_ON ): (IMAGE_SPAM_OFF)); + case COLUMN_THREAD_AUTHOR://Return icon as place holder. + return FilesDefs::getIconFromGxsIdCache(RsGxsId(fmpe.srcId.toStdString()),QIcon(), exist); + } return QVariant(); } @@ -671,6 +693,15 @@ void RsMessageModel::setMsgStar(const QModelIndex& i,bool star) emit dataChanged(i.sibling(i.row(),0),i.sibling(i.row(),COLUMN_THREAD_NB_COLUMNS-1)); } +void RsMessageModel::setMsgJunk(const QModelIndex& i,bool junk) +{ + preMods(); + rsMsgs->MessageJunk(i.data(MsgIdRole).toString().toStdString(),junk); + + emit dataChanged(i.sibling(i.row(),0),i.sibling(i.row(),COLUMN_THREAD_NB_COLUMNS-1)); +} + + QModelIndex RsMessageModel::getIndexOfMessage(const std::string& mid) const { // Brutal search. This is not so nice, so dont call that in a loop! If too costly, we'll use a map. diff --git a/retroshare-gui/src/gui/msgs/MessageModel.h b/retroshare-gui/src/gui/msgs/MessageModel.h index d6939fff1..8a74ed4b3 100644 --- a/retroshare-gui/src/gui/msgs/MessageModel.h +++ b/retroshare-gui/src/gui/msgs/MessageModel.h @@ -58,11 +58,12 @@ public: COLUMN_THREAD_SUBJECT = 0x02, COLUMN_THREAD_READ = 0x03, COLUMN_THREAD_AUTHOR = 0x04, - COLUMN_THREAD_DATE = 0x05, - COLUMN_THREAD_TAGS = 0x06, - COLUMN_THREAD_MSGID = 0x07, - COLUMN_THREAD_NB_COLUMNS = 0x08, - COLUMN_THREAD_CONTENT = 0x08, + COLUMN_THREAD_SPAM = 0x05, + COLUMN_THREAD_DATE = 0x06, + COLUMN_THREAD_TAGS = 0x07, + COLUMN_THREAD_MSGID = 0x08, + COLUMN_THREAD_NB_COLUMNS = 0x09, + COLUMN_THREAD_CONTENT = 0x09 }; enum QuickViewFilter { @@ -74,6 +75,8 @@ public: QUICK_VIEW_LATER = 0x05, QUICK_VIEW_STARRED = 0x06, QUICK_VIEW_SYSTEM = 0x07, + QUICK_VIEW_SPAM = 0x08, + QUICK_VIEW_ATTACHMENT = 0x09, QUICK_VIEW_USER = 100 }; @@ -107,7 +110,6 @@ public: void setCurrentBox(BoxName bn) ; void setQuickViewFilter(QuickViewFilter fn) ; - const RsMessageId& currentMessageId() const; void setFilter(FilterType filter_type, const QStringList& strings) ; int rowCount(const QModelIndex& parent = QModelIndex()) const override; @@ -147,6 +149,7 @@ public: // control over message flags and so on. This is handled by the model because it will allow it to update accordingly void setMsgReadStatus(const QModelIndex& i, bool read_status); void setMsgStar(const QModelIndex& index,bool star) ; + void setMsgJunk(const QModelIndex& index,bool junk) ; public slots: void updateMessages(); diff --git a/retroshare-gui/src/gui/msgs/MessageUserNotify.cpp b/retroshare-gui/src/gui/msgs/MessageUserNotify.cpp index fa586f1c8..4144867c6 100644 --- a/retroshare-gui/src/gui/msgs/MessageUserNotify.cpp +++ b/retroshare-gui/src/gui/msgs/MessageUserNotify.cpp @@ -18,6 +18,7 @@ * * *******************************************************************************/ +#include "gui/common/FilesDefs.h" #include "MessageUserNotify.h" #include "gui/notifyqt.h" #include "gui/MainWindow.h" @@ -40,12 +41,12 @@ bool MessageUserNotify::hasSetting(QString *name, QString *group) QIcon MessageUserNotify::getIcon() { - return QIcon(":/icons/png/messages.png"); + return FilesDefs::getIconFromQtResourcePath(":/icons/png/messages.png"); } QIcon MessageUserNotify::getMainIcon(bool hasNew) { - return hasNew ? QIcon(":/icons/png/messages-notify.png") : QIcon(":/icons/png/messages.png"); + return hasNew ? FilesDefs::getIconFromQtResourcePath(":/icons/png/messages-notify.png") : FilesDefs::getIconFromQtResourcePath(":/icons/png/messages.png"); } unsigned int MessageUserNotify::getNewCount() diff --git a/retroshare-gui/src/gui/msgs/MessageUserNotify.h b/retroshare-gui/src/gui/msgs/MessageUserNotify.h index b032123a0..34c65dad6 100644 --- a/retroshare-gui/src/gui/msgs/MessageUserNotify.h +++ b/retroshare-gui/src/gui/msgs/MessageUserNotify.h @@ -31,6 +31,7 @@ public: MessageUserNotify(QObject *parent = 0); virtual bool hasSetting(QString *name, QString *group); + virtual QString textInfo() const override { return tr("new mail(s)"); } private: virtual QIcon getIcon(); diff --git a/retroshare-gui/src/gui/msgs/MessageWidget.cpp b/retroshare-gui/src/gui/msgs/MessageWidget.cpp index d0a59e14b..d3f3af131 100644 --- a/retroshare-gui/src/gui/msgs/MessageWidget.cpp +++ b/retroshare-gui/src/gui/msgs/MessageWidget.cpp @@ -28,12 +28,15 @@ #include #include #include +#include +#include #include "gui/notifyqt.h" #include "gui/RetroShareLink.h" #include "gui/common/TagDefs.h" #include "gui/common/PeerDefs.h" #include "gui/common/Emoticons.h" +#include "gui/common/FilesDefs.h" #include "gui/settings/rsharesettings.h" #include "MessageComposer.h" #include "MessageWidget.h" @@ -49,8 +52,8 @@ #include /* Images for context menu icons */ -#define IMAGE_DOWNLOAD ":/images/start.png" -#define IMAGE_DOWNLOADALL ":/images/startall.png" +#define IMAGE_DOWNLOAD ":/icons/png/download.png" +#define IMAGE_DOWNLOADALL ":/icons/mail/downloadall.png" #define COLUMN_FILE_NAME 0 #define COLUMN_FILE_SIZE 1 @@ -130,12 +133,31 @@ MessageWidget::MessageWidget(bool controlled, QWidget *parent, Qt::WindowFlags f isControlled = controlled; isWindow = false; currMsgFlags = 0; + expandFiles = false; + + ui.actionTextBesideIcon->setData(Qt::ToolButtonTextBesideIcon); + ui.actionIconOnly->setData(Qt::ToolButtonIconOnly); connect(ui.msgList, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(msgfilelistWidgetCostumPopupMenu(QPoint))); connect(ui.expandFilesButton, SIGNAL(clicked()), this, SLOT(togglefileview())); connect(ui.downloadButton, SIGNAL(clicked()), this, SLOT(getallrecommended())); connect(ui.msgText, SIGNAL(anchorClicked(QUrl)), this, SLOT(anchorClicked(QUrl))); connect(ui.sendInviteButton, SIGNAL(clicked()), this, SLOT(sendInvite())); + + connect(ui.replyButton, SIGNAL(clicked()), this, SLOT(reply())); + connect(ui.replyallButton, SIGNAL(clicked()), this, SLOT(replyAll())); + connect(ui.forwardButton, SIGNAL(clicked()), this, SLOT(forward())); + connect(ui.deleteButton, SIGNAL(clicked()), this, SLOT(remove())); + + connect(ui.actionSaveAs, SIGNAL(triggered()), this, SLOT(saveAs())); + connect(ui.actionPrint, SIGNAL(triggered()), this, SLOT(print())); + connect(ui.actionPrintPreview, SIGNAL(triggered()), this, SLOT(printPreview())); + connect(ui.actionIconOnly, SIGNAL(triggered()), this, SLOT(buttonStyle())); + connect(ui.actionTextBesideIcon, SIGNAL(triggered()), this, SLOT(buttonStyle())); + + QAction *viewsource = new QAction(tr("View source"), this); + viewsource->setShortcut(QKeySequence("CTRL+O")); + connect(viewsource, SIGNAL(triggered()), this, SLOT(viewSource())); connect(NotifyQt::getInstance(), SIGNAL(messagesTagsChanged()), this, SLOT(messagesTagsChanged())); connect(NotifyQt::getInstance(), SIGNAL(messagesChanged()), this, SLOT(messagesChanged())); @@ -157,6 +179,16 @@ MessageWidget::MessageWidget(bool controlled, QWidget *parent, Qt::WindowFlags f msglheader->resizeSection (COLUMN_FILE_SIZE, 100); msglheader->resizeSection (COLUMN_FILE_HASH, 200); + QMenu *moremenu = new QMenu(); + moremenu->addAction(viewsource); + moremenu->addAction(ui.actionSaveAs); + moremenu->addAction(ui.actionPrint); + moremenu->addAction(ui.actionPrintPreview); + moremenu->addSeparator(); + moremenu->addAction(ui.actionTextBesideIcon); + moremenu->addAction(ui.actionIconOnly); + ui.moreButton->setMenu(moremenu); + QFont font = QFont("Arial", 10, QFont::Bold); ui.subjectText->setFont(font); @@ -251,15 +283,19 @@ void MessageWidget::processSettings(const QString &settingsGroup, bool load) // load settings // expandFiles - bool value = Settings->value("expandFiles", false).toBool(); - ui.expandFilesButton->setChecked(value); - ui.msgList->setVisible(value); - togglefileview(); + expandFiles = Settings->value("expandFiles", false).toBool(); + + // toolbar button style + Qt::ToolButtonStyle style = (Qt::ToolButtonStyle) Settings->value("ToolButon_Style", Qt::ToolButtonTextBesideIcon).toInt(); + setToolbarButtonStyle(style); } else { // save settings // expandFiles - Settings->setValue("expandFiles", ui.expandFilesButton->isChecked()); + Settings->setValue("expandFiles", expandFiles); + + //toolbar button style + Settings->setValue("ToolButon_Style", ui.replyButton->toolButtonStyle()); } Settings->endGroup(); @@ -279,25 +315,29 @@ void MessageWidget::msgfilelistWidgetCostumPopupMenu( QPoint /*point*/ ) { QMenu contextMnu(this); - contextMnu.addAction(QIcon(IMAGE_DOWNLOAD), tr("Download"), this, SLOT(getcurrentrecommended())); - contextMnu.addAction(QIcon(IMAGE_DOWNLOADALL), tr("Download all"), this, SLOT(getallrecommended())); + contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_DOWNLOAD), tr("Download"), this, SLOT(getcurrentrecommended())); + contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_DOWNLOADALL), tr("Download all"), this, SLOT(getallrecommended())); contextMnu.exec(QCursor::pos()); } -void MessageWidget::togglefileview() +void MessageWidget::togglefileview(bool noUpdate/*=false*/) { /* if msg header visible -> change icon and tooltip * three widgets... */ if (ui.expandFilesButton->isChecked()) { - ui.expandFilesButton->setIcon(QIcon(QString(":/images/edit_remove24.png"))); + ui.expandFilesButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/png/down-arrow.png"))); ui.expandFilesButton->setToolTip(tr("Hide the attachment pane")); } else { - ui.expandFilesButton->setIcon(QIcon(QString(":/images/edit_add24.png"))); + ui.expandFilesButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/png/up-arrow.png"))); ui.expandFilesButton->setToolTip(tr("Show the attachment pane")); } + if (!noUpdate) + expandFiles = ui.expandFilesButton->isChecked(); + + ui.msgList->setVisible(ui.expandFilesButton->isChecked()); } /* download the recommendations... */ @@ -389,13 +429,10 @@ void MessageWidget::messagesChanged() void MessageWidget::clearTagLabels() { /* clear all tags */ - while (tagLabels.size()) { - delete tagLabels.front(); - tagLabels.pop_front(); - } - while (ui.tagLayout->count()) { - delete ui.tagLayout->takeAt(0); - } + qDeleteAll(tagLabels); + tagLabels.clear(); + + misc::clearLayout(ui.tagLayout); ui.tagsLabel->setVisible(false); } @@ -473,6 +510,17 @@ void MessageWidget::fill(const std::string &msgId) clearTagLabels(); + ui.inviteFrame->hide(); + ui.expandFilesButton->setChecked(false); + ui.downloadButton->setEnabled(false); + togglefileview(true); + + ui.replyButton->setEnabled(false); + ui.replyallButton->setEnabled(false); + ui.forwardButton->setEnabled(false); + ui.deleteButton->setEnabled(false); + ui.moreButton->setEnabled(false); + currMsgFlags = 0; return; @@ -480,6 +528,13 @@ void MessageWidget::fill(const std::string &msgId) clearTagLabels(); + + ui.replyButton->setEnabled(true); + ui.replyallButton->setEnabled(true); + ui.forwardButton->setEnabled(true); + ui.deleteButton->setEnabled(true); + ui.moreButton->setEnabled(true); + MessageInfo msgInfo; if (rsMail->getMessage(currMsgId, msgInfo) == false) { std::cerr << "MessageWidget::fill() Couldn't find Msg" << std::endl; @@ -501,6 +556,7 @@ void MessageWidget::fill(const std::string &msgId) for (it = recList.begin(); it != recList.end(); ++it) { QTreeWidgetItem *item = new QTreeWidgetItem; item->setText(COLUMN_FILE_NAME, QString::fromUtf8(it->fname.c_str())); + item->setIcon(COLUMN_FILE_NAME, FilesDefs::getIconFromFileType(it->fname.c_str())); item->setText(COLUMN_FILE_SIZE, misc::friendlyUnit(it->size)); item->setData(COLUMN_FILE_SIZE, Qt::UserRole, QVariant(qulonglong(it->size)) ); item->setText(COLUMN_FILE_HASH, QString::fromStdString(it->hash.toStdString())); @@ -512,6 +568,9 @@ void MessageWidget::fill(const std::string &msgId) /* add the items in! */ ui.msgList->insertTopLevelItems(0, items); + ui.expandFilesButton->setChecked(expandFiles && (items.count()>0) ); + ui.downloadButton->setEnabled(items.count()>0); + togglefileview(true); /* iterate through the sources */ RetroShareLink link; @@ -610,10 +669,14 @@ void MessageWidget::fill(const std::string &msgId) } ui.subjectText->setText(QString::fromUtf8(msgInfo.title.c_str())); + + unsigned int formatTextFlag = RSHTML_FORMATTEXT_EMBED_LINKS ; - // emoticons disabled because of crazy cost. - //text = RsHtmlMsg(msgInfo.msgflags).formatText(ui.msgText->document(), QString::fromUtf8(msgInfo.msg.c_str()), RSHTML_FORMATTEXT_EMBED_SMILEYS | RSHTML_FORMATTEXT_EMBED_LINKS); - text = RsHtmlMsg(msgInfo.msgflags).formatText(ui.msgText->document(), QString::fromUtf8(msgInfo.msg.c_str()), RSHTML_FORMATTEXT_EMBED_LINKS); + // embed smileys ? + if (Settings->valueFromGroup(QString("Messages"), QString::fromUtf8("Emoticons"), true).toBool()) { + formatTextFlag |= RSHTML_FORMATTEXT_EMBED_SMILEYS ; + } + text = RsHtmlMsg(msgInfo.msgflags).formatText(ui.msgText->document(), QString::fromUtf8(msgInfo.msg.c_str()), formatTextFlag); ui.msgText->resetImagesStatus(Settings->getMsgLoadEmbeddedImages() || (msgInfo.msgflags & RS_MSG_LOAD_EMBEDDED_IMAGES)); ui.msgText->setHtml(text); @@ -651,8 +714,15 @@ void MessageWidget::remove() if (isWindow) { window()->close(); } else { - deleteLater(); + if (isControlled) { + currMsgId.clear(); + fill(currMsgId); + } else { + deleteLater(); + } } + + emit messageRemoved(); } void MessageWidget::print() @@ -796,3 +866,34 @@ void MessageWidget::sendInvite() //} } + +void MessageWidget::setToolbarButtonStyle(Qt::ToolButtonStyle style) +{ + ui.deleteButton->setToolButtonStyle(style); + ui.replyButton->setToolButtonStyle(style); + ui.replyallButton->setToolButtonStyle(style); + ui.forwardButton->setToolButtonStyle(style); + ui.moreButton->setToolButtonStyle(style); +} + +void MessageWidget::buttonStyle() +{ + setToolbarButtonStyle((Qt::ToolButtonStyle) dynamic_cast(sender())->data().toInt()); +} + +void MessageWidget::viewSource() +{ + QDialog *dialog = new QDialog(this); + QPlainTextEdit *pte = new QPlainTextEdit(dialog); + pte->setPlainText( ui.msgText->toHtml() ); + QGridLayout *gl = new QGridLayout(dialog); + gl->addWidget(pte,0,0,1,1); + dialog->setWindowTitle(tr("Document source")); + dialog->resize(500, 400); + + dialog->exec(); + + ui.msgText->setHtml(pte->toPlainText()); + + delete dialog; +} diff --git a/retroshare-gui/src/gui/msgs/MessageWidget.h b/retroshare-gui/src/gui/msgs/MessageWidget.h index a56a75d73..2bd630cb3 100644 --- a/retroshare-gui/src/gui/msgs/MessageWidget.h +++ b/retroshare-gui/src/gui/msgs/MessageWidget.h @@ -58,6 +58,8 @@ public: QString subject(bool noEmpty); +signals: + void messageRemoved(); private slots: void reply(); @@ -75,22 +77,26 @@ private slots: void messagesTagsChanged(); void messagesChanged(); - void togglefileview(); + void togglefileview(bool noUpdate = false); void getcurrentrecommended(); void getallrecommended(); void anchorClicked(const QUrl &url); void loadImagesAlways(); + void buttonStyle(); + void viewSource(); private: void clearTagLabels(); void showTagLabels(); + void setToolbarButtonStyle(Qt::ToolButtonStyle style); bool isControlled; bool isWindow; std::string currMsgId; unsigned int currMsgFlags; + bool expandFiles; QList tagLabels; diff --git a/retroshare-gui/src/gui/msgs/MessageWidget.ui b/retroshare-gui/src/gui/msgs/MessageWidget.ui index aece924af..24373dedb 100644 --- a/retroshare-gui/src/gui/msgs/MessageWidget.ui +++ b/retroshare-gui/src/gui/msgs/MessageWidget.ui @@ -51,81 +51,269 @@ 3 - - + + 0 0 + + + 9 + + - Subject: + Bcc: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + + + + + 0 + 0 + + + + + 9 + + + + To: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 9 + + + + Cc: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + false + + + + + + + + 0 + 0 + + + + + 9 + + + + Tags: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 9 + + + + Qt::LeftToRight + + + Date + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + 6 + - - - - 0 - 0 - + + + Reply - - true + + + :/icons/mail/reply.png:/icons/mail/reply.png - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + 22 + 22 + + + + Qt::ToolButtonTextBesideIcon - + + + <html><head/><body><p>Reply to All</p></body></html> + + + Reply all + + + + :/icons/mail/reply-all.png:/icons/mail/reply-all.png + + + + 22 + 22 + + + + Qt::ToolButtonTextBesideIcon + + + + + + + <html><head/><body><p>Forward selected Message</p></body></html> + + + Forward + + + + :/icons/mail/foward.png:/icons/mail/foward.png + + + + 22 + 22 + + + + Qt::ToolButtonTextBesideIcon + + + + + + + Delete + + + + :/icons/mail/delete.png:/icons/mail/delete.png + + + + 22 + 22 + + + + Qt::ToolButtonTextBesideIcon + + + + + - + 0 0 - - Qt::LeftToRight + + <html><head/><body><p>More actions</p></body></html> - Date + More - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + :/icons/png/options2.png:/icons/png/options2.png - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + 22 + 22 + + + + QToolButton::InstantPopup + + + Qt::ToolButtonTextBesideIcon - - + + - + 0 0 - - From: + + + 9 + 75 + true + - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - + @@ -145,6 +333,11 @@ 16777215 + + + 9 + + true @@ -153,23 +346,7 @@ - - - - - 0 - 0 - - - - To: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - + @@ -188,26 +365,7 @@ - - - - - 0 - 0 - - - - Cc: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - false - - - - + @@ -226,23 +384,7 @@ - - - - - 0 - 0 - - - - Bcc: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - + @@ -261,24 +403,50 @@ - - + + + + + 0 0 + + + 9 + + - Tags: + Subject: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + + + + 0 + 0 + + + + + 9 + + + + From: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + @@ -292,19 +460,6 @@ - - - - - 0 - 0 - - - - false - - - @@ -442,6 +597,19 @@ + + + + + 0 + 0 + + + + false + + + @@ -467,7 +635,7 @@ - :/images/attachment.png + :/icons/mail/attach24.png @@ -487,8 +655,8 @@ - - :/images/edit_remove24.png:/images/edit_remove24.png + + :/icons/png/down-arrow.png:/icons/png/down-arrow.png true @@ -539,8 +707,8 @@ Download all - - :/images/down.png:/images/down.png + + :/icons/png/download.png:/icons/png/download.png @@ -583,11 +751,29 @@ Print - + Print Preview + + + Save as + + + + + Buttons Text Beside Icon + + + Buttons Text Beside Icon + + + + + Buttons Icon only + + @@ -604,6 +790,7 @@ + diff --git a/retroshare-gui/src/gui/msgs/MessageWindow.cpp b/retroshare-gui/src/gui/msgs/MessageWindow.cpp index 0dc9e8ef4..ad1067367 100644 --- a/retroshare-gui/src/gui/msgs/MessageWindow.cpp +++ b/retroshare-gui/src/gui/msgs/MessageWindow.cpp @@ -18,6 +18,7 @@ * * *******************************************************************************/ +#include "gui/common/FilesDefs.h" #include "MessageWindow.h" #include "MessageWidget.h" #include "MessageComposer.h" @@ -55,19 +56,13 @@ MessageWindow::MessageWindow(QWidget *parent, Qt::WindowFlags flags) msgWidget = NULL; - // create tag menu + // create tag menu TagsMenu *menu = new TagsMenu (tr("Tags"), this); connect(menu, SIGNAL(aboutToShow()), this, SLOT(tagAboutToShow())); connect(menu, SIGNAL(tagSet(int, bool)), this, SLOT(tagSet(int, bool))); connect(menu, SIGNAL(tagRemoveAll()), this, SLOT(tagRemoveAll())); ui.tagButton->setMenu(menu); - - // create print menu - QMenu *printmenu = new QMenu(); - printmenu->addAction(ui.actionPrint); - printmenu->addAction(ui.actionPrint_Preview); - ui.printButton->setMenu(printmenu); // create view menu QMenu *viewmenu = new QMenu(); @@ -92,7 +87,7 @@ void MessageWindow::processSettings(bool load) // load settings /* toolbar button style */ - Qt::ToolButtonStyle style = (Qt::ToolButtonStyle) Settings->value("ToolButon_Stlye", Qt::ToolButtonIconOnly).toInt(); + Qt::ToolButtonStyle style = (Qt::ToolButtonStyle) Settings->value("ToolButon_Stlye", Qt::ToolButtonTextBesideIcon).toInt(); setToolbarButtonStyle(style); } else { // save settings @@ -115,11 +110,6 @@ void MessageWindow::addWidget(MessageWidget *widget) ui.msgLayout->addWidget(msgWidget); setWindowTitle(msgWidget->subject(true)); - msgWidget->connectAction(MessageWidget::ACTION_REMOVE, ui.removemessageButton); - msgWidget->connectAction(MessageWidget::ACTION_REPLY, ui.replymessageButton); - msgWidget->connectAction(MessageWidget::ACTION_REPLY_ALL, ui.replyallmessageButton); - msgWidget->connectAction(MessageWidget::ACTION_FORWARD, ui.forwardmessageButton); - msgWidget->connectAction(MessageWidget::ACTION_PRINT, ui.printButton); msgWidget->connectAction(MessageWidget::ACTION_PRINT, ui.actionPrint); msgWidget->connectAction(MessageWidget::ACTION_PRINT, actionPrint); msgWidget->connectAction(MessageWidget::ACTION_PRINT_PREVIEW, ui.actionPrint_Preview); @@ -191,12 +181,12 @@ void MessageWindow::setupFileActions() menuBar()->addMenu(menu); actionSaveAs = menu->addAction(tr("Save &As File")); - actionPrint = menu->addAction(QIcon(":/images/textedit/fileprint.png"), tr("&Print...")); + actionPrint = menu->addAction(FilesDefs::getIconFromQtResourcePath(":/images/textedit/fileprint.png"), tr("&Print...")); actionPrint->setShortcut(QKeySequence::Print); - actionPrintPreview = menu->addAction(QIcon(":/images/textedit/fileprint.png"), tr("Print Preview...")); + actionPrintPreview = menu->addAction(FilesDefs::getIconFromQtResourcePath(":/images/textedit/fileprint.png"), tr("Print Preview...")); -// a = new QAction(QIcon(":/images/textedit/exportpdf.png"), tr("&Export PDF..."), this); +// a = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/textedit/exportpdf.png"), tr("&Export PDF..."), this); // a->setShortcut(Qt::CTRL + Qt::Key_D); // connect(a, SIGNAL(triggered()), this, SLOT(filePrintPdf())); // menu->addAction(a); @@ -210,13 +200,7 @@ void MessageWindow::setupFileActions() void MessageWindow::setToolbarButtonStyle(Qt::ToolButtonStyle style) { ui.newmessageButton->setToolButtonStyle(style); - ui.removemessageButton->setToolButtonStyle(style); - ui.replymessageButton->setToolButtonStyle(style); - ui.replyallmessageButton->setToolButtonStyle(style); - ui.forwardmessageButton->setToolButtonStyle(style); ui.tagButton->setToolButtonStyle(style); - ui.printButton->setToolButtonStyle(style); - ui.viewtoolButton->setToolButtonStyle(style); } void MessageWindow::buttonStyle() diff --git a/retroshare-gui/src/gui/msgs/MessageWindow.ui b/retroshare-gui/src/gui/msgs/MessageWindow.ui index 7c5689dd7..6b796d226 100644 --- a/retroshare-gui/src/gui/msgs/MessageWindow.ui +++ b/retroshare-gui/src/gui/msgs/MessageWindow.ui @@ -81,8 +81,8 @@ Compose - - :/images/folder-draft24.png:/images/folder-draft24.png + + :/icons/mail/compose.png:/icons/mail/compose.png @@ -91,7 +91,7 @@ - Qt::ToolButtonIconOnly + Qt::ToolButtonTextBesideIcon true @@ -105,200 +105,7 @@ - - - - Qt::NoFocus - - - Reply to selected message - - - Reply - - - - :/images/replymail-pressed.png:/images/replymail-pressed.png - - - - 24 - 24 - - - - Qt::ToolButtonIconOnly - - - true - - - - - - - Qt::NoFocus - - - Reply all to selected message - - - Reply all - - - - :/images/replymailall24-hover.png:/images/replymailall24-hover.png - - - - 24 - 24 - - - - Qt::ToolButtonIconOnly - - - true - - - - - - - 0 - 0 - - - - - 16777 - 16777 - - - - Qt::NoFocus - - - Forward selected message - - - Forward - - - - :/images/mailforward24-hover.png:/images/mailforward24-hover.png - - - - 24 - 24 - - - - Qt::ToolButtonIconOnly - - - true - - - - - - - Qt::Vertical - - - - - - - Qt::NoFocus - - - Remove selected message - - - Delete - - - - :/images/deletemail24.png:/images/deletemail24.png - - - - 24 - 24 - - - - Qt::ToolButtonIconOnly - - - true - - - - - - - Qt::NoFocus - - - Print selected message - - - Print - - - - :/images/print24.png:/images/print24.png - - - - 24 - 24 - - - - QToolButton::MenuButtonPopup - - - Qt::ToolButtonIconOnly - - - true - - - - - - - Qt::NoFocus - - - Display - - - - :/images/looknfeel.png:/images/looknfeel.png - - - - 24 - 24 - - - - QToolButton::MenuButtonPopup - - - true - - - - Qt::Horizontal @@ -311,7 +118,33 @@ - + + + + Qt::NoFocus + + + Display + + + + :/icons/svg/design.svg:/icons/svg/design.svg + + + + 24 + 24 + + + + QToolButton::InstantPopup + + + true + + + + Qt::NoFocus @@ -323,8 +156,8 @@ Tags - - :/images/tag24.png:/images/tag24.png + + :/icons/mail/tags.png:/icons/mail/tags.png @@ -333,7 +166,10 @@ - QToolButton::MenuButtonPopup + QToolButton::InstantPopup + + + Qt::ToolButtonTextBesideIcon true @@ -355,7 +191,7 @@ 0 0 698 - 20 + 21 @@ -396,6 +232,7 @@ + diff --git a/retroshare-gui/src/gui/msgs/MessagesDialog.cpp b/retroshare-gui/src/gui/msgs/MessagesDialog.cpp index 62a027a51..acb19d900 100644 --- a/retroshare-gui/src/gui/msgs/MessagesDialog.cpp +++ b/retroshare-gui/src/gui/msgs/MessagesDialog.cpp @@ -53,13 +53,26 @@ #include /* Images for context menu icons */ -#define IMAGE_MESSAGE ":/images/folder-draft.png" -#define IMAGE_MESSAGEREMOVE ":/images/message-mail-imapdelete.png" +#define IMAGE_MAIL ":/icons/png/message.png" +#define IMAGE_MESSAGE ":/icons/mail/compose.png" +#define IMAGE_MESSAGEREMOVE ":/icons/mail/delete.png" #define IMAGE_STAR_ON ":/images/star-on-16.png" #define IMAGE_STAR_OFF ":/images/star-off-16.png" -#define IMAGE_SYSTEM ":/images/user/user_request16.png" +#define IMAGE_SYSTEM ":/icons/notification.png" #define IMAGE_DECRYPTMESSAGE ":/images/decrypt-mail.png" #define IMAGE_AUTHOR_INFO ":/images/info16.png" +#define IMAGE_NOTFICATION ":/icons/notification.png" +#define IMAGE_SPAM_ON ":/images/junk_on.png" +#define IMAGE_SPAM_OFF ":/images/junk_off.png" +#define IMAGE_ATTACHMENTS ":/icons/mail/attach24.png" +#define IMAGE_ATTACHMENT ":/icons/mail/attach16.png" + +#define IMAGE_INBOX ":/images/folder-inbox.png" +#define IMAGE_OUTBOX ":/images/folder-outbox.png" +#define IMAGE_SENT ":/images/folder-sent.png" +#define IMAGE_DRAFTS ":/images/folder-draft.png" +#define IMAGE_TRASH ":/images/folder-trash.png" +#define IMAGE_FOLDER ":/images/foldermail.png" #define ROLE_QUICKVIEW_TYPE Qt::UserRole #define ROLE_QUICKVIEW_ID Qt::UserRole + 1 @@ -71,6 +84,8 @@ #define QUICKVIEW_STATIC_ID_STARRED 1 #define QUICKVIEW_STATIC_ID_SYSTEM 2 +#define QUICKVIEW_STATIC_ID_SPAM 3 +#define QUICKVIEW_STATIC_ID_ATTACHMENT 4 #define ROW_INBOX 0 #define ROW_OUTBOX 1 @@ -82,7 +97,7 @@ class MessageSortFilterProxyModel: public QSortFilterProxyModel { public: - MessageSortFilterProxyModel(const QHeaderView *header,QObject *parent = NULL): QSortFilterProxyModel(parent),m_header(header) , m_sortingEnabled(false) {} + MessageSortFilterProxyModel(QObject *parent = NULL): QSortFilterProxyModel(parent), m_sortingEnabled(false) {} bool lessThan(const QModelIndex& left, const QModelIndex& right) const override { @@ -102,7 +117,6 @@ public: void setSortingEnabled(bool b) { m_sortingEnabled = b ; } private: - const QHeaderView *m_header ; bool m_sortingEnabled; }; @@ -116,20 +130,18 @@ MessagesDialog::MessagesDialog(QWidget *parent) inProcessSettings = false; inChange = false; lockUpdate = 0; - - ui.actionTextBesideIcon->setData(Qt::ToolButtonTextBesideIcon); - ui.actionIconOnly->setData(Qt::ToolButtonIconOnly); - ui.actionTextUnderIcon->setData(Qt::ToolButtonTextUnderIcon); + lastSelectedIndex = QModelIndex(); msgWidget = new MessageWidget(true, this); ui.msgLayout->addWidget(msgWidget); + connect(msgWidget, SIGNAL(messageRemoved()), this, SLOT(messageRemoved())); connectActions(); listMode = LIST_NOTHING; mMessageModel = new RsMessageModel(this); - mMessageProxyModel = new MessageSortFilterProxyModel(ui.messageTreeWidget->header(),this); + mMessageProxyModel = new MessageSortFilterProxyModel(this); mMessageProxyModel->setSourceModel(mMessageModel); mMessageProxyModel->setSortRole(RsMessageModel::SortRole); mMessageProxyModel->setDynamicSortFilter(false); @@ -141,11 +153,11 @@ MessagesDialog::MessagesDialog(QWidget *parent) changeBox(0); // set to inbox - ui.messageTreeWidget->setItemDelegateForColumn(RsMessageModel::COLUMN_THREAD_AUTHOR,new GxsIdTreeItemDelegate()) ; - RSElidedItemDelegate *itemDelegate = new RSElidedItemDelegate(this); itemDelegate->setSpacing(QSize(0, 2)); - ui.messageTreeWidget->setItemDelegate(itemDelegate); + ui.messageTreeWidget->setItemDelegateForColumn(RsMessageModel::COLUMN_THREAD_SUBJECT,itemDelegate); + + ui.messageTreeWidget->setItemDelegateForColumn(RsMessageModel::COLUMN_THREAD_AUTHOR,new GxsIdTreeItemDelegate()) ; // workaround for Qt bug, should be solved in next Qt release 4.7.0 // http://bugreports.qt.nokia.com/browse/QTBUG-8270 @@ -159,22 +171,13 @@ MessagesDialog::MessagesDialog(QWidget *parent) /* Set initial section sizes */ QHeaderView * msgwheader = ui.messageTreeWidget->header () ; - ui.forwardmessageButton->setToolTip(tr("Forward selected Message")); - ui.replyallmessageButton->setToolTip(tr("Reply to All")); - - QMenu *printmenu = new QMenu(); - printmenu->addAction(ui.actionPrint); - printmenu->addAction(ui.actionPrintPreview); - ui.printButton->setMenu(printmenu); - - QMenu *viewmenu = new QMenu(); - viewmenu->addAction(ui.actionTextBesideIcon); - viewmenu->addAction(ui.actionIconOnly); - ui.viewtoolButton->setMenu(viewmenu); - // Set initial size of the splitter ui.listSplitter->setStretchFactor(0, 0); ui.listSplitter->setStretchFactor(1, 1); + + // Set initial size of the splitter + ui.boxSplitter->setStretchFactor(0, 0); + ui.boxSplitter->setStretchFactor(1, 1); /* add filter actions */ ui.filterLineEdit->addFilter(QIcon(), tr("Subject"), RsMessageModel::COLUMN_THREAD_SUBJECT, tr("Search Subject")); @@ -197,6 +200,7 @@ MessagesDialog::MessagesDialog(QWidget *parent) msgwheader->resizeSection (RsMessageModel::COLUMN_THREAD_STAR, fm.width('0')*1.5); msgwheader->resizeSection (RsMessageModel::COLUMN_THREAD_ATTACHMENT, fm.width('0')*1.5); msgwheader->resizeSection (RsMessageModel::COLUMN_THREAD_READ, fm.width('0')*1.5); + msgwheader->resizeSection (RsMessageModel::COLUMN_THREAD_SPAM, fm.width('0')*1.5); msgwheader->resizeSection (RsMessageModel::COLUMN_THREAD_SUBJECT, fm.width("You have a message")*3.0); msgwheader->resizeSection (RsMessageModel::COLUMN_THREAD_AUTHOR, fm.width("[Retroshare]")*1.1); @@ -209,6 +213,7 @@ MessagesDialog::MessagesDialog(QWidget *parent) QHeaderView_setSectionResizeModeColumn(msgwheader, RsMessageModel::COLUMN_THREAD_STAR, QHeaderView::Fixed); QHeaderView_setSectionResizeModeColumn(msgwheader, RsMessageModel::COLUMN_THREAD_ATTACHMENT, QHeaderView::Fixed); QHeaderView_setSectionResizeModeColumn(msgwheader, RsMessageModel::COLUMN_THREAD_READ, QHeaderView::Fixed); + QHeaderView_setSectionResizeModeColumn(msgwheader, RsMessageModel::COLUMN_THREAD_SPAM, QHeaderView::Fixed); ui.messageTreeWidget->setSortingEnabled(true); @@ -238,8 +243,9 @@ MessagesDialog::MessagesDialog(QWidget *parent) //ui.messageTreeWidget->installEventFilter(this); - // remove close button of the the first tab - ui.tabWidget->hideCloseButton(0); + // remove close button of the the first tab + ui.tabWidget->hideCloseButton(0); + ui.tabWidget->setHideTabBarWithOneTab(true); int S = QFontMetricsF(font()).height(); QString help_str = tr( @@ -271,10 +277,6 @@ MessagesDialog::MessagesDialog(QWidget *parent) connect(ui.tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(tabCloseRequested(int))); connect(ui.newmessageButton, SIGNAL(clicked()), this, SLOT(newmessage())); - connect(ui.actionTextBesideIcon, SIGNAL(triggered()), this, SLOT(buttonStyle())); - connect(ui.actionIconOnly, SIGNAL(triggered()), this, SLOT(buttonStyle())); - connect(ui.actionTextUnderIcon, SIGNAL(triggered()), this, SLOT(buttonStyle())); - connect(ui.messageTreeWidget, SIGNAL(clicked(const QModelIndex&)) , this, SLOT(clicked(const QModelIndex&))); connect(ui.messageTreeWidget, SIGNAL(doubleClicked(const QModelIndex&)) , this, SLOT(doubleClicked(const QModelIndex&))); connect(ui.messageTreeWidget, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(messageTreeWidgetCustomPopupMenu(const QPoint&))); @@ -332,7 +334,7 @@ UserNotify *MessagesDialog::createUserNotify(QObject *parent) void MessagesDialog::processSettings(bool load) { - int messageTreeVersion = 2; // version number for the settings to solve problems when modifying the column count + int messageTreeVersion = 3; // version number for the settings to solve problems when modifying the column count inProcessSettings = true; @@ -353,10 +355,8 @@ void MessagesDialog::processSettings(bool load) // state of splitter ui.msgSplitter->restoreState(Settings->value("SplitterMsg").toByteArray()); ui.listSplitter->restoreState(Settings->value("SplitterList").toByteArray()); + ui.boxSplitter->restoreState(Settings->value("SplitterBox").toByteArray()); - /* toolbar button style */ - Qt::ToolButtonStyle style = (Qt::ToolButtonStyle) Settings->value("ToolButon_Style", Qt::ToolButtonIconOnly).toInt(); - setToolbarButtonStyle(style); } else { // save settings @@ -367,9 +367,7 @@ void MessagesDialog::processSettings(bool load) // state of splitter Settings->setValue("SplitterMsg", ui.msgSplitter->saveState()); Settings->setValue("SplitterList", ui.listSplitter->saveState()); - - /* toolbar button style */ - Settings->setValue("ToolButon_Style", ui.newmessageButton->toolButtonStyle()); + Settings->setValue("SplitterBox", ui.boxSplitter->saveState()); } Settings->endGroup(); @@ -424,7 +422,7 @@ void MessagesDialog::fillQuickView() // add static items item = new QListWidgetItem(tr("Starred"), ui.quickViewWidget); - item->setIcon(QIcon(IMAGE_STAR_ON)); + item->setIcon(FilesDefs::getIconFromQtResourcePath(IMAGE_STAR_ON)); item->setData(ROLE_QUICKVIEW_TYPE, QUICKVIEW_TYPE_STATIC); item->setData(ROLE_QUICKVIEW_ID, QUICKVIEW_STATIC_ID_STARRED); item->setData(ROLE_QUICKVIEW_TEXT, item->text()); // for updateMessageSummaryList @@ -434,7 +432,7 @@ void MessagesDialog::fillQuickView() } item = new QListWidgetItem(tr("System"), ui.quickViewWidget); - item->setIcon(QIcon(IMAGE_SYSTEM)); + item->setIcon(FilesDefs::getIconFromQtResourcePath(IMAGE_NOTFICATION)); item->setData(ROLE_QUICKVIEW_TYPE, QUICKVIEW_TYPE_STATIC); item->setData(ROLE_QUICKVIEW_ID, QUICKVIEW_STATIC_ID_SYSTEM); item->setData(ROLE_QUICKVIEW_TEXT, item->text()); // for updateMessageSummaryList @@ -443,12 +441,34 @@ void MessagesDialog::fillQuickView() itemToSelect = item; } + item = new QListWidgetItem(tr("Spam"), ui.quickViewWidget); + item->setIcon(FilesDefs::getIconFromQtResourcePath(IMAGE_SPAM_ON)); + item->setData(ROLE_QUICKVIEW_TYPE, QUICKVIEW_TYPE_STATIC); + item->setData(ROLE_QUICKVIEW_ID, QUICKVIEW_STATIC_ID_SPAM); + item->setData(ROLE_QUICKVIEW_TEXT, item->text()); // for updateMessageSummaryList + + if (selectedType == QUICKVIEW_TYPE_STATIC && selectedId == QUICKVIEW_STATIC_ID_SPAM) { + itemToSelect = item; + } + + item = new QListWidgetItem(tr("Attachment"), ui.quickViewWidget); + item->setIcon(FilesDefs::getIconFromQtResourcePath(IMAGE_ATTACHMENT)); + item->setData(ROLE_QUICKVIEW_TYPE, QUICKVIEW_TYPE_STATIC); + item->setData(ROLE_QUICKVIEW_ID, QUICKVIEW_STATIC_ID_ATTACHMENT); + item->setData(ROLE_QUICKVIEW_TEXT, item->text()); // for updateMessageSummaryList + + if (selectedType == QUICKVIEW_TYPE_STATIC && selectedId == QUICKVIEW_STATIC_ID_ATTACHMENT) { + itemToSelect = item; + } + for (tag = tags.types.begin(); tag != tags.types.end(); ++tag) { text = TagDefs::name(tag->first, tag->second.first); + QPixmap tagpixmap(16,16); + tagpixmap.fill(QColor(tag->second.second)); item = new QListWidgetItem (text, ui.quickViewWidget); - item->setForeground(QBrush(QColor(tag->second.second))); - item->setIcon(QIcon(":/images/foldermail.png")); + item->setData(Qt::ForegroundRole, QColor(tag->second.second)); + item->setIcon(tagpixmap); item->setData(ROLE_QUICKVIEW_TYPE, QUICKVIEW_TYPE_TAG); item->setData(ROLE_QUICKVIEW_ID, tag->first); item->setData(ROLE_QUICKVIEW_TEXT, text); // for updateMessageSummaryList @@ -469,18 +489,25 @@ void MessagesDialog::fillQuickView() int MessagesDialog::getSelectedMessages(QList& mid) { - //To check if the selection has more than one row. + //To check if the selection has more than one row. - mid.clear(); - QModelIndexList qmil = ui.messageTreeWidget->selectionModel()->selectedRows(); + mid.clear(); + QModelIndexList qmil = ui.messageTreeWidget->selectionModel()->selectedRows(); - foreach(const QModelIndex& m, qmil) + foreach(const QModelIndex& m, qmil) mid.push_back(m.sibling(m.row(),RsMessageModel::COLUMN_THREAD_MSGID).data(RsMessageModel::MsgIdRole).toString()) ; - return mid.size(); + if (mid.isEmpty()) + { + const QModelIndex& m = ui.messageTreeWidget->currentIndex(); + if (m.isValid()) + mid.push_back(m.sibling(m.row(),RsMessageModel::COLUMN_THREAD_MSGID).data(RsMessageModel::MsgIdRole).toString()) ; + } + + return mid.size(); } -int MessagesDialog::getSelectedMsgCount (QList *items, QList *itemsRead, QList *itemsUnread, QList *itemsStar) +int MessagesDialog::getSelectedMsgCount (QList *items, QList *itemsRead, QList *itemsUnread, QList *itemsStar, QList *itemsJunk) { QModelIndexList qmil = ui.messageTreeWidget->selectionModel()->selectedRows(); @@ -488,6 +515,7 @@ int MessagesDialog::getSelectedMsgCount (QList *items, QListclear(); if (itemsUnread) itemsUnread->clear(); if (itemsStar) itemsStar->clear(); + if (itemsJunk) itemsJunk->clear(); foreach(const QModelIndex& m, qmil) { @@ -495,10 +523,9 @@ int MessagesDialog::getSelectedMsgCount (QList *items, QListappend(m); if (m.data(RsMessageModel::UnreadRole).toBool()) - if (itemsUnread) - itemsUnread->append(m); - else if(itemsRead) - itemsRead->append(m); + { if (itemsUnread) itemsUnread->append(m); } + else + { if (itemsRead) itemsRead->append(m); } if (itemsStar && m.data(RsMessageModel::MsgFlagsRole).toInt() & RS_MSG_STAR) itemsStar->append(m); @@ -523,6 +550,14 @@ bool MessagesDialog::hasMessageStar(const QModelIndex& real_index) return real_index.data(RsMessageModel::MsgFlagsRole).toInt() & RS_MSG_STAR; } +bool MessagesDialog::hasMessageSpam(const QModelIndex& real_index) +{ + if (!real_index.isValid()) + return false; + + return real_index.data(RsMessageModel::MsgFlagsRole).toInt() & RS_MSG_SPAM; +} + void MessagesDialog::messageTreeWidgetCustomPopupMenu(QPoint /*point*/) { std::string cid; @@ -541,20 +576,21 @@ void MessagesDialog::messageTreeWidgetCustomPopupMenu(QPoint /*point*/) QList itemsRead; QList itemsUnread; QList itemsStar; + QList itemsJunk; - int nCount = getSelectedMsgCount (NULL, &itemsRead, &itemsUnread, &itemsStar); + int nCount = getSelectedMsgCount (NULL, &itemsRead, &itemsUnread, &itemsStar, &itemsJunk); /** Defines the actions for the context menu */ QMenu contextMnu( this ); - QAction *action = contextMnu.addAction(QIcon(":/images/view_split_top_bottom.png"), tr("Open in a new window"), this, SLOT(openAsWindow())); + QAction *action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/view_split_top_bottom.png"), tr("Open in a new window"), this, SLOT(openAsWindow())); if (nCount != 1) { action->setDisabled(true); } - action = contextMnu.addAction(QIcon(":/images/tab-dock.png"), tr("Open in a new tab"), this, SLOT(openAsTab())); + action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/tab-dock.png"), tr("Open in a new tab"), this, SLOT(openAsTab())); if (nCount != 1) { action->setDisabled(true); } @@ -572,12 +608,12 @@ void MessagesDialog::messageTreeWidgetCustomPopupMenu(QPoint /*point*/) contextMnu.addSeparator(); - action = contextMnu.addAction(QIcon(":/images/message-mail-read.png"), tr("Mark as read"), this, SLOT(markAsRead())); + action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/message-mail-read.png"), tr("Mark as read"), this, SLOT(markAsRead())); if (itemsUnread.isEmpty()) { action->setDisabled(true); } - action = contextMnu.addAction(QIcon(":/images/message-mail.png"), tr("Mark as unread"), this, SLOT(markAsUnread())); + action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/message-mail.png"), tr("Mark as unread"), this, SLOT(markAsUnread())); if (itemsRead.isEmpty()) { action->setDisabled(true); @@ -588,6 +624,11 @@ void MessagesDialog::messageTreeWidgetCustomPopupMenu(QPoint /*point*/) action->setChecked(itemsStar.size()); connect(action, SIGNAL(triggered(bool)), this, SLOT(markWithStar(bool))); + action = contextMnu.addAction(tr("Mark as Junk")); + action->setCheckable(true); + action->setChecked(itemsStar.size()); + connect(action, SIGNAL(triggered(bool)), this, SLOT(markWithJunk(bool))); + contextMnu.addSeparator(); // add tags @@ -606,7 +647,7 @@ void MessagesDialog::messageTreeWidgetCustomPopupMenu(QPoint /*point*/) action->setDisabled(true); } - action = contextMnu.addAction(QIcon(IMAGE_MESSAGEREMOVE), (nCount > 1) ? tr("Remove Messages") : tr("Remove Message"), this, SLOT(removemessage())); + action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MESSAGEREMOVE), (nCount > 1) ? tr("Remove Messages") : tr("Remove Message"), this, SLOT(removemessage())); if (nCount == 0) { action->setDisabled(true); } @@ -633,11 +674,11 @@ void MessagesDialog::messageTreeWidgetCustomPopupMenu(QPoint /*point*/) { std::cerr << "Src ID = " << msgInfo.rsgxsid_srcId << std::endl; - contextMnu.addAction(QIcon(IMAGE_AUTHOR_INFO),tr("Show author in People"),this,SLOT(showAuthorInPeopleTab())); + contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_AUTHOR_INFO),tr("Show author in People"),this,SLOT(showAuthorInPeopleTab())); contextMnu.addSeparator(); } - contextMnu.addAction(QIcon(IMAGE_MESSAGE), tr("New Message"), this, SLOT(newmessage())); + contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MESSAGE), tr("New Message"), this, SLOT(newmessage())); contextMnu.exec(QCursor::pos()); } @@ -645,17 +686,17 @@ void MessagesDialog::messageTreeWidgetCustomPopupMenu(QPoint /*point*/) void MessagesDialog::showAuthorInPeopleTab() { std::string cid; - std::string mid; + std::string mid; - if(!getCurrentMsg(cid, mid)) - return ; + if(!getCurrentMsg(cid, mid)) + return ; - MessageInfo msgInfo; - if (!rsMail->getMessage(mid, msgInfo)) - return; + MessageInfo msgInfo; + if (!rsMail->getMessage(mid, msgInfo)) + return; if(msgInfo.rsgxsid_srcId.isNull()) - return ; + return ; /* window will destroy itself! */ IdDialog *idDialog = dynamic_cast(MainWindow::getPage(MainWindow::People)); @@ -708,6 +749,7 @@ void MessagesDialog::openAsWindow() } msgWidget->activateWindow(); + connect(msgWidget, SIGNAL(messageRemoved()), this, SLOT(messageRemoved())); /* window will destroy itself! */ } @@ -725,8 +767,9 @@ void MessagesDialog::openAsTab() return; } - ui.tabWidget->addTab(msgWidget, msgWidget->subject(true)); + ui.tabWidget->addTab(msgWidget,FilesDefs::getIconFromQtResourcePath(IMAGE_MAIL), msgWidget->subject(true)); ui.tabWidget->setCurrentWidget(msgWidget); + connect(msgWidget, SIGNAL(messageRemoved()), this, SLOT(messageRemoved())); /* window will destroy itself! */ } @@ -752,55 +795,94 @@ void MessagesDialog::editmessage() void MessagesDialog::changeBox(int box_row) { - if (inChange) { - // already in change method - return; - } + if (inChange) { + // already in change method + return; + } - inChange = true; + inChange = true; - ui.quickViewWidget->setCurrentItem(NULL); - listMode = LIST_BOX; + QListWidgetItem* item = ui.listWidget->item(box_row); - switch(box_row) - { - case 0: mMessageModel->setCurrentBox(RsMessageModel::BOX_INBOX ); break; - case 1: mMessageModel->setCurrentBox(RsMessageModel::BOX_OUTBOX); break; - case 2: mMessageModel->setCurrentBox(RsMessageModel::BOX_DRAFTS); break; - case 3: mMessageModel->setCurrentBox(RsMessageModel::BOX_SENT ); break; - case 4: mMessageModel->setCurrentBox(RsMessageModel::BOX_TRASH ); break; - default: - mMessageModel->setCurrentBox(RsMessageModel::BOX_NONE); break; - } - inChange = false; + if (item) + { + ui.quickViewWidget->setCurrentItem(NULL); + changeQuickView(-1); + + listMode = LIST_BOX; + + QString placeholderText = tr("No message available in your %1.").arg(item->text()); + switch(box_row) + { + case ROW_INBOX: mMessageModel->setCurrentBox(RsMessageModel::BOX_INBOX ); + break; + case ROW_OUTBOX: mMessageModel->setCurrentBox(RsMessageModel::BOX_OUTBOX); + break; + case ROW_DRAFTBOX: mMessageModel->setCurrentBox(RsMessageModel::BOX_DRAFTS); + break; + case ROW_SENTBOX: mMessageModel->setCurrentBox(RsMessageModel::BOX_SENT ); + break; + case ROW_TRASHBOX: mMessageModel->setCurrentBox(RsMessageModel::BOX_TRASH ); + break; + default: + mMessageModel->setCurrentBox(RsMessageModel::BOX_NONE); + } + + insertMsgTxtAndFiles(ui.messageTreeWidget->currentIndex()); + ui.messageTreeWidget->setPlaceholderText(placeholderText); + } + else + { + mMessageModel->setCurrentBox(RsMessageModel::BOX_NONE); + } + inChange = false; } void MessagesDialog::changeQuickView(int newrow) { - Q_UNUSED(newrow); - ui.listWidget->setCurrentItem(NULL); - listMode = LIST_QUICKVIEW; + RsMessageModel::QuickViewFilter f = RsMessageModel::QUICK_VIEW_ALL ; + QListWidgetItem* item = ui.quickViewWidget->item(newrow); - RsMessageModel::QuickViewFilter f = RsMessageModel::QUICK_VIEW_ALL ; + if(item ) + { + ui.listWidget->setCurrentItem(NULL); + changeBox(-1); - if(newrow >= 0) // -1 means that nothing is selected - switch(newrow) - { - case 0x00: f = RsMessageModel::QUICK_VIEW_STARRED ; break; - case 0x01: f = RsMessageModel::QUICK_VIEW_SYSTEM ; break; - case 0x02: f = RsMessageModel::QUICK_VIEW_IMPORTANT; break; - case 0x03: f = RsMessageModel::QUICK_VIEW_WORK ; break; - case 0x04: f = RsMessageModel::QUICK_VIEW_PERSONAL ; break; - case 0x05: f = RsMessageModel::QUICK_VIEW_TODO ; break; - case 0x06: f = RsMessageModel::QUICK_VIEW_LATER ; break; - default: - f = RsMessageModel::QuickViewFilter( (int)RsMessageModel::QUICK_VIEW_USER + newrow - 0x07); + listMode = LIST_QUICKVIEW; + + QString placeholderText; + switch (item->data(ROLE_QUICKVIEW_TYPE).toInt()) { + case QUICKVIEW_TYPE_TAG: + { + placeholderText = tr("No message using %1 tag available.").arg(item->data(ROLE_QUICKVIEW_TEXT).toString()); + f = RsMessageModel::QuickViewFilter( item->data(ROLE_QUICKVIEW_ID).toUInt()); + } + break; + case QUICKVIEW_TYPE_STATIC: + { + placeholderText = tr("No %1 message available.").arg(item->data(ROLE_QUICKVIEW_TEXT).toString()); + switch (item->data(ROLE_QUICKVIEW_ID).toInt()) { + case QUICKVIEW_STATIC_ID_STARRED:{ + f = RsMessageModel::QUICK_VIEW_STARRED; + placeholderText = tr("No starred message available. Stars let you give messages a special status to make them easier to find. To star a message, click on the light gray star beside any message."); + } + break; + case QUICKVIEW_STATIC_ID_SYSTEM: f = RsMessageModel::QUICK_VIEW_SYSTEM; + break; + case QUICKVIEW_STATIC_ID_SPAM: f = RsMessageModel::QUICK_VIEW_SPAM; + break; + case QUICKVIEW_STATIC_ID_ATTACHMENT: f = RsMessageModel::QUICK_VIEW_ATTACHMENT; + } + } } - mMessageModel->setQuickViewFilter(f); - mMessageProxyModel->setFilterRegExp(QRegExp(RsMessageModel::FilterString)); // this triggers the update of the proxy model - insertMsgTxtAndFiles(ui.messageTreeWidget->currentIndex()); + insertMsgTxtAndFiles(ui.messageTreeWidget->currentIndex()); + ui.messageTreeWidget->setPlaceholderText(placeholderText); + } + + mMessageModel->setQuickViewFilter(f); + mMessageProxyModel->setFilterRegExp(QRegExp(RsMessageModel::FilterString)); // this triggers the update of the proxy model } void MessagesDialog::messagesTagsChanged() @@ -809,46 +891,52 @@ void MessagesDialog::messagesTagsChanged() mMessageModel->updateMessages(); } -static void InitIconAndFont(QTreeWidgetItem *item) -{ -} // click in messageTreeWidget -void MessagesDialog::currentChanged(const QModelIndex& new_proxy_index,const QModelIndex& old_proxy_index) +void MessagesDialog::currentChanged(const QModelIndex& new_proxy_index,const QModelIndex& /*old_proxy_index*/) { - if(!new_proxy_index.isValid()) - return; + if(!new_proxy_index.isValid()) + return; - // show current message directly + // show current message directly insertMsgTxtAndFiles(new_proxy_index); } // click in messageTreeWidget void MessagesDialog::clicked(const QModelIndex& proxy_index) { - if(!proxy_index.isValid()) - return; + if(!proxy_index.isValid()) + return; - QModelIndex real_index = mMessageProxyModel->mapToSource(proxy_index); + QModelIndex real_index = mMessageProxyModel->mapToSource(proxy_index); - switch (proxy_index.column()) - { - case RsMessageModel::COLUMN_THREAD_READ: - { - mMessageModel->setMsgReadStatus(real_index, !isMessageRead(proxy_index)); - insertMsgTxtAndFiles(proxy_index); - updateMessageSummaryList(); - return; - } - case RsMessageModel::COLUMN_THREAD_STAR: - { - mMessageModel->setMsgStar(real_index, !hasMessageStar(proxy_index)); - return; - } - } + switch (proxy_index.column()) + { + case RsMessageModel::COLUMN_THREAD_READ: + { + mMessageModel->setMsgReadStatus(real_index, !isMessageRead(proxy_index)); + //Already updated by currentChanged + //insertMsgTxtAndFiles(proxy_index); + updateMessageSummaryList(); + return; + } + case RsMessageModel::COLUMN_THREAD_STAR: + { + mMessageModel->setMsgStar(real_index, !hasMessageStar(proxy_index)); + updateMessageSummaryList(); + return; + } + case RsMessageModel::COLUMN_THREAD_SPAM: + { + mMessageModel->setMsgJunk(real_index, !hasMessageSpam(proxy_index)); + updateMessageSummaryList(); + return; + } + } - // show current message directly - insertMsgTxtAndFiles(proxy_index); + // show current message directly + //Already updated by currentChanged + //insertMsgTxtAndFiles(proxy_index); } // double click in messageTreeWidget @@ -892,7 +980,7 @@ void MessagesDialog::updateCurrentMessage() void MessagesDialog::markAsRead() { QList itemsUnread; - getSelectedMsgCount (NULL, NULL, &itemsUnread, NULL); + getSelectedMsgCount (NULL, NULL, &itemsUnread, NULL, NULL); foreach(const QModelIndex& index,itemsUnread) mMessageModel->setMsgReadStatus(index,true); @@ -903,7 +991,7 @@ void MessagesDialog::markAsRead() void MessagesDialog::markAsUnread() { QList itemsRead; - getSelectedMsgCount (NULL, &itemsRead, NULL, NULL); + getSelectedMsgCount (NULL, &itemsRead, NULL, NULL, NULL); foreach(const QModelIndex& index,itemsRead) mMessageModel->setMsgReadStatus(index,false); @@ -919,7 +1007,13 @@ void MessagesDialog::markWithStar(bool checked) mMessageModel->setMsgStar(index, checked); } +void MessagesDialog::markWithJunk(bool checked) +{ + QModelIndexList lst = ui.messageTreeWidget->selectionModel()->selectedRows(); + foreach(const QModelIndex& index,lst) + mMessageModel->setMsgJunk(index, checked); +} void MessagesDialog::insertMsgTxtAndFiles(const QModelIndex& proxy_index) { @@ -929,14 +1023,20 @@ void MessagesDialog::insertMsgTxtAndFiles(const QModelIndex& proxy_index) QModelIndex real_index = mMessageProxyModel->mapToSource(proxy_index); - if(!real_index.isValid()) + if(!real_index.isValid()) { mCurrMsgId.clear(); msgWidget->fill(mCurrMsgId); updateInterface(); + lastSelectedIndex = QModelIndex(); return; } - mid = real_index.data(RsMessageModel::MsgIdRole).toString().toStdString(); + + lastSelectedIndex = proxy_index; + if (!ui.messageTreeWidget->indexBelow(proxy_index).isValid()) + lastSelectedIndex = ui.messageTreeWidget->indexAbove(proxy_index); + + mid = real_index.data(RsMessageModel::MsgIdRole).toString().toStdString(); /* Save the Data.... for later */ @@ -958,9 +1058,8 @@ void MessagesDialog::insertMsgTxtAndFiles(const QModelIndex& proxy_index) else if ((msgInfo.msgflags & RS_MSG_UNREAD_BY_USER) && bSetToReadOnActive) // set to read mMessageModel->setMsgReadStatus(real_index, true); - updateInterface(); + msgWidget->fill(mid); updateMessageSummaryList(); - msgWidget->fill(mid); } bool MessagesDialog::getCurrentMsg(std::string &cid, std::string &mid) @@ -1001,6 +1100,15 @@ void MessagesDialog::removemessage() mMessageModel->updateMessages(); updateMessageSummaryList(); + messageRemoved(); +} + +void MessagesDialog::messageRemoved() +{ + if (lastSelectedIndex.isValid()) + ui.messageTreeWidget->setCurrentIndex(lastSelectedIndex); + else + insertMsgTxtAndFiles(QModelIndex()); } void MessagesDialog::undeletemessage() @@ -1015,25 +1123,6 @@ void MessagesDialog::undeletemessage() updateMessageSummaryList(); } -void MessagesDialog::setToolbarButtonStyle(Qt::ToolButtonStyle style) -{ - ui.newmessageButton->setToolButtonStyle(style); - ui.removemessageButton->setToolButtonStyle(style); - ui.replymessageButton->setToolButtonStyle(style); - ui.replyallmessageButton->setToolButtonStyle(style); - ui.forwardmessageButton->setToolButtonStyle(style); - ui.tagButton->setToolButtonStyle(style); - ui.printButton->setToolButtonStyle(style); - ui.viewtoolButton->setToolButtonStyle(style); -} - -void MessagesDialog::buttonStyle() -{ - setToolbarButtonStyle((Qt::ToolButtonStyle) dynamic_cast(sender())->data().toInt()); -} - - - void MessagesDialog::filterChanged(const QString& text) { QStringList items = text.split(' ',QString::SkipEmptyParts); @@ -1095,6 +1184,8 @@ void MessagesDialog::updateMessageSummaryList() unsigned int trashboxCount = 0; unsigned int starredCount = 0; unsigned int systemCount = 0; + unsigned int spamCount = 0; + unsigned int attachmentCount = 0; /* calculating the new messages */ @@ -1123,6 +1214,10 @@ void MessagesDialog::updateMessageSummaryList() ++systemCount; } + if (it->msgflags & RS_MSG_SPAM) { + ++spamCount; + } + /* calculate box */ if (it->msgflags & RS_MSG_TRASH) { ++trashboxCount; @@ -1189,8 +1284,8 @@ void MessagesDialog::updateMessageSummaryList() QFont qf = item->font(); qf.setBold(true); item->setFont(qf); - item->setIcon(QIcon(":/images/folder-inbox-new.png")); - item->setForeground(QBrush(mTextColorInbox)); + item->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/folder-inbox-new.png")); + item->setData(Qt::ForegroundRole, mTextColorInbox); } else { @@ -1199,8 +1294,8 @@ void MessagesDialog::updateMessageSummaryList() QFont qf = item->font(); qf.setBold(false); item->setFont(qf); - item->setIcon(QIcon(":/images/folder-inbox.png")); - item->setForeground(QBrush(ui.messageTreeWidget->palette().color(QPalette::Text))); + item->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/folder-inbox.png")); + item->setData(Qt::ForegroundRole, QVariant()); } //QList QListWidget::findItems ( const QString & text, Qt::MatchFlags flags ) const @@ -1282,6 +1377,12 @@ void MessagesDialog::updateMessageSummaryList() case QUICKVIEW_STATIC_ID_SYSTEM: text += " (" + QString::number(systemCount) + ")"; break; + case QUICKVIEW_STATIC_ID_SPAM: + text += " (" + QString::number(spamCount) + ")"; + break; + case QUICKVIEW_STATIC_ID_ATTACHMENT: + text += " (" + QString::number(attachmentCount) + ")"; + break; } item->setText(text); @@ -1289,6 +1390,7 @@ void MessagesDialog::updateMessageSummaryList() break; } } + updateInterface(); } void MessagesDialog::tagAboutToShow() @@ -1386,14 +1488,9 @@ void MessagesDialog::connectActions() msg = dynamic_cast(ui.tabWidget->widget(tab)); } - ui.replymessageButton->disconnect(); - ui.replyallmessageButton->disconnect(); - ui.forwardmessageButton->disconnect(); - ui.printButton->disconnect(); ui.actionPrint->disconnect(); ui.actionPrintPreview->disconnect(); ui.actionSaveAs->disconnect(); - ui.removemessageButton->disconnect(); ui.actionReply->disconnect(); ui.actionReplyAll->disconnect(); @@ -1409,18 +1506,15 @@ void MessagesDialog::connectActions() if (msg) { if (tab == 0) { // connect with own slot to remove multiple messages - connect(ui.removemessageButton, SIGNAL(clicked()), this, SLOT(removemessage())); + //connect(ui.removemessageButton, SIGNAL(clicked()), this, SLOT(removemessage())); } else { - msg->connectAction(MessageWidget::ACTION_REMOVE, ui.removemessageButton); + //msg->connectAction(MessageWidget::ACTION_REMOVE, ui.removemessageButton); } - msg->connectAction(MessageWidget::ACTION_REPLY, ui.replymessageButton); - msg->connectAction(MessageWidget::ACTION_REPLY_ALL, ui.replyallmessageButton); - msg->connectAction(MessageWidget::ACTION_FORWARD, ui.forwardmessageButton); - msg->connectAction(MessageWidget::ACTION_PRINT, ui.printButton); msg->connectAction(MessageWidget::ACTION_PRINT, ui.actionPrint); msg->connectAction(MessageWidget::ACTION_PRINT_PREVIEW, ui.actionPrintPreview); msg->connectAction(MessageWidget::ACTION_SAVE_AS, ui.actionSaveAs); } + } void MessagesDialog::updateInterface() @@ -1430,24 +1524,35 @@ void MessagesDialog::updateInterface() int tab = ui.tabWidget->currentIndex(); if (tab == 0) - { - QList msgs; + { + QList msgs; count = getSelectedMessages(msgs); } - else - { + else + { MessageWidget *msg = dynamic_cast(ui.tabWidget->widget(tab)); if (msg && msg->msgId().empty() == false) count = 1; } - ui.replymessageButton->setEnabled(count == 1); - ui.replyallmessageButton->setEnabled(count == 1); - ui.forwardmessageButton->setEnabled(count == 1); - ui.printButton->setEnabled(count == 1); ui.actionPrint->setEnabled(count == 1); ui.actionPrintPreview->setEnabled(count == 1); ui.actionSaveAs->setEnabled(count == 1); - ui.removemessageButton->setEnabled(count >= 1); ui.tagButton->setEnabled(count >= 1); + + if (ui.listWidget->currentItem()) + { + ui.tabWidget->setTabText(0, ui.listWidget->currentItem()->text()); + ui.tabWidget->setTabIcon(0, ui.listWidget->currentItem()->icon()); + } + else if (ui.quickViewWidget->currentItem()) + { + ui.tabWidget->setTabText(0, ui.quickViewWidget->currentItem()->text()); + ui.tabWidget->setTabIcon(0, ui.quickViewWidget->currentItem()->icon()); + } + else + { + ui.tabWidget->setTabText(0, tr("No Box selected.")); + ui.tabWidget->setTabIcon(0, FilesDefs::getIconFromQtResourcePath(":/icons/warning_yellow_128.png")); + } } diff --git a/retroshare-gui/src/gui/msgs/MessagesDialog.h b/retroshare-gui/src/gui/msgs/MessagesDialog.h index a8115d466..bf0148cf5 100644 --- a/retroshare-gui/src/gui/msgs/MessagesDialog.h +++ b/retroshare-gui/src/gui/msgs/MessagesDialog.h @@ -23,7 +23,8 @@ #include -#include "mainpage.h" +#include + #include "ui_MessagesDialog.h" #define IMAGE_MESSAGES ":/icons/png/message.png" @@ -57,10 +58,6 @@ public: void setTextColorInbox(QColor color) { mTextColorInbox = color; } -signals: - void messagesAboutToLoad(); - void messagesLoaded(); - protected: virtual UserNotify *createUserNotify(QObject *parent) override; bool eventFilter(QObject *obj, QEvent *ev); @@ -69,6 +66,7 @@ protected: public slots: //void insertMessages(); void messagesTagsChanged(); + void messageRemoved(); void preModelUpdate(); void postModelUpdate(); @@ -97,11 +95,10 @@ private slots: void markAsRead(); void markAsUnread(); void markWithStar(bool checked); + void markWithJunk(bool checked); void emptyTrash(); - void buttonStyle(); - void filterChanged(const QString &text); void filterColumnChanged(int column); @@ -123,13 +120,13 @@ private: bool getCurrentMsg(std::string &cid, std::string &mid); void setMsgAsReadUnread(const QList &items, bool read); - int getSelectedMsgCount (QList *items, QList *itemsRead, QList *itemsUnread, QList *itemsStar); + int getSelectedMsgCount (QList *items, QList *itemsRead, QList *itemsUnread, QList *itemsStar, QList *itemsJunk); bool isMessageRead(const QModelIndex &real_index); bool hasMessageStar(const QModelIndex &index); + bool hasMessageSpam(const QModelIndex &index); void processSettings(bool load); - void setToolbarButtonStyle(Qt::ToolButtonStyle style); void fillQuickView(); void closeTab(const std::string &msgId); @@ -159,6 +156,7 @@ private: Ui::MessagesDialog ui; QList mTmpSavedSelectedIds; + QModelIndex lastSelectedIndex; }; #endif diff --git a/retroshare-gui/src/gui/msgs/MessagesDialog.ui b/retroshare-gui/src/gui/msgs/MessagesDialog.ui index 8c830e042..183396a2c 100644 --- a/retroshare-gui/src/gui/msgs/MessagesDialog.ui +++ b/retroshare-gui/src/gui/msgs/MessagesDialog.ui @@ -23,497 +23,20 @@ 0 - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 28 - - - - QFrame::NoFrame - - - QFrame::Sunken - - - - 2 - - - 2 - - - 2 - - - 2 - - - 6 - - - - - Qt::Vertical - - - - - - - - 0 - 0 - - - - - 167777 - 167777 - - - - Qt::NoFocus - - - New Message - - - Compose - - - - :/images/folder-draft24.png:/images/folder-draft24.png - - - - 24 - 24 - - - - Qt::ToolButtonIconOnly - - - true - - - - - - - - 0 - 0 - - - - - 16777 - 16777 - - - - Qt::NoFocus - - - Remove selected message - - - Delete - - - - :/images/deletemail24.png:/images/deletemail24.png - - - - 24 - 24 - - - - Qt::ToolButtonIconOnly - - - true - - - - - - - Messages - - - - - - - - 200 - 0 - - - - - 200 - 16777215 - - - - - - - - - 0 - 0 - - - - - 16777 - 16777 - - - - Qt::NoFocus - - - Forward selected message - - - Foward - - - - :/images/mailforward24-hover.png:/images/mailforward24-hover.png - - - - 24 - 24 - - - - Qt::ToolButtonIconOnly - - - true - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777 - 16777 - - - - Qt::NoFocus - - - Print selected message - - - Print - - - - :/images/print24.png:/images/print24.png - - - - 24 - 24 - - - - QToolButton::MenuButtonPopup - - - Qt::ToolButtonIconOnly - - - true - - - - - - - - 0 - 0 - - - - Qt::NoFocus - - - Display - - - - :/images/looknfeel.png:/images/looknfeel.png - - - - 24 - 24 - - - - QToolButton::MenuButtonPopup - - - true - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 24 - 24 - - - - - - - :/icons/png/message.png - - - true - - - - - - - Qt::NoFocus - - - Tags - - - Tags - - - - :/images/tag24.png:/images/tag24.png - - - - 24 - 24 - - - - QToolButton::MenuButtonPopup - - - true - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777 - 16777 - - - - Qt::NoFocus - - - Reply to selected message - - - Reply - - - - :/images/replymail-pressed.png:/images/replymail-pressed.png - - - - 24 - 24 - - - - Qt::ToolButtonIconOnly - - - true - - - - - - - - 0 - 0 - - - - - 16777 - 16777 - - - - Qt::NoFocus - - - Reply all to selected message - - - Reply all - - - - :/images/replymailall24-hover.png:/images/replymailall24-hover.png - - - - 24 - 24 - - - - Qt::ToolButtonIconOnly - - - true - - - - - - - Qt::NoFocus - - - - :/icons/help_64.png:/icons/help_64.png - - - true - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Horizontal - - - QSizePolicy::Minimum - - - - 40 - 20 - - - - - - - - Qt::Vertical - - - - - - true - - Main Tab + + + :/icons/png/message.png:/icons/png/message.png - + + Messages + + 0 @@ -526,9 +49,6 @@ 0 - - 0 - @@ -548,99 +68,9 @@ 0 - - 6 - - + 0 - - - - - 0 - 0 - - - - Qt::CustomContextMenu - - - - Inbox - - - - :/images/folder-inbox.png:/images/folder-inbox.png - - - - - Outbox - - - - :/images/folder-outbox.png:/images/folder-outbox.png - - - - - Draft - - - - :/images/folder-draft.png:/images/folder-draft.png - - - - - Sent - - - - :/images/folder-sent.png:/images/folder-sent.png - - - - - Trash - - - - :/images/folder-trash.png:/images/folder-trash.png - - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - - - - - 0 - 18 - - - - Total Inbox: - - - @@ -654,104 +84,321 @@ 3 - - + + - 16 - 16 + 0 + 0 - - :/images/foldermail.png + + + 167777 + 167777 + + + + Qt::NoFocus + + + New Message - - - - - Folders + Compose - - - - - - - - - true - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 3 - - - 3 - - - - + + + :/icons/mail/compose.png:/icons/mail/compose.png + + - 16 - 16 + 24 + 24 - - :/images/tag24.png + + Qt::ToolButtonTextBesideIcon - + true - - - - Quick View - - - + + + + Qt::Vertical + + + + + 0 + 0 + + + + Qt::CustomContextMenu + + + + Inbox + + + + :/images/folder-inbox.png:/images/folder-inbox.png + + + + + Outbox + + + + :/images/folder-outbox.png:/images/folder-outbox.png + + + + + Draft + + + + :/images/folder-draft.png:/images/folder-draft.png + + + + + Sent + + + + :/images/folder-sent.png:/images/folder-sent.png + + + + + Trash + + + + :/images/folder-trash.png:/images/folder-trash.png + + + + + + + 0 + + + + + true + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 3 + + + 3 + + + 3 + + + + + + 16 + 16 + + + + :/icons/mail/filter24.png + + + true + + + + + + + Quick View + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + + + + + + + 0 + 18 + + + + Total Inbox: + + + - - - Qt::Vertical - - - - Qt::CustomContextMenu + + + + 0 - - QAbstractItemView::ExtendedSelection - - - false - - - true - - - true - - - false - - - true - - - - - + + + + 3 + + + 3 + + + + + Qt::NoFocus + + + Tags + + + Tags + + + + :/icons/mail/tags.png:/icons/mail/tags.png + + + + 24 + 24 + + + + QToolButton::InstantPopup + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + + + + Qt::NoFocus + + + + :/icons/help_64.png:/icons/help_64.png + + + true + + + true + + + + + + + + + Qt::Vertical + + + + Qt::CustomContextMenu + + + QAbstractItemView::ExtendedSelection + + + false + + + true + + + true + + + false + + + true + + + + + + + + @@ -773,60 +420,36 @@ Print Preview - - - Buttons Icon Only - - - Buttons Icon Only - - - - - Buttons Text Beside Icon - - - Buttons with Text - - - - - Buttons Text Under Icon - - - Set Text Under Icon - - Save As... - + - - :/images/mail_reply.png:/images/mail_reply.png + + :/icons/mail/foward.png:/icons/mail/foward.png - Reply to Message + Forward Message - - :/images/mail_replyall.png:/images/mail_replyall.png + + :/icons/mail/reply-all.png:/icons/mail/reply-all.png Reply to All - + - - :/images/mail_forward.png:/images/mail_forward.png + + :/icons/mail/reply.png:/icons/mail/reply.png - Forward Message + Reply to Message @@ -836,20 +459,20 @@ QLineEdit

gui/common/LineEditClear.h
- - StyledLabel - QLabel -
gui/common/StyledLabel.h
-
RSTabWidget QTabWidget
gui/common/RSTabWidget.h
1
+ + RSTreeView + QTreeView +
gui/common/RSTreeView.h
+ 1 +
- replymessageButton listWidget diff --git a/retroshare-gui/src/gui/notifyqt.cpp b/retroshare-gui/src/gui/notifyqt.cpp index a9bdd2705..7bb1b6cf2 100644 --- a/retroshare-gui/src/gui/notifyqt.cpp +++ b/retroshare-gui/src/gui/notifyqt.cpp @@ -18,6 +18,7 @@ * * *******************************************************************************/ +#include "gui/common/FilesDefs.h" #include #include @@ -32,7 +33,7 @@ #include #include -#include "RsAutoUpdatePage.h" +#include #include "MainWindow.h" #include "toaster/OnlineToaster.h" @@ -199,51 +200,6 @@ class SignatureEventData std::string reason ; }; -bool NotifyQt::askForDeferredSelfSignature(const void *data, const uint32_t len, unsigned char *sign, unsigned int *signlen,int& signature_result, std::string reason /*=""*/) -{ - { - QMutexLocker m(&_mutex) ; - - std::cerr << "NotifyQt:: deferred signature event requeted. " << std::endl; - - // Look into the queue - - Sha1CheckSum chksum = RsDirUtil::sha1sum((uint8_t*)data,len) ; - - std::map::iterator it = _deferred_signature_queue.find(chksum.toStdString()) ; - signature_result = SELF_SIGNATURE_RESULT_PENDING ; - - if(it != _deferred_signature_queue.end()) - { - signature_result = it->second->signature_result ; - - if(it->second->signature_result != SELF_SIGNATURE_RESULT_PENDING) // found it. Copy the result, and remove from the queue. - { - // We should check for the exact data match, for the sake of being totally secure. - // - std::cerr << "Found into queue: returning it" << std::endl; - - memcpy(sign,it->second->sign,*it->second->signlen) ; - *signlen = *(it->second->signlen) ; - - delete it->second ; - _deferred_signature_queue.erase(it) ; - } - return true ; // already registered, but not done yet. - } - - // Not found. Store in the queue and emit a signal. - // - std::cerr << "NotifyQt:: deferred signature event requeted. Pushing into queue" << std::endl; - - SignatureEventData *edta = new SignatureEventData(data,len,*signlen, reason) ; - - _deferred_signature_queue[chksum.toStdString()] = edta ; - } - emit deferredSignatureHandlingRequested() ; - return true; -} - void NotifyQt::handleSignatureEvent() { std::cerr << "NotifyQt:: performing a deferred signature in the main GUI thread." << std::endl; @@ -321,7 +277,7 @@ bool NotifyQt::askForPluginConfirmation(const std::string& plugin_file_name, con text += "" ; dialog.setText(text) ; - dialog.setWindowIcon(QIcon(":/icons/logo_128.png")); + dialog.setWindowIcon(FilesDefs::getIconFromQtResourcePath(":/icons/logo_128.png")); dialog.setStandardButtons(QMessageBox::Yes | QMessageBox::No) ; int ret = dialog.exec(); @@ -349,36 +305,6 @@ void NotifyQt::notifyDiscInfoChanged() emit discInfoChanged() ; } -#ifdef TO_REMOVE -void NotifyQt::notifyDownloadComplete(const std::string& fileHash) -{ - { - QMutexLocker m(&_mutex) ; - if(!_enabled) - return ; - } - -#ifdef NOTIFY_DEBUG - std::cerr << "Notifyqt::notifyDownloadComplete notified that a download is completed" << std::endl; -#endif - - emit downloadComplete(QString::fromStdString(fileHash)); -} - -void NotifyQt::notifyDownloadCompleteCount(uint32_t count) -{ - { - QMutexLocker m(&_mutex) ; - if(!_enabled) - return ; - } - - std::cerr << "Notifyqt::notifyDownloadCompleteCount " << count << std::endl; - - emit downloadCompleteCountChanged(count); -} -#endif - void NotifyQt::notifyDiskFull(uint32_t loc,uint32_t size_in_mb) { { @@ -424,23 +350,6 @@ void NotifyQt::notifyPeerStatusChangedSummary() emit peerStatusChangedSummary(); } -#ifdef TO_REMOVE -void NotifyQt::notifyGxsChange(const RsGxsChanges& changes) -{ - { - QMutexLocker m(&_mutex) ; - if(!_enabled) - return ; - } - -#ifdef NOTIFY_DEBUG - std::cerr << "Notifyqt:: notified that gxs has changes" << std::endl; -#endif - - emit gxsChange(changes); -} -#endif - void NotifyQt::notifyOwnStatusMessageChanged() { { @@ -584,35 +493,6 @@ void NotifyQt::notifyTurtleSearchResult(const RsPeerId& pid,uint32_t search_id,c } } -#ifdef TO_REMOVE -void NotifyQt::notifyHashingInfo(uint32_t type, const std::string& fileinfo) -{ - QString info; - - { - QMutexLocker m(&_mutex) ; - if(!_enabled) - return ; - } - - switch (type) { - case NOTIFY_HASHTYPE_EXAMINING_FILES: - info = tr("Examining shared files..."); - break; - case NOTIFY_HASHTYPE_FINISH: - break; - case NOTIFY_HASHTYPE_HASH_FILE: - info = tr("Hashing file") + " " + QString::fromUtf8(fileinfo.c_str()); - break; - case NOTIFY_HASHTYPE_SAVE_FILE_INDEX: - info = tr("Saving file index..."); - break; - } - - emit hashingInfoChanged(info); -} -#endif - void NotifyQt::notifyHistoryChanged(uint32_t msgId, int type) { { @@ -780,19 +660,6 @@ void NotifyQt::notifyListPreChange(int list, int /*type*/) return; } - /* New Timer Based Update scheme ... - * means correct thread seperation - * - * uses Flags, to detect changes - */ - -void NotifyQt::resetCachedPassphrases() -{ - std::cerr << "Clearing PGP passphrase." << std::endl; - - rsNotify->clearPgpPassphrase() ; -} - void NotifyQt::enable() { QMutexLocker m(&_mutex) ; diff --git a/retroshare-gui/src/gui/notifyqt.h b/retroshare-gui/src/gui/notifyqt.h index 19bd20bba..069ae8056 100644 --- a/retroshare-gui/src/gui/notifyqt.h +++ b/retroshare-gui/src/gui/notifyqt.h @@ -67,9 +67,6 @@ class NotifyQt: public QObject, public NotifyClient virtual void notifyChatStatus(const ChatId &chat_id,const std::string& status_string); virtual void notifyChatCleared(const ChatId &chat_id); virtual void notifyCustomState(const std::string& peer_id, const std::string& status_string); -#ifdef TO_REMOVE - virtual void notifyHashingInfo(uint32_t type, const std::string& fileinfo); -#endif virtual void notifyTurtleSearchResult(const RsPeerId &pid, uint32_t search_id, const std::list& found_files); virtual void notifyTurtleSearchResult(uint32_t search_id,const std::list& found_groups); virtual void notifyPeerHasNewAvatar(std::string peer_id) ; @@ -87,27 +84,9 @@ class NotifyQt: public QObject, public NotifyClient virtual void notifyHistoryChanged(uint32_t msgId, int type); virtual void notifyDiscInfoChanged() ; -#ifdef TO_REMOVE - virtual void notifyDownloadComplete(const std::string& fileHash); - virtual void notifyDownloadCompleteCount(uint32_t count); -#endif virtual bool askForPassword(const std::string& title, const std::string& key_details, bool prev_is_bad, std::string& password, bool &cancelled); virtual bool askForPluginConfirmation(const std::string& plugin_filename, const std::string& plugin_file_hash,bool first_time); - // Queues the signature event so that it canhappen in the main GUI thread (to ask for passwd). - // To use this function: call is multiple times as soon as it returns true. - // - // Dont' use a while, if you're in a mutexed part, otherwize it will lock. You need to call the function - // and periodically exit the locked code between calls to allow the signature to happen. - // - // Returns: - // false = the signature is registered, but the result is not there yet. Call again soon. - // true = signature done. Data is ready. signature_result takes the following values: - // 1: signature success - // 2: signature failed. Wrong passwd, user pressed cancel, etc. - // - virtual bool askForDeferredSelfSignature(const void *data, const uint32_t len, unsigned char *sign, unsigned int *signlen, int& signature_result, std::string reason = "") ; - /* Notify from GUI */ void notifyChatFontChanged(); void notifyChatStyleChanged(int /*ChatStyle::enumStyleType*/ styleType); @@ -132,10 +111,6 @@ class NotifyQt: public QObject, public NotifyClient void neighboursChanged() const ; void messagesChanged() const ; void messagesTagsChanged() const; -#ifdef REMOVE - void forumsChanged() const ; // use connect with Qt::QueuedConnection - void channelsChanged(int type) const ; // use connect with Qt::QueuedConnection -#endif void configChanged() const ; void logInfoChanged(const QString&) const ; void chatStatusChanged(const ChatId&,const QString&) const ; @@ -150,17 +125,9 @@ class NotifyQt: public QObject, public NotifyClient void peerStatusChanged(const QString& /* peer_id */, int /* status */); void peerStatusChangedSummary() const; void gxsChange(const RsGxsChanges& /* changes */); -#ifdef REMOVE - void publicChatChanged(int type) const ; - void privateChatChanged(int list, int type) const ; -#endif void chatMessageReceived(ChatMessage msg); void groupsChanged(int type) const ; void discInfoChanged() const ; -#ifdef REMOVE - void forumMsgReadSatusChanged(const QString& forumId, const QString& msgId, int status); - void channelMsgReadSatusChanged(const QString& channelId, const QString& msgId, int status); -#endif void historyChanged(uint msgId, int type); void chatLobbyInviteReceived() ; void deferredSignatureHandlingRequested() ; @@ -176,7 +143,6 @@ class NotifyQt: public QObject, public NotifyClient public slots: void UpdateGUI(); /* called by timer */ void SetDisableAll(bool bValue); - void resetCachedPassphrases() ; private slots: void runningTick(); diff --git a/retroshare-gui/src/gui/profile/ProfileManager.cpp b/retroshare-gui/src/gui/profile/ProfileManager.cpp index eca3c6c55..c0a63229e 100644 --- a/retroshare-gui/src/gui/profile/ProfileManager.cpp +++ b/retroshare-gui/src/gui/profile/ProfileManager.cpp @@ -19,6 +19,7 @@ *******************************************************************************/ +#include "gui/common/FilesDefs.h" #include #include #include @@ -37,7 +38,7 @@ #include -#define IMAGE_EXPORT ":/images/exportpeers_16x16.png" +#define IMAGE_EXPORT "" #define COLUMN_NAME 0 #define COLUMN_EMAIL 1 @@ -50,7 +51,7 @@ ProfileManager::ProfileManager(QWidget *parent) /* Invoke Qt Designer generated QObject setup routine */ ui.setupUi(this); - ui.headerFrame->setHeaderImage(QPixmap(":/images/contact_new128.png")); + ui.headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/profile.png")); ui.headerFrame->setHeaderText(tr("Profile Manager")); connect(ui.identityTreeWidget, SIGNAL( customContextMenuRequested(QPoint)), this, SLOT( identityTreeWidgetCostumPopupMenu(QPoint))); @@ -67,7 +68,7 @@ void ProfileManager::identityTreeWidgetCostumPopupMenu(QPoint) QMenu contextMnu(this); - QAction *action = contextMnu.addAction(QIcon(IMAGE_EXPORT), tr("Export Identity"), this, SLOT(exportIdentity())); + QAction *action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_EXPORT), tr("Export Identity"), this, SLOT(exportIdentity())); action->setEnabled(item != NULL); contextMnu.exec(QCursor::pos()); diff --git a/retroshare-gui/src/gui/profile/ProfileManager.ui b/retroshare-gui/src/gui/profile/ProfileManager.ui index b5a49cff2..9ec918e89 100644 --- a/retroshare-gui/src/gui/profile/ProfileManager.ui +++ b/retroshare-gui/src/gui/profile/ProfileManager.ui @@ -134,7 +134,7 @@ p, li { white-space: pre-wrap; } - + diff --git a/retroshare-gui/src/gui/qss/stylesheet/Standard.qss b/retroshare-gui/src/gui/qss/stylesheet/Standard.qss index 3e9a063cf..7ab47c667 100644 --- a/retroshare-gui/src/gui/qss/stylesheet/Standard.qss +++ b/retroshare-gui/src/gui/qss/stylesheet/Standard.qss @@ -225,9 +225,8 @@ ChatWidget QFrame#infoFrame { background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #FFFFD7, stop:1 #FFFFB2); } -ChatWidget QFrame#titleBarFrame { - background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #FEFEFE, stop:1 #E8E8E8); - border: 1px solid #CCCCCC; +ChatWidget QFrame#titleBarFrame, QFrame#toolBarFrameTop { + } PopupChatWindow QToolBar#chattoolBar{ @@ -238,13 +237,12 @@ PopupChatWindow QToolBar#chattoolBar{ /* Messages */ MessageComposer QToolBar#toolBar { - background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #9BDBF9, stop:1 #1592CD); - border: 0px; + } MessagesDialog QFrame#folderFrame, MessagesDialog QFrame#quickViewFrame { - background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #FEFEFE, stop:1 #E8E8E8); - border: 1px solid #CCCCCC; + background: transparent; + } /* Profile */ @@ -270,39 +268,6 @@ PluginItem > QFrame#frame { } */ -/* SearchDialog */ - -SearchDialog QFrame#searchLineFrame[valid=false] { - border: 2px solid #079E00; - background-color: #DBDBDB; -} - -SearchDialog QFrame#searchLineFrame[valid=true] { - border: 2px solid #079E00; - background-color: white; -} - -SearchDialog QPushButton#pushButtonSearch { - border-image: url(:/images/btn1.png) 4; - border-width: 4; - padding: 0px 6px; - font-size: 12px; - color: black; -} - -SearchDialog QPushButton#pushButtonSearch:hover { - border-image: url(:/images/btn2.png) 4; -} - -SearchDialog QPushButton#pushButtonSearch:pressed { - border-image: url(:/images/btn3.png) 4; -} - -SearchDialog QFrame#searchLineFrame > QLineEdit { - background: transparent; - border: none; -} - /* GetStartedDialog */ GetStartedDialog QTextEdit { @@ -606,54 +571,63 @@ IdEditDialog QLabel#info_label background: #FFFFD7; background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #FFFFD7, stop:1 #FFFFB2); } + GenCertDialog QComboBox#genPGPuser { border: 2px solid #0099cc; border-radius: 6px; background: white; font: bold; } + GenCertDialog QSpinBox#hiddenport_spinBox { border: 2px solid #0099cc; border-radius: 6px; background: white; font: bold; } + GenCertDialog QLineEdit#hiddenaddr_input { border: 2px solid #0099cc; border-radius: 6px; background: white; font: bold; } + GenCertDialog QLineEdit#password2_input { border: 2px solid #0099cc; border-radius: 6px; background: white; font: bold; } + GenCertDialog QLineEdit#password_input { border: 2px solid #0099cc; border-radius: 6px; background: white; font: bold; } + GenCertDialog QLineEdit#nickname_input { border: 2px solid #0099cc; border-radius: 6px; background: white; font: bold; } + GenCertDialog QLineEdit#node_input { border: 2px solid #0099cc; border-radius: 6px; background: white; font: bold; } + GenCertDialog QLineEdit#name_input { border: 2px solid #0099cc; border-radius: 6px; background: white; font: bold; } + GenCertDialog QPushButton#genButton { border-image: url(:/images/btn_blue.png) 4; border-width: 4; @@ -767,12 +741,11 @@ GxsForumThreadWidget QToolButton#subscribeToolButton:hover { border-radius: 4px; } -GxsChannelPostsWidget QFrame#infoFrame -{ - +GxsForumMsgItem QFrame#frame{ + background-color: white; } -GxsChannelPostsWidget QToolButton#subscribeToolButton { +GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton { font: bold; font-size: 14px; color: white; @@ -781,18 +754,18 @@ GxsChannelPostsWidget QToolButton#subscribeToolButton { max-height: 27px; } -GxsChannelPostsWidget QToolButton#subscribeToolButton:hover { +GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton:hover { background: #03b1f3; border-radius: 4px; } -GxsChannelPostsWidget QToolButton#subscribeToolButton:pressed { +GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton:pressed { background: #03b1f3; border-radius: 4px; border: 1px solid gray; } -GxsChannelPostsWidget QToolButton#subscribeToolButton:disabled { +GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton:disabled { background: gray; border-radius: 4px; border: 1px solid gray; @@ -800,23 +773,48 @@ GxsChannelPostsWidget QToolButton#subscribeToolButton:disabled { } /* only for MenuButtonPopup */ -GxsChannelPostsWidget QToolButton#subscribeToolButton[popupMode="1"] { +GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton[popupMode="1"] { padding-right: 0px; } -GxsChannelPostsWidget QToolButton#subscribeToolButton::menu-arrow { +GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton::menu-arrow { image: none; } -GxsChannelPostsWidget QToolButton#subscribeToolButton::menu-button { +GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton::menu-button { image: none; } +GxsChannelFilesStatusWidget QToolButton#openFolderToolButton::menu-indicator { + image: none; +} + +GxsChannelFilesStatusWidget QToolButton#openFolderToolButton[popupMode="0"] { + padding-right: 0px; +} + +GxsChannelFilesStatusWidget QToolButton#openFolderToolButton::menu-indicator { + image: none; +} + +GxsChannelFilesStatusWidget QToolButton#openFolderToolButton[popupMode="0"] { + padding-right: 0px; +} + +GxsGroupDialog QLabel#groupLogo{ + border: 2px solid #CCCCCC; + border-radius: 3px; +} + PostedItem QFrame#frame_notes { background: white; } +PostedItem QFrame#mainFrame { + background-color: white; +} + PostedItem QLabel#notes { } @@ -862,6 +860,11 @@ PostedCardView QFrame#voteFrame { background: #f8f9fa; } +PostedCardView QFrame#mainFrame { + background: white; +} + + GxsCommentDialog QComboBox#sortBox { font: bold; color: #0099cc; @@ -880,12 +883,54 @@ PostedCreatePostDialog QPushButton#submitButton { border-radius: 4px; max-height: 27px; min-width: 4em; - padding: 2px; + padding: 2px; } PostedCreatePostDialog QPushButton#submitButton:hover { background: #03b1f3; border-radius: 4px; min-width: 4em; - padding: 2px; -} \ No newline at end of file + padding: 2px; +} + +MessageWidget QTextBrowser#toText , QTextBrowser#ccText , QTextBrowser#bccText { + background: transparent; + border: none; +} + +WireGroupItem QWidget:hover +{ + background-color: #7ecbfb; +} + +WireDialog QFrame#frame{ + background-color: white; +} + +WireDialog QWidget#scrollAreaWidgetContents_groups{ + background-color: white; +} + +MessagesDialog QWidget#messageTreeWidget::item { + padding: 2px; +} + +MessagesDialog QWidget#messageTreeWidget::item:selected { + background-color: #cde8ff; + color: black; +} + +MessagesDialog QWidget#messageTreeWidget::item:hover { + background-color: #e5f3ff; + color: black; +} + +GxsForumThreadWidget QWidget#threadTreeWidget::item { + padding: 2px; +} + +PulseTopLevel QFrame#frame, PulseViewGroup QFrame#frame, PulseReply QFrame#frame { + border: 2px solid #7ecbfb; + border-radius: 6px; + background: white; +} diff --git a/retroshare-gui/src/gui/qss/stylesheet/qss.default b/retroshare-gui/src/gui/qss/stylesheet/qss.default index 66422a25f..d55011bee 100644 --- a/retroshare-gui/src/gui/qss/stylesheet/qss.default +++ b/retroshare-gui/src/gui/qss/stylesheet/qss.default @@ -84,7 +84,7 @@ ConfCertDialog QLabel#servicePermissionsLabel qproperty-fontSizeFactor: 125; } -GxsChannelPostsWidget QLabel#nameLabel +GxsChannelPostsWidgetWithModel QLabel#channelName_LB { qproperty-fontSizeFactor: 250; } @@ -138,6 +138,10 @@ ForumsDialog, GxsForumThreadWidget qproperty-textColorUnreadChildren: darkgray; qproperty-textColorNotSubscribed: black; qproperty-textColorMissing: darkRed; + qproperty-textColorPinned: darkOrange; + + qproperty-backgroundColorPinned: rgb(255, 200, 180); + qproperty-backgroundColorFiltered: rgb(255, 240, 210); } GroupTreeWidget @@ -186,7 +190,8 @@ NetworkDialog RSTextBrowser, MimeTextEdit { - qproperty-textColorQuote: rgb(120, 153, 34); + /*qproperty-textColorQuote: rgb(120, 153, 34);*/ + qproperty-textColorQuotes: ColorList(#789922 #039bd5 #800000 #800080 #008080 #b10dc9 #85144b #3d9970); } QLabel#headerTextLabel_Person diff --git a/retroshare-gui/src/gui/settings/AboutPage.h b/retroshare-gui/src/gui/settings/AboutPage.h index 5c9be1be9..4881ca730 100644 --- a/retroshare-gui/src/gui/settings/AboutPage.h +++ b/retroshare-gui/src/gui/settings/AboutPage.h @@ -22,8 +22,9 @@ #include -#include +#include "retroshare-gui/configpage.h" #include "ui_AboutPage.h" +#include "gui/common/FilesDefs.h" class AboutPage : public ConfigPage { @@ -38,7 +39,7 @@ public: /** Loads the settings for this page */ virtual void load(); - virtual QPixmap iconPixmap() const { return QPixmap(":/icons/svg/info.svg") ; } + virtual QPixmap iconPixmap() const { return FilesDefs::getPixmapFromQtResourcePath(":/icons/svg/info.svg") ; } virtual QString pageName() const { return tr("About") ; } virtual QString helpText() const { return ""; } diff --git a/retroshare-gui/src/gui/settings/AppearancePage.cpp b/retroshare-gui/src/gui/settings/AppearancePage.cpp index 0c3c2c924..02ad43143 100755 --- a/retroshare-gui/src/gui/settings/AppearancePage.cpp +++ b/retroshare-gui/src/gui/settings/AppearancePage.cpp @@ -42,6 +42,7 @@ #include "gui/statusbar/SoundStatus.h" #include "gui/statusbar/ToasterDisable.h" #include "gui/statusbar/SysTrayStatus.h" +#include "gui/common/FilesDefs.h" /** Constructor */ AppearancePage::AppearancePage(QWidget * parent, Qt::WindowFlags flags) @@ -69,7 +70,7 @@ AppearancePage::AppearancePage(QWidget * parent, Qt::WindowFlags flags) /* Populate combo boxes */ foreach (QString code, LanguageSupport::languageCodes()) { - ui.cmboLanguage->addItem(QIcon(":/images/flags/" + code + ".png"), LanguageSupport::languageName(code), code); + ui.cmboLanguage->addItem(FilesDefs::getIconFromQtResourcePath(":/images/flags/" + code + ".png"), LanguageSupport::languageName(code), code); } foreach (QString style, QStyleFactory::keys()) { ui.cmboStyle->addItem(style, style.toLower()); diff --git a/retroshare-gui/src/gui/settings/AppearancePage.h b/retroshare-gui/src/gui/settings/AppearancePage.h index 9700d6ca3..897563753 100755 --- a/retroshare-gui/src/gui/settings/AppearancePage.h +++ b/retroshare-gui/src/gui/settings/AppearancePage.h @@ -21,9 +21,10 @@ #ifndef _APPERARANCEPAGE_H #define _APPERARANCEPAGE_H -#include +#include "retroshare-gui/configpage.h" #include "gui/MainWindow.h" #include "ui_AppearancePage.h" +#include "gui/common/FilesDefs.h" class AppearancePage : public ConfigPage { @@ -36,7 +37,7 @@ public: /** Loads the settings for this page */ virtual void load(); - virtual QPixmap iconPixmap() const { return QPixmap(":/icons/settings/appearance.svg") ; } + virtual QPixmap iconPixmap() const { return FilesDefs::getPixmapFromQtResourcePath(":/icons/settings/appearance.svg") ; } virtual QString pageName() const { return tr("Appearance") ; } virtual QString helpText() const { return ""; } diff --git a/retroshare-gui/src/gui/settings/ChannelPage.cpp b/retroshare-gui/src/gui/settings/ChannelPage.cpp index 2cebb8848..ef2fa6307 100644 --- a/retroshare-gui/src/gui/settings/ChannelPage.cpp +++ b/retroshare-gui/src/gui/settings/ChannelPage.cpp @@ -33,17 +33,10 @@ ChannelPage::ChannelPage(QWidget * parent, Qt::WindowFlags flags) ui.groupFrameSettingsWidget->setOpenAllInNewTabText(tr("Open each channel in a new tab")); ui.groupFrameSettingsWidget->setType(GroupFrameSettings::Channel) ; - connect(ui.loadThreadCheckBox,SIGNAL(toggled(bool)),this,SLOT(updateLoadThread())) ; connect(ui.emoteicon_checkBox,SIGNAL(toggled(bool)),this,SLOT(updateEmotes())) ; } -void ChannelPage::updateLoadThread() -{ - Settings->setChannelLoadThread(ui.loadThreadCheckBox->isChecked()); - NotifyQt::getInstance()->notifySettingsChanged(); -} - ChannelPage::~ChannelPage() { } @@ -51,7 +44,6 @@ ChannelPage::~ChannelPage() /** Loads the settings for this page */ void ChannelPage::load() { - whileBlocking(ui.loadThreadCheckBox)->setChecked(Settings->getChannelLoadThread()); ui.groupFrameSettingsWidget->loadSettings(GroupFrameSettings::Channel); Settings->beginGroup(QString("ChannelPostsWidget")); diff --git a/retroshare-gui/src/gui/settings/ChannelPage.h b/retroshare-gui/src/gui/settings/ChannelPage.h index 6e684ead0..93f32cabc 100644 --- a/retroshare-gui/src/gui/settings/ChannelPage.h +++ b/retroshare-gui/src/gui/settings/ChannelPage.h @@ -21,8 +21,9 @@ #ifndef CHANNELPAGE_H #define CHANNELPAGE_H -#include +#include "retroshare-gui/configpage.h" #include "ui_ChannelPage.h" +#include "gui/common/FilesDefs.h" class ChannelPage : public ConfigPage { @@ -35,16 +36,13 @@ public: /** Loads the settings for this page */ virtual void load(); - virtual QPixmap iconPixmap() const { return QPixmap(":/icons/settings/channels.svg") ; } + virtual QPixmap iconPixmap() const { return FilesDefs::getPixmapFromQtResourcePath(":/icons/settings/channels.svg") ; } virtual QString pageName() const { return tr("Channels") ; } virtual QString helpText() const { return ""; } private slots: void updateEmotes(); -protected slots: - void updateLoadThread() ; - private: Ui::ChannelPage ui; }; diff --git a/retroshare-gui/src/gui/settings/ChannelPage.ui b/retroshare-gui/src/gui/settings/ChannelPage.ui index cd0533552..5e75e442e 100644 --- a/retroshare-gui/src/gui/settings/ChannelPage.ui +++ b/retroshare-gui/src/gui/settings/ChannelPage.ui @@ -29,13 +29,6 @@ General - - - - Load posts in background (Thread) - - - diff --git a/retroshare-gui/src/gui/settings/ChatPage.cpp b/retroshare-gui/src/gui/settings/ChatPage.cpp index a17aa57d4..d28909220 100644 --- a/retroshare-gui/src/gui/settings/ChatPage.cpp +++ b/retroshare-gui/src/gui/settings/ChatPage.cpp @@ -38,8 +38,8 @@ #include #define VARIANT_STANDARD "Standard" -#define IMAGE_CHAT_CREATE ":/images/add_24x24.png" -#define IMAGE_CHAT_OPEN ":/images/typing.png" +#define IMAGE_CHAT_CREATE ":/icons/png/add.png" +#define IMAGE_CHAT_OPEN ":/icons/png/typing.png" #define IMAGE_CHAT_DELETE ":/images/deletemail24.png" #define IMAGE_CHAT_COPY ":/images/copyrslink.png" diff --git a/retroshare-gui/src/gui/settings/ChatPage.h b/retroshare-gui/src/gui/settings/ChatPage.h index 24ddc570d..6c79f7ed7 100644 --- a/retroshare-gui/src/gui/settings/ChatPage.h +++ b/retroshare-gui/src/gui/settings/ChatPage.h @@ -21,9 +21,10 @@ #ifndef _CHATPAGE_H #define _CHATPAGE_H -#include +#include "retroshare-gui/configpage.h" #include "gui/chat/ChatStyle.h" #include "ui_ChatPage.h" +#include "gui/common/FilesDefs.h" class ChatPage : public ConfigPage { @@ -38,7 +39,7 @@ class ChatPage : public ConfigPage /** Loads the settings for this page */ virtual void load(); - virtual QPixmap iconPixmap() const { return QPixmap(":/icons/settings/chat.svg") ; } + virtual QPixmap iconPixmap() const { return FilesDefs::getPixmapFromQtResourcePath(":/icons/settings/chat.svg") ; } virtual QString pageName() const { return tr("Chats") ; } virtual QString helpText() const { return ""; } diff --git a/retroshare-gui/src/gui/settings/CryptoPage.h b/retroshare-gui/src/gui/settings/CryptoPage.h index 6db7cde4f..abd8bcb6c 100755 --- a/retroshare-gui/src/gui/settings/CryptoPage.h +++ b/retroshare-gui/src/gui/settings/CryptoPage.h @@ -21,8 +21,9 @@ #ifndef _CRYPTOPAGE_H #define _CRYPTOPAGE_H -#include +#include "retroshare-gui/configpage.h" #include "ui_CryptoPage.h" +#include "gui/common/FilesDefs.h" class CryptoPage : public ConfigPage { @@ -36,7 +37,7 @@ class CryptoPage : public ConfigPage /** Loads the settings for this page */ - virtual QPixmap iconPixmap() const { return QPixmap(":/icons/settings/profile.svg") ; } + virtual QPixmap iconPixmap() const { return FilesDefs::getPixmapFromQtResourcePath(":/icons/settings/profile.svg") ; } virtual QString pageName() const { return tr("Node") ; } virtual QString helpText() const { return ""; } diff --git a/retroshare-gui/src/gui/settings/CryptoPage.ui b/retroshare-gui/src/gui/settings/CryptoPage.ui index bbda9302c..bf986996b 100755 --- a/retroshare-gui/src/gui/settings/CryptoPage.ui +++ b/retroshare-gui/src/gui/settings/CryptoPage.ui @@ -14,7 +14,7 @@ - 1 + 0 diff --git a/retroshare-gui/src/gui/settings/FileAssociationsPage.cpp b/retroshare-gui/src/gui/settings/FileAssociationsPage.cpp index 72f4b0eaf..fe7e36a7b 100755 --- a/retroshare-gui/src/gui/settings/FileAssociationsPage.cpp +++ b/retroshare-gui/src/gui/settings/FileAssociationsPage.cpp @@ -19,6 +19,7 @@ * * *******************************************************************************/ +#include "gui/common/FilesDefs.h" #include "FileAssociationsPage.h" #include "AddFileAssociationDialog.h" //#include "rshare.h" // for Rshare::dataDirectory() method @@ -60,19 +61,19 @@ FileAssociationsPage::FileAssociationsPage(QWidget * parent, Qt::WindowFlags fla QVBoxLayout* pageLay = new QVBoxLayout(this); toolBar = new QToolBar("actions", this); - newAction = new QAction(QIcon(":/images/add_24x24.png"), tr("&New"), this); + newAction = new QAction(FilesDefs::getIconFromQtResourcePath(":/icons/png/add.png"), tr("&New"), this); //newAction->setShortcut(tr("Ctrl+N")); newAction->setStatusTip(tr("Add new Association")); connect(newAction, SIGNAL(triggered()), this, SLOT(addnew())); toolBar->addAction(newAction); - editAction = new QAction(QIcon(":/images/kcmsystem24.png"), + editAction = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/kcmsystem24.png"), tr("&Edit"), this); editAction->setStatusTip(tr("Edit this Association")); connect(editAction, SIGNAL(triggered()), this, SLOT(edit())); toolBar->addAction(editAction); - removeAction = new QAction(QIcon(":/images/edit_remove24.png"), + removeAction = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/edit_remove24.png"), tr("&Remove"), this); removeAction->setStatusTip(tr("Remove this Association")); connect(removeAction, SIGNAL(triggered()), this, SLOT(remove())); diff --git a/retroshare-gui/src/gui/settings/FileAssociationsPage.h b/retroshare-gui/src/gui/settings/FileAssociationsPage.h index 91c9ca573..721c29969 100755 --- a/retroshare-gui/src/gui/settings/FileAssociationsPage.h +++ b/retroshare-gui/src/gui/settings/FileAssociationsPage.h @@ -22,7 +22,8 @@ #ifndef __FileAssociationsPage__ #define __FileAssociationsPage__ -#include +#include "retroshare-gui/configpage.h" +#include "gui/common/FilesDefs.h" class QToolBar; class QAction; @@ -52,7 +53,7 @@ public: virtual ~FileAssociationsPage(); virtual void load(); - virtual QPixmap iconPixmap() const { return QPixmap(":/images/filetype-association.png") ; } + virtual QPixmap iconPixmap() const { return FilesDefs::getPixmapFromQtResourcePath(":/images/filetype-association.png") ; } virtual QString pageName() const { return tr("Associations") ; } protected: diff --git a/retroshare-gui/src/gui/settings/ForumPage.cpp b/retroshare-gui/src/gui/settings/ForumPage.cpp index 8039a087c..ee0d32566 100644 --- a/retroshare-gui/src/gui/settings/ForumPage.cpp +++ b/retroshare-gui/src/gui/settings/ForumPage.cpp @@ -35,6 +35,8 @@ ForumPage::ForumPage(QWidget * parent, Qt::WindowFlags flags) connect(ui.expandNewMessages , SIGNAL(toggled(bool)), this, SLOT( updateExpandNewMessages())); connect(ui.loadEmbeddedImages , SIGNAL(toggled(bool)), this, SLOT(updateLoadEmbeddedImages() )); connect(ui.loadEmoticons , SIGNAL(toggled(bool)), this, SLOT( updateLoadEmoticons() )); + connect(ui.minimumFontSize , SIGNAL(valueChanged(int)), this, SLOT(updateFonts())); + connect(ui.minimumContrast , SIGNAL(valueChanged(int)), this, SLOT(updateFonts())); ui.groupFrameSettingsWidget->setType(GroupFrameSettings::Forum) ; } @@ -51,10 +53,22 @@ void ForumPage::updateLoadEmoticons() { Settings->setForumLoadEmoticons( /** Loads the settings for this page */ void ForumPage::load() { + Settings->beginGroup(QString("Forum")); whileBlocking(ui.setMsgToReadOnActivate)->setChecked(Settings->getForumMsgSetToReadOnActivate()); whileBlocking(ui.expandNewMessages)->setChecked(Settings->getForumExpandNewMessages()); whileBlocking(ui.loadEmbeddedImages)->setChecked(Settings->getForumLoadEmbeddedImages()); whileBlocking(ui.loadEmoticons)->setChecked(Settings->getForumLoadEmoticons()); + whileBlocking(ui.minimumFontSize)->setValue(Settings->value("MinimumFontSize", 10).toInt()); + whileBlocking(ui.minimumContrast)->setValue(Settings->value("MinimumContrast", 4.5).toDouble()); + Settings->endGroup(); ui.groupFrameSettingsWidget->loadSettings(GroupFrameSettings::Forum); } + +void ForumPage::updateFonts() +{ + Settings->beginGroup(QString("Forum")); + Settings->setValue("MinimumFontSize", ui.minimumFontSize->value()); + Settings->setValue("MinimumContrast", ui.minimumContrast->value()); + Settings->endGroup(); +} diff --git a/retroshare-gui/src/gui/settings/ForumPage.h b/retroshare-gui/src/gui/settings/ForumPage.h index 19027c303..3827df0e4 100644 --- a/retroshare-gui/src/gui/settings/ForumPage.h +++ b/retroshare-gui/src/gui/settings/ForumPage.h @@ -21,8 +21,9 @@ #ifndef FORUMPAGE_H #define FORUMPAGE_H -#include +#include "retroshare-gui/configpage.h" #include "ui_ForumPage.h" +#include "gui/common/FilesDefs.h" class ForumPage : public ConfigPage { @@ -35,7 +36,7 @@ public: /** Loads the settings for this page */ virtual void load(); - virtual QPixmap iconPixmap() const { return QPixmap(":/icons/settings/forums.svg") ; } + virtual QPixmap iconPixmap() const { return FilesDefs::getPixmapFromQtResourcePath(":/icons/settings/forums.svg") ; } virtual QString pageName() const { return tr("Forums") ; } virtual QString helpText() const { return ""; } @@ -45,6 +46,9 @@ protected slots: void updateLoadEmbeddedImages(); void updateLoadEmoticons(); +private slots: + void updateFonts(); + private: Ui::ForumPage ui; }; diff --git a/retroshare-gui/src/gui/settings/ForumPage.ui b/retroshare-gui/src/gui/settings/ForumPage.ui index 4fb74d888..a52638987 100644 --- a/retroshare-gui/src/gui/settings/ForumPage.ui +++ b/retroshare-gui/src/gui/settings/ForumPage.ui @@ -17,6 +17,16 @@ Misc + + + + <html><head/><body><p>This option is costly and it's in the dev's plans to improve it. In the mean time it's disabled by default. If you enable it and long forum posts take a while to display, then disable it again. </p></body></html> + + + Load emoticons (costly) + + + @@ -38,15 +48,71 @@ - - - - <html><head/><body><p>This option is costly and it's in the dev's plans to improve it. In the mean time it's disabled by default. If you enable it and long forum posts take a while to display, then disable it again. </p></body></html> - - - Load emoticons (costly) - - + + + + + + Minimum font size + + + + + + + 1 + + + 64 + + + + + + + Qt::Horizontal + + + + 40 + 38 + + + + + + + + Minimum text contrast + + + + + + + 1.000000000000000 + + + 21.000000000000000 + + + 0.500000000000000 + + + + + + + + + + + + Tabs + + + + @@ -64,18 +130,6 @@ - - - - Tabs - - - - - - - - diff --git a/retroshare-gui/src/gui/settings/GeneralPage.h b/retroshare-gui/src/gui/settings/GeneralPage.h index b03871bf4..b36471cbe 100755 --- a/retroshare-gui/src/gui/settings/GeneralPage.h +++ b/retroshare-gui/src/gui/settings/GeneralPage.h @@ -22,8 +22,9 @@ #ifndef _GENERALPAGE_H #define _GENERALPAGE_H -#include +#include "retroshare-gui/configpage.h" #include "ui_GeneralPage.h" +#include "gui/common/FilesDefs.h" class GeneralPage : public ConfigPage { @@ -39,7 +40,7 @@ public: /** Loads the settings for this page */ virtual void load(); - virtual QPixmap iconPixmap() const { return QPixmap(":/icons/settings/general.svg") ; } + virtual QPixmap iconPixmap() const { return FilesDefs::getPixmapFromQtResourcePath(":/icons/settings/general.svg") ; } virtual QString pageName() const { return tr("General") ; } virtual QString helpText() const { return ""; } diff --git a/retroshare-gui/src/gui/settings/JsonApiPage.h b/retroshare-gui/src/gui/settings/JsonApiPage.h index 646016662..f516a541d 100644 --- a/retroshare-gui/src/gui/settings/JsonApiPage.h +++ b/retroshare-gui/src/gui/settings/JsonApiPage.h @@ -35,7 +35,7 @@ public: /** Loads the settings for this page */ virtual QPixmap iconPixmap() const - { return QPixmap(":/icons/svg/empty-circle.svg"); } + { return FilesDefs::getPixmapFromQtResourcePath(":/icons/svg/empty-circle.svg"); } virtual QString pageName() const { return tr("JSON API"); } virtual QString helpText() const; diff --git a/retroshare-gui/src/gui/settings/MessagePage.cpp b/retroshare-gui/src/gui/settings/MessagePage.cpp index 908a765c3..91a24ab02 100644 --- a/retroshare-gui/src/gui/settings/MessagePage.cpp +++ b/retroshare-gui/src/gui/settings/MessagePage.cpp @@ -39,7 +39,6 @@ MessagePage::MessagePage(QWidget * parent, Qt::WindowFlags flags) connect (ui.editpushButton, SIGNAL(clicked(bool)), this, SLOT (editTag())); connect (ui.deletepushButton, SIGNAL(clicked(bool)), this, SLOT (deleteTag())); connect (ui.defaultTagButton, SIGNAL(clicked(bool)), this, SLOT (defaultTag())); - //connect (ui.encryptedMsgs_CB, SIGNAL(toggled(bool)), this, SLOT (toggleEnableEncryptedDistantMsgs(bool))); connect (ui.tags_listWidget, SIGNAL(currentRowChanged(int)), this, SLOT(currentRowChangedTag(int))); @@ -54,6 +53,7 @@ MessagePage::MessagePage(QWidget * parent, Qt::WindowFlags flags) connect(ui.setMsgToReadOnActivate,SIGNAL(toggled(bool)), this,SLOT(updateMsgToReadOnActivate())); connect(ui.loadEmbeddedImages, SIGNAL(toggled(bool)), this,SLOT(updateLoadEmbededImages() )); connect(ui.openComboBox, SIGNAL(currentIndexChanged(int)),this,SLOT(updateMsgOpen() )); + connect(ui.emoticonscheckBox, SIGNAL(toggled(bool)), this,SLOT(updateLoadEmoticons() )); } MessagePage::~MessagePage() @@ -84,6 +84,7 @@ void MessagePage::updateMsgToReadOnActivate() { Settings->setMsgSetToReadOnActiv void MessagePage::updateLoadEmbededImages() { Settings->setMsgLoadEmbeddedImages(ui.loadEmbeddedImages->isChecked()); } void MessagePage::updateMsgOpen() { Settings->setMsgOpen( static_cast(ui.openComboBox->itemData(ui.openComboBox->currentIndex()).toInt()) ); } void MessagePage::updateDistantMsgs() { Settings->setValue("DistantMessages", ui.comboBox->currentIndex()); } +void MessagePage::updateLoadEmoticons() { Settings->setValueToGroup("Messages", "Emoticons", ui.emoticonscheckBox->isChecked()); } void MessagePage::updateMsgTags() { @@ -110,9 +111,12 @@ void MessagePage::updateMsgTags() void MessagePage::load() { + Settings->beginGroup(QString("Messages")); whileBlocking(ui.setMsgToReadOnActivate)->setChecked(Settings->getMsgSetToReadOnActivate()); whileBlocking(ui.loadEmbeddedImages)->setChecked(Settings->getMsgLoadEmbeddedImages()); whileBlocking(ui.openComboBox)->setCurrentIndex(ui.openComboBox->findData(Settings->getMsgOpen())); + whileBlocking(ui.emoticonscheckBox)->setChecked(Settings->value("Emoticons", true).toBool()); + Settings->endGroup(); // state of filter combobox @@ -140,7 +144,7 @@ void MessagePage::fillTags() QString text = TagDefs::name(Tag->first, Tag->second.first); QListWidgetItem *pItemWidget = new QListWidgetItem(text, ui.tags_listWidget); - pItemWidget->setTextColor(QColor(Tag->second.second)); + pItemWidget->setData(Qt::ForegroundRole, QColor(Tag->second.second)); pItemWidget->setData(Qt::UserRole, Tag->first); } } @@ -155,7 +159,7 @@ void MessagePage::addTag() QString text = TagDefs::name(Tag->first, Tag->second.first); QListWidgetItem *pItemWidget = new QListWidgetItem(text, ui.tags_listWidget); - pItemWidget->setTextColor(QColor(Tag->second.second)); + pItemWidget->setData(Qt::ForegroundRole, QColor(Tag->second.second)); pItemWidget->setData(Qt::UserRole, TagDlg.m_nId); m_changedTagIds.push_back(TagDlg.m_nId); @@ -186,7 +190,7 @@ void MessagePage::editTag() if (Tag->first >= RS_MSGTAGTYPE_USER) { pItemWidget->setText(QString::fromStdString(Tag->second.first)); } - pItemWidget->setTextColor(QColor(Tag->second.second)); + pItemWidget->setData(Qt::ForegroundRole, QColor(Tag->second.second)); if (std::find(m_changedTagIds.begin(), m_changedTagIds.end(), TagDlg.m_nId) == m_changedTagIds.end()) { m_changedTagIds.push_back(TagDlg.m_nId); @@ -267,3 +271,4 @@ void MessagePage::currentRowChangedTag(int row) ui.editpushButton->setEnabled(bEditEnable); ui.deletepushButton->setEnabled(bDeleteEnable); } + diff --git a/retroshare-gui/src/gui/settings/MessagePage.h b/retroshare-gui/src/gui/settings/MessagePage.h index b4c2e9c67..f3a4c12a7 100644 --- a/retroshare-gui/src/gui/settings/MessagePage.h +++ b/retroshare-gui/src/gui/settings/MessagePage.h @@ -23,10 +23,11 @@ #include -#include +#include "retroshare-gui/configpage.h" #include "ui_MessagePage.h" #include "gui/msgs/MessageInterface.h" +#include "gui/common/FilesDefs.h" class MessagePage : public ConfigPage { @@ -39,7 +40,7 @@ public: /** Loads the settings for this page */ virtual void load(); - virtual QPixmap iconPixmap() const { return QPixmap(":/icons/settings/messages.svg") ; } + virtual QPixmap iconPixmap() const { return FilesDefs::getPixmapFromQtResourcePath(":/icons/settings/messages.svg") ; } virtual QString pageName() const { return tr("Mail") ; } virtual QString helpText() const { return ""; } @@ -58,6 +59,7 @@ private slots: void updateMsgOpen() ; void updateDistantMsgs() ; void updateMsgTags() ; + void updateLoadEmoticons(); private: void fillTags(); diff --git a/retroshare-gui/src/gui/settings/MessagePage.ui b/retroshare-gui/src/gui/settings/MessagePage.ui index 57d801bed..58da48b9f 100644 --- a/retroshare-gui/src/gui/settings/MessagePage.ui +++ b/retroshare-gui/src/gui/settings/MessagePage.ui @@ -65,14 +65,7 @@ - - - - Load embedded images - - - - + @@ -86,6 +79,20 @@ + + + + Load embedded images + + + + + + + Load Emoticons + + + diff --git a/retroshare-gui/src/gui/settings/NotifyPage.h b/retroshare-gui/src/gui/settings/NotifyPage.h index ca6aa83be..f78c188a0 100755 --- a/retroshare-gui/src/gui/settings/NotifyPage.h +++ b/retroshare-gui/src/gui/settings/NotifyPage.h @@ -25,6 +25,7 @@ #include "ui_NotifyPage.h" #include "gui/chat/ChatLobbyUserNotify.h" +#include "gui/common/FilesDefs.h" class UserNotify; class FeedNotify; @@ -78,7 +79,7 @@ public: /** Loads the settings for this page */ virtual void load(); - virtual QPixmap iconPixmap() const { return QPixmap(":/icons/settings/notify.svg") ; } + virtual QPixmap iconPixmap() const { return FilesDefs::getPixmapFromQtResourcePath(":/icons/settings/notify.svg") ; } virtual QString pageName() const { return tr("Notify") ; } virtual QString helpText() const ; diff --git a/retroshare-gui/src/gui/settings/NotifyPage.ui b/retroshare-gui/src/gui/settings/NotifyPage.ui index 0f25f470e..f468a62ec 100755 --- a/retroshare-gui/src/gui/settings/NotifyPage.ui +++ b/retroshare-gui/src/gui/settings/NotifyPage.ui @@ -75,7 +75,7 @@ - Links + Boards @@ -103,7 +103,7 @@ - Security + Friend Requests @@ -155,7 +155,7 @@ - Connect attempt + Friend Requests @@ -256,7 +256,7 @@ - Connect attempt + Friend Requests diff --git a/retroshare-gui/src/gui/settings/PeoplePage.h b/retroshare-gui/src/gui/settings/PeoplePage.h index 68a7c0415..ec0af7927 100644 --- a/retroshare-gui/src/gui/settings/PeoplePage.h +++ b/retroshare-gui/src/gui/settings/PeoplePage.h @@ -21,8 +21,9 @@ #ifndef PEOPLEPAGE_H #define PEOPLEPAGE_H -#include +#include "retroshare-gui/configpage.h" #include "ui_PeoplePage.h" +#include "gui/common/FilesDefs.h" class PeoplePage : public ConfigPage { @@ -35,7 +36,7 @@ public: /** Loads the settings for this page */ virtual void load(); - virtual QPixmap iconPixmap() const { return QPixmap(":/icons/settings/people.svg") ; } + virtual QPixmap iconPixmap() const { return FilesDefs::getPixmapFromQtResourcePath(":/icons/settings/people.svg") ; } virtual QString pageName() const { return tr("People") ; } virtual QString helpText() const { return ""; } diff --git a/retroshare-gui/src/gui/settings/PluginsPage.h b/retroshare-gui/src/gui/settings/PluginsPage.h index 02c4290a0..00a63070a 100644 --- a/retroshare-gui/src/gui/settings/PluginsPage.h +++ b/retroshare-gui/src/gui/settings/PluginsPage.h @@ -20,8 +20,9 @@ #pragma once -#include +#include "retroshare-gui/configpage.h" #include "ui_PluginsPage.h" +#include "gui/common/FilesDefs.h" namespace settings { @@ -36,7 +37,7 @@ class PluginsPage : public ConfigPage /** Loads the settings for this page */ virtual void load(); - virtual QPixmap iconPixmap() const { return QPixmap(":/icons/settings/plugins.svg") ; } + virtual QPixmap iconPixmap() const { return FilesDefs::getPixmapFromQtResourcePath(":/icons/settings/plugins.svg") ; } virtual QString pageName() const { return tr("Plugins") ; } virtual QString helpText() const ; diff --git a/retroshare-gui/src/gui/settings/PostedPage.cpp b/retroshare-gui/src/gui/settings/PostedPage.cpp index 107c49074..f4c178705 100644 --- a/retroshare-gui/src/gui/settings/PostedPage.cpp +++ b/retroshare-gui/src/gui/settings/PostedPage.cpp @@ -30,7 +30,7 @@ PostedPage::PostedPage(QWidget * parent, Qt::WindowFlags flags) setAttribute(Qt::WA_QuitOnClose, false); /* Initialize GroupFrameSettingsWidget */ - ui->groupFrameSettingsWidget->setOpenAllInNewTabText(tr("Open each topic in a new tab")); + ui->groupFrameSettingsWidget->setOpenAllInNewTabText(tr("Open each board in a new tab")); ui->groupFrameSettingsWidget->setType(GroupFrameSettings::Posted); } diff --git a/retroshare-gui/src/gui/settings/PostedPage.h b/retroshare-gui/src/gui/settings/PostedPage.h index d8189e509..b3c31c6ba 100644 --- a/retroshare-gui/src/gui/settings/PostedPage.h +++ b/retroshare-gui/src/gui/settings/PostedPage.h @@ -21,7 +21,8 @@ #ifndef POSTEDPAGE_H #define POSTEDPAGE_H -#include +#include "retroshare-gui/configpage.h" +#include "gui/common/FilesDefs.h" namespace Ui { class PostedPage; @@ -38,8 +39,8 @@ public: /** Loads the settings for this page */ virtual void load(); - virtual QPixmap iconPixmap() const { return QPixmap(":/icons/settings/posted.svg") ; } - virtual QString pageName() const { return tr("Links") ; } + virtual QPixmap iconPixmap() const { return FilesDefs::getPixmapFromQtResourcePath(":/icons/settings/posted.svg") ; } + virtual QString pageName() const { return tr("Boards") ; } virtual QString helpText() const { return ""; } private: diff --git a/retroshare-gui/src/gui/settings/RSPermissionMatrixWidget.cpp b/retroshare-gui/src/gui/settings/RSPermissionMatrixWidget.cpp index b1e9c5c63..7d869ab57 100644 --- a/retroshare-gui/src/gui/settings/RSPermissionMatrixWidget.cpp +++ b/retroshare-gui/src/gui/settings/RSPermissionMatrixWidget.cpp @@ -35,6 +35,8 @@ #include #include #include +#include "rsitems/rsserviceids.h" +#include #define NOT_IMPLEMENTED std::cerr << __PRETTY_FUNCTION__ << ": not yet implemented." << std::endl; @@ -57,6 +59,7 @@ RSPermissionMatrixWidget::RSPermissionMatrixWidget(QWidget *parent) :QFrame(parent) { _painter = new QPainter(); + _current_service_id = 0; setMouseTracking(true) ; @@ -530,6 +533,45 @@ void RSPermissionMatrixWidget::paintEvent(QPaintEvent *) _max_height = S*fMATRIX_START_Y + (peer_ids.size()+3) * S*fROW_SIZE ; _max_width = matrix_start_x + (service_ids.size()+3) * S*fCOL_SIZE ; + if(n_row_selected == -1) + { + for(uint32_t i=0;i> 8); + uint16_t serviceOrig=(service_ids[i]>>8) & 0xffff; + QTextDocument td; + td.setHtml(ServiceDescription(serviceOrig)); + QString service_name = "Service: 0x"+ QString::number(serviceOrig,16) + " " + QString::fromStdString(rsServiceControl->getServiceName(service_ids[i])); + QBrush brush ; + brush.setColor(QColor::fromHsvF(0.0f,0.0f,1.0f,0.8f)); + brush.setStyle(Qt::SolidPattern) ; + QPen pen ; + pen.setWidth(1) ; + pen.setBrush(FOREGROUND_COLOR) ; + _painter->setPen(pen) ; + QRect position = computeNodePosition(0,i,false) ; + int popup_x = position.x() + (50 * S / 14.0); + int popup_y = position.y() - (10 * S / 14.0) + line_height; + int popup_width = std::max((int)td.size().width(), fm.width(service_name)) + S; + int popup_height = td.size().height() + line_height*2; + while (popup_x + popup_width > _max_width) + popup_x -= S; + if (popup_y + popup_height > _max_height) + popup_y -= popup_height; + QRect info_pos(popup_x, popup_y, popup_width, popup_height) ; + _painter->fillRect(info_pos,brush) ; + _painter->drawRect(info_pos) ; + float x = info_pos.x() + 5*S/14.0 ; + float y = info_pos.y() + line_height + 1*S/14.0 ; + _painter->drawText(QPointF(x,y), service_name) ; y += line_height ; + _painter->translate(QPointF(popup_x, popup_y+2*line_height)); + td.drawContents(_painter); + } + } + } + /* Stop the painter */ _painter->end(); } @@ -610,4 +652,33 @@ void RSPermissionMatrixWidget::userPermissionSwitched(uint32_t /* ServiceId */,c NOT_IMPLEMENTED ; } - +QString RSPermissionMatrixWidget::ServiceDescription(uint16_t serviceid) +{ + switch(serviceid) + { + case RS_SERVICE_TYPE_DISC: return tr("Location info exchange between friends. Helps to find actual address in case of dynamic IPs
Without it you will have to rely on DHT only for getting fresh addresses"); + case RS_SERVICE_TYPE_CHAT: return tr("Used by direct F2F chat, distant chat and chat lobbies"); + case RS_SERVICE_TYPE_MSG: return tr("Mailing service. Also required for direct f2f chat"); + case RS_SERVICE_TYPE_TURTLE: return tr("Anonymous routing. Used by file transfers and file search,
distant chat, distant mail and distant channels/etc sync"); + case RS_SERVICE_TYPE_HEARTBEAT: return tr("Checks if peers alive"); + case RS_SERVICE_TYPE_FILE_TRANSFER: return tr("File transfer. If you kill it - you won't be able to dl files from friend shares. Anonymous access unnaffected"); + case RS_SERVICE_TYPE_GROUTER: return tr("Used by distant mail for immediate delivery using anonymous tunnels (turtle router)"); + case RS_SERVICE_TYPE_FILE_DATABASE: return tr("Exchange shared directories info, aka browsable(visible) files"); + case RS_SERVICE_TYPE_SERVICEINFO: return tr("Allows your node to tell to your friends which service are ON on your side, and vice-versa"); + case RS_SERVICE_TYPE_BWCTRL: return tr("Speed management"); + case RS_SERVICE_TYPE_GXS_TUNNEL: return tr("Used by distant chat, distant mail, and distant channels sync for transfer data using anonymous tunnels"); + case RS_SERVICE_TYPE_BANLIST: return tr("IP filter lists exchange"); + case RS_SERVICE_TYPE_STATUS: return tr("Share user status like online, away, busy with friends"); + case RS_SERVICE_GXS_TYPE_GXSID: return tr("Identity data exchange. Required by all identities-related functions like chats, forums, mail, etc"); + case RS_SERVICE_GXS_TYPE_PHOTO: return tr("RS_SERVICE_GXS_TYPE_PHOTO"); + case RS_SERVICE_GXS_TYPE_WIKI: return tr("RS_SERVICE_GXS_TYPE_WIKI"); + case RS_SERVICE_GXS_TYPE_FORUMS: return tr("Transfer Forums"); + case RS_SERVICE_GXS_TYPE_POSTED: return tr("Transfer Posted"); + case RS_SERVICE_GXS_TYPE_CHANNELS: return tr("Transfer Channels"); + case RS_SERVICE_GXS_TYPE_GXSCIRCLE: return tr("Transfer Circles"); + case RS_SERVICE_GXS_TYPE_REPUTATION:return tr("Votes exchange - bans/upvotes for Identities"); + case RS_SERVICE_TYPE_GXS_TRANS: return tr("Used by distant mail for deferred delivery - stored at friends when target offline"); + case RS_SERVICE_TYPE_RTT: return tr("Measures the Round Trip Time between you and your friends"); + default: return tr("unknown"); + } +} diff --git a/retroshare-gui/src/gui/settings/RSPermissionMatrixWidget.h b/retroshare-gui/src/gui/settings/RSPermissionMatrixWidget.h index 6b64150cb..ffc9a32b0 100644 --- a/retroshare-gui/src/gui/settings/RSPermissionMatrixWidget.h +++ b/retroshare-gui/src/gui/settings/RSPermissionMatrixWidget.h @@ -59,6 +59,7 @@ class RSPermissionMatrixWidget: public QFrame public: RSPermissionMatrixWidget(QWidget *parent=NULL); virtual ~RSPermissionMatrixWidget() ; + QString ServiceDescription(uint16_t serviceid); public slots: void setHideOffline(bool hide); diff --git a/retroshare-gui/src/gui/settings/ServerPage.cpp b/retroshare-gui/src/gui/settings/ServerPage.cpp index 6cc23cb38..00d01eee8 100755 --- a/retroshare-gui/src/gui/settings/ServerPage.cpp +++ b/retroshare-gui/src/gui/settings/ServerPage.cpp @@ -23,6 +23,7 @@ #include #include "rshare.h" #include "rsharesettings.h" +#include "util/i2pcommon.h" #include "util/RsNetUtil.h" #include "util/misc.h" @@ -82,6 +83,10 @@ ServerPage::ServerPage(QWidget * parent, Qt::WindowFlags flags) manager = NULL ; mOngoingConnectivityCheck = -1; +#ifndef RS_USE_I2P_BOB + ui.hiddenServiceTab->removeTab(TAB_HIDDEN_SERVICE_I2P_BOB); // warning: the order of operation here is very important. +#endif + if(RsAccounts::isHiddenNode()) { if(RsAccounts::isTorAuto()) @@ -91,8 +96,6 @@ ServerPage::ServerPage(QWidget * parent, Qt::WindowFlags flags) ui.tabWidget->removeTab(TAB_RELAYS) ; // remove relays. Not useful in Tor mode. ui.tabWidget->removeTab(TAB_IP_FILTERS) ; // remove IP filters. Not useful in Tor mode. - ui.hiddenServiceTab->removeTab(TAB_HIDDEN_SERVICE_I2P_BOB) ; // remove the Automatic I2P/BOB tab - ui.hiddenpage_proxyAddress_i2p->hide() ; ui.hiddenpage_proxyLabel_i2p->hide() ; ui.hiddenpage_proxyPort_i2p->hide() ; @@ -109,8 +112,7 @@ ServerPage::ServerPage(QWidget * parent, Qt::WindowFlags flags) } else { - ui.hiddenServiceTab->removeTab(TAB_HIDDEN_SERVICE_I2P_BOB); // warning: the order of operation here is very important. - ui.hiddenServiceTab->removeTab(TAB_HIDDEN_SERVICE_INCOMING); + ui.hiddenServiceTab->removeTab(TAB_HIDDEN_SERVICE_INCOMING); // warning: the order of operation here is very important. } ui.filteredIpsTable->setHorizontalHeaderItem(COLUMN_RANGE,new QTableWidgetItem(tr("IP Range"))) ; @@ -882,19 +884,19 @@ void ServerPage::updateStatus() /******* Network Status Tab *******/ if(net_status.netUpnpOk) - ui.iconlabel_upnp->setPixmap(QPixmap(":/images/ledon1.png")); + ui.iconlabel_upnp->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/ledon1.png")); else - ui.iconlabel_upnp->setPixmap(QPixmap(":/images/ledoff1.png")); + ui.iconlabel_upnp->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/ledoff1.png")); if (net_status.netLocalOk) - ui.iconlabel_netLimited->setPixmap(QPixmap(":/images/ledon1.png")); + ui.iconlabel_netLimited->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/ledon1.png")); else - ui.iconlabel_netLimited->setPixmap(QPixmap(":/images/ledoff1.png")); + ui.iconlabel_netLimited->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/ledoff1.png")); if (net_status.netExtAddressOk) - ui.iconlabel_ext->setPixmap(QPixmap(":/images/ledon1.png")); + ui.iconlabel_ext->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/ledon1.png")); else - ui.iconlabel_ext->setPixmap(QPixmap(":/images/ledoff1.png")); + ui.iconlabel_ext->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/ledoff1.png")); } @@ -1078,7 +1080,7 @@ void ServerPage::loadHiddenNode() ui.textlabel_hiddenMode->show(); ui.iconlabel_hiddenMode->show() ; - ui.iconlabel_hiddenMode->setPixmap(QPixmap(":/images/ledon1.png")); + ui.iconlabel_hiddenMode->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/ledon1.png")); // CHANGE OPTIONS ON whileBlocking(ui.discComboBox)->removeItem(3); @@ -1127,9 +1129,9 @@ void ServerPage::loadHiddenNode() for(std::list::const_iterator it(detail.ipAddressList.begin());it!=detail.ipAddressList.end();++it) whileBlocking(ui.ipAddressList)->addItem(QString::fromStdString(*it)); - ui.iconlabel_upnp->setPixmap(QPixmap(":/images/ledoff1.png")); - ui.iconlabel_netLimited->setPixmap(QPixmap(":/images/ledoff1.png")); - ui.iconlabel_ext->setPixmap(QPixmap(":/images/ledoff1.png")); + ui.iconlabel_upnp->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/ledoff1.png")); + ui.iconlabel_netLimited->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/ledoff1.png")); + ui.iconlabel_ext->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/ledoff1.png")); whileBlocking(ui.allowIpDeterminationCB)->setChecked(false); whileBlocking(ui.allowIpDeterminationCB)->setEnabled(false); @@ -1233,19 +1235,19 @@ void ServerPage::updateStatusHiddenNode() /******* Network Status Tab *******/ if(net_status.netUpnpOk) - ui.iconlabel_upnp->setPixmap(QPixmap(":/images/ledon1.png")); + ui.iconlabel_upnp->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/ledon1.png")); else - ui.iconlabel_upnp->setPixmap(QPixmap(":/images/ledoff1.png")); + ui.iconlabel_upnp->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/ledoff1.png")); if (net_status.netLocalOk) - ui.iconlabel_netLimited->setPixmap(QPixmap(":/images/ledon1.png")); + ui.iconlabel_netLimited->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/ledon1.png")); else - ui.iconlabel_netLimited->setPixmap(QPixmap(":/images/ledoff1.png")); + ui.iconlabel_netLimited->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/ledoff1.png")); if (net_status.netExtAddressOk) - ui.iconlabel_ext->setPixmap(QPixmap(":/images/ledon1.png")); + ui.iconlabel_ext->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/ledon1.png")); else - ui.iconlabel_ext->setPixmap(QPixmap(":/images/ledoff1.png")); + ui.iconlabel_ext->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/ledoff1.png")); #endif } @@ -1304,12 +1306,12 @@ void ServerPage::updateOutProxyIndicator() if(socket.waitForConnected(500)) { socket.disconnectFromHost(); - ui.iconlabel_tor_outgoing->setPixmap(QPixmap(ICON_STATUS_OK)) ; + ui.iconlabel_tor_outgoing->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_OK)) ; ui.iconlabel_tor_outgoing->setToolTip(tr("Proxy seems to work.")) ; } else { - ui.iconlabel_tor_outgoing->setPixmap(QPixmap(ICON_STATUS_UNKNOWN)) ; + ui.iconlabel_tor_outgoing->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_UNKNOWN)) ; ui.iconlabel_tor_outgoing->setToolTip(tr("Tor proxy is not enabled")) ; } @@ -1318,12 +1320,12 @@ void ServerPage::updateOutProxyIndicator() if(socket.waitForConnected(500)) { socket.disconnectFromHost(); - ui.iconlabel_i2p_outgoing->setPixmap(QPixmap(ICON_STATUS_OK)) ; + ui.iconlabel_i2p_outgoing->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_OK)) ; ui.iconlabel_i2p_outgoing->setToolTip(tr("Proxy seems to work.")) ; } else { - ui.iconlabel_i2p_outgoing->setPixmap(QPixmap(ICON_STATUS_UNKNOWN)) ; + ui.iconlabel_i2p_outgoing->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_UNKNOWN)) ; ui.iconlabel_i2p_outgoing->setToolTip(tr("I2P proxy is not enabled")) ; } @@ -1332,12 +1334,12 @@ void ServerPage::updateOutProxyIndicator() if(true == (mBobAccessible = socket.waitForConnected(500))) { socket.disconnectFromHost(); - ui.iconlabel_i2p_outgoing_2->setPixmap(QPixmap(ICON_STATUS_OK)) ; + ui.iconlabel_i2p_outgoing_2->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_OK)) ; ui.iconlabel_i2p_outgoing_2->setToolTip(tr("BOB is running and accessible")) ; } else { - ui.iconlabel_i2p_outgoing_2->setPixmap(QPixmap(ICON_STATUS_UNKNOWN)) ; + ui.iconlabel_i2p_outgoing_2->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_UNKNOWN)) ; ui.iconlabel_i2p_outgoing_2->setToolTip(tr("BOB is not accessible! Is it running?")) ; } } @@ -1349,13 +1351,13 @@ void ServerPage::updateInProxyIndicator() if(!mIsHiddenNode) return ; - //ui.iconlabel_tor_incoming->setPixmap(QPixmap(ICON_STATUS_UNKNOWN)) ; - //ui.testIncomingTor_PB->setIcon(QIcon(":/loader/circleball-16.gif")) ; + //ui.iconlabel_tor_incoming->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_UNKNOWN)) ; + //ui.testIncomingTor_PB->setIcon(FilesDefs::getIconFromQtResourcePath(":/loader/circleball-16.gif")) ; QMovie *movie = new QMovie(":/images/loader/circleball-16.gif"); ui.iconlabel_service_incoming->setMovie(movie); movie->start(); - if (mHiddenType == RS_HIDDEN_TYPE_I2P && mBobSettings.enableBob) { + if (mHiddenType == RS_HIDDEN_TYPE_I2P && mBobSettings.enable) { QTcpSocket tcpSocket; @@ -1442,15 +1444,16 @@ void ServerPage::getNewKey() void ServerPage::loadKey() { - mBobSettings.keys = ui.pteBobServerKey->toPlainText().toStdString(); - mBobSettings.addr = p3I2pBob::keyToBase32Addr(mBobSettings.keys); + mBobSettings.address.privateKey = ui.pteBobServerKey->toPlainText().toStdString(); + mBobSettings.address.publicKey = i2p::publicKeyFromPrivate(mBobSettings.address.privateKey); + mBobSettings.address.base32 = i2p::keyToBase32Addr(mBobSettings.address.publicKey); rsAutoProxyMonitor::taskSync(autoProxyType::I2PBOB, autoProxyTask::setSettings, &mBobSettings); } void ServerPage::enableBob(bool checked) { - mBobSettings.enableBob = checked; + mBobSettings.enable = checked; rsAutoProxyMonitor::taskSync(autoProxyType::I2PBOB, autoProxyTask::setSettings, &mBobSettings); @@ -1490,7 +1493,7 @@ void ServerPage::toggleBobAdvancedSettings(bool checked) { ui.swBobAdvanced->setCurrentIndex(checked ? 1 : 0); - if (!mBobSettings.keys.empty()) { + if (!mBobSettings.address.privateKey.empty()) { if (checked) { ui.pbBobGenAddr->show(); } else { @@ -1581,9 +1584,9 @@ void ServerPage::loadCommon() whileBlocking(ui.hiddenpage_proxyPort_i2p_2)->setValue(proxyport); // this one is for bob tab // don't use whileBlocking here - ui.cb_enableBob->setChecked(mBobSettings.enableBob); + ui.cb_enableBob->setChecked(mBobSettings.enable); - if (!mBobSettings.keys.empty()) { + if (!mBobSettings.address.privateKey.empty()) { ui.lBobB32Addr->show(); ui.leBobB32Addr->show(); } @@ -1626,13 +1629,13 @@ void ServerPage::saveBob() void ServerPage::updateStatusBob() { - QString addr = QString::fromStdString(mBobSettings.addr); + QString addr = QString::fromStdString(mBobSettings.address.base32); if (ui.leBobB32Addr->text() != addr) { ui.leBobB32Addr->setText(addr); ui.hiddenpage_serviceAddress->setText(addr); - ui.pteBobServerKey->setPlainText(QString::fromStdString(mBobSettings.keys)); + ui.pteBobServerKey->setPlainText(QString::fromStdString(mBobSettings.address.privateKey)); - if (!mBobSettings.keys.empty()) { + if (!mBobSettings.address.privateKey.empty()) { // we have an addr -> show fields ui.lBobB32Addr->show(); ui.leBobB32Addr->show(); @@ -1658,7 +1661,7 @@ void ServerPage::updateStatusBob() QString bobSimpleText = QString(); bobSimpleText.append(tr("RetroShare uses BOB to set up a %1 tunnel at %2:%3 (named %4)\n\n" "When changing options (e.g. port) use the buttons at the bottom to restart BOB.\n\n"). - arg(mBobSettings.keys.empty() ? tr("client") : tr("server"), + arg(mBobSettings.address.privateKey.empty() ? tr("client") : tr("server"), ui.hiddenpage_proxyAddress_i2p_2->text(), ui.hiddenpage_proxyPort_i2p_2->text(), bs.tunnelName.empty() ? tr("unknown") : @@ -1671,7 +1674,7 @@ void ServerPage::updateStatusBob() case csConnected: case csDoDisconnect: case csWaitForBob: - ui.iconlabel_i2p_bob->setPixmap(QPixmap(ICON_STATUS_WORKING)); + ui.iconlabel_i2p_bob->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_WORKING)); ui.iconlabel_i2p_bob->setToolTip(tr("BOB is processing a request")); enableBobElements(false); @@ -1704,7 +1707,7 @@ void ServerPage::updateStatusBob() // get error msg from bob rsAutoProxyMonitor::taskSync(autoProxyType::I2PBOB, autoProxyTask::getErrorInfo, &errorString); - ui.iconlabel_i2p_bob->setPixmap(QPixmap(ICON_STATUS_ERROR)); + ui.iconlabel_i2p_bob->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_ERROR)); ui.iconlabel_i2p_bob->setToolTip(tr("BOB is broken\n") + QString::fromStdString(errorString)); enableBobElements(false); @@ -1720,7 +1723,7 @@ void ServerPage::updateStatusBob() case csIdel: switch (bs.ct) { case ctRunSetUp: - ui.iconlabel_i2p_bob->setPixmap(QPixmap(ICON_STATUS_OK)); + ui.iconlabel_i2p_bob->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_OK)); ui.iconlabel_i2p_bob->setToolTip(tr("BOB tunnel is running")); enableBobElements(false); @@ -1733,7 +1736,7 @@ void ServerPage::updateStatusBob() break; case ctRunCheck: case ctRunGetKeys: - ui.iconlabel_i2p_bob->setPixmap(QPixmap(ICON_STATUS_WORKING)); + ui.iconlabel_i2p_bob->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_WORKING)); ui.iconlabel_i2p_bob->setToolTip(tr("BOB is processing a request")); enableBobElements(false); @@ -1746,7 +1749,7 @@ void ServerPage::updateStatusBob() break; case ctRunShutDown: case ctIdle: - ui.iconlabel_i2p_bob->setPixmap(QPixmap(ICON_STATUS_UNKNOWN)); + ui.iconlabel_i2p_bob->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_UNKNOWN)); ui.iconlabel_i2p_bob->setToolTip(tr("BOB tunnel is not running")); enableBobElements(true); @@ -1780,15 +1783,15 @@ void ServerPage::updateStatusBob() void ServerPage::setUpBobElements() { - ui.gbBob->setEnabled(mBobSettings.enableBob); - if (mBobSettings.enableBob) { + ui.gbBob->setEnabled(mBobSettings.enable); + if (mBobSettings.enable) { ui.hiddenpage_proxyAddress_i2p->setEnabled(false); ui.hiddenpage_proxyAddress_i2p->setToolTip("Use I2P/BOB settings to change this value"); ui.hiddenpage_proxyPort_i2p->setEnabled(false); ui.hiddenpage_proxyPort_i2p->setToolTip("Use I2P/BOB settings to change this value"); - ui.leBobB32Addr->setText(QString::fromStdString(mBobSettings.addr)); - ui.pteBobServerKey->setPlainText(QString::fromStdString(mBobSettings.keys)); + ui.leBobB32Addr->setText(QString::fromStdString(mBobSettings.address.base32)); + ui.pteBobServerKey->setPlainText(QString::fromStdString(mBobSettings.address.privateKey)); // cast to int to avoid problems int li, lo, qi, qo, vi, vo; @@ -1841,14 +1844,14 @@ void ServerPage::updateInProxyIndicatorResult(bool success) if (success) { std::cerr <<"Connected!" << std::endl; - ui.iconlabel_service_incoming->setPixmap(QPixmap(ICON_STATUS_OK)) ; + ui.iconlabel_service_incoming->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_OK)) ; ui.iconlabel_service_incoming->setToolTip(tr("You are reachable through the hidden service.")) ; - //ui.testIncomingTor_PB->setIcon(QIcon(ICON_STATUS_OK)) ; + //ui.testIncomingTor_PB->setIcon(FilesDefs::getIconFromQtResourcePath(ICON_STATUS_OK)) ; } else { std::cerr <<"Failed!" << std::endl; - //ui.testIncomingTor_PB->setIcon(QIcon(ICON_STATUS_UNKNOWN)) ; - ui.iconlabel_service_incoming->setPixmap(QPixmap(ICON_STATUS_UNKNOWN)) ; + //ui.testIncomingTor_PB->setIcon(FilesDefs::getIconFromQtResourcePath(ICON_STATUS_UNKNOWN)) ; + ui.iconlabel_service_incoming->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_UNKNOWN)) ; ui.iconlabel_service_incoming->setToolTip(tr("The proxy is not enabled or broken.\nAre all services up and running fine??\nAlso check your ports!")) ; } // delete movie diff --git a/retroshare-gui/src/gui/settings/ServerPage.h b/retroshare-gui/src/gui/settings/ServerPage.h index 247a72c8c..de4751d5e 100755 --- a/retroshare-gui/src/gui/settings/ServerPage.h +++ b/retroshare-gui/src/gui/settings/ServerPage.h @@ -36,8 +36,9 @@ #include #include -#include -#include +#include "retroshare-gui/configpage.h" +#include "retroshare-gui/RsAutoUpdatePage.h" +#include "gui/common/FilesDefs.h" class QNetworkReply; @@ -55,7 +56,7 @@ public: /** Loads the settings for this page */ virtual void load(); - virtual QPixmap iconPixmap() const { return QPixmap(":/icons/png/network.png") ; } + virtual QPixmap iconPixmap() const { return FilesDefs::getPixmapFromQtResourcePath(":/icons/png/network.png") ; } virtual QString pageName() const { return tr("Network") ; } virtual QString helpText() const { return ""; } diff --git a/retroshare-gui/src/gui/settings/ServerPage.ui b/retroshare-gui/src/gui/settings/ServerPage.ui index 223820604..5ef7308b1 100755 --- a/retroshare-gui/src/gui/settings/ServerPage.ui +++ b/retroshare-gui/src/gui/settings/ServerPage.ui @@ -6,8 +6,8 @@ 0 0 - 1283 - 929 + 712 + 502 @@ -26,7 +26,7 @@ - 1 + 0 diff --git a/retroshare-gui/src/gui/settings/ServicePermissionsPage.h b/retroshare-gui/src/gui/settings/ServicePermissionsPage.h index 5c98aae1e..94c22f9b0 100644 --- a/retroshare-gui/src/gui/settings/ServicePermissionsPage.h +++ b/retroshare-gui/src/gui/settings/ServicePermissionsPage.h @@ -25,6 +25,7 @@ #include #include "ui_ServicePermissionsPage.h" +#include "gui/common/FilesDefs.h" class ServicePermissionsPage: public ConfigPage { @@ -37,7 +38,7 @@ public: /** Loads the settings for this page */ virtual void load() {} - virtual QPixmap iconPixmap() const { return QPixmap(":/icons/settings/permissions.svg") ; } + virtual QPixmap iconPixmap() const { return FilesDefs::getPixmapFromQtResourcePath(":/icons/settings/permissions.svg") ; } virtual QString pageName() const { return tr("Permissions") ; } virtual QString helpText() const ; diff --git a/retroshare-gui/src/gui/settings/SoundPage.h b/retroshare-gui/src/gui/settings/SoundPage.h index a8c2d9acd..d20426d83 100644 --- a/retroshare-gui/src/gui/settings/SoundPage.h +++ b/retroshare-gui/src/gui/settings/SoundPage.h @@ -23,8 +23,9 @@ #include -#include +#include "retroshare-gui/configpage.h" #include "ui_SoundPage.h" +#include "gui/common/FilesDefs.h" class SoundPage : public ConfigPage { @@ -39,7 +40,7 @@ public: /** Loads the settings for this page */ virtual void load(); - virtual QPixmap iconPixmap() const { return QPixmap(":/icons/settings/sound.svg") ; } + virtual QPixmap iconPixmap() const { return FilesDefs::getPixmapFromQtResourcePath(":/icons/settings/sound.svg") ; } virtual QString pageName() const { return tr("Sound") ; } virtual QString helpText() const { return ""; } diff --git a/retroshare-gui/src/gui/settings/TransferPage.h b/retroshare-gui/src/gui/settings/TransferPage.h index 8f4c8f0dd..19ceedfca 100644 --- a/retroshare-gui/src/gui/settings/TransferPage.h +++ b/retroshare-gui/src/gui/settings/TransferPage.h @@ -23,8 +23,9 @@ # include -#include +#include "retroshare-gui/configpage.h" #include "ui_TransferPage.h" +#include "gui/common/FilesDefs.h" class TransferPage: public ConfigPage { @@ -37,7 +38,7 @@ class TransferPage: public ConfigPage /** Loads the settings for this page */ virtual void load() ; - virtual QPixmap iconPixmap() const { return QPixmap(":/icons/settings/filesharing.svg") ; } + virtual QPixmap iconPixmap() const { return FilesDefs::getPixmapFromQtResourcePath(":/icons/settings/filesharing.svg") ; } virtual QString pageName() const { return tr("Files") ; } virtual QString helpText() const { return ""; } diff --git a/retroshare-gui/src/gui/settings/TransferPage.ui b/retroshare-gui/src/gui/settings/TransferPage.ui index 04d1d9400..4c925ebb1 100644 --- a/retroshare-gui/src/gui/settings/TransferPage.ui +++ b/retroshare-gui/src/gui/settings/TransferPage.ui @@ -6,500 +6,534 @@ 0 0 - 1339 - 1519 + 701 + 527 - - - - - Shared Directories + + + + + 0 - - - - - Edit Share - - - - - - - true - - - Automatically share incoming directory (Recommended) - - - true - - - - - - - - - - 0 - 0 - - - - Auto-check shared directories every - - - true - - - - - - - minute(s) - - - 1 - - - 5000 - - - 10 - - - - - - - - - - - <html><head/><body><p>Tells Retroshare to follow the links. Loops and duplicate directories are automatically taken care of. If unchecked, Retroshare will just ignore symbolic links to both files and directories.</p></body></html> - - - follow symbolic links - - - true - - - - - - - - - - - <html><head/><body><p>When following sybolic links, Retroshare can encounter the same directory/file more than once. If checked, this option will make tell Retroshare to silently ignore the file. This option saves Retroshare indexing against directory loops.</p></body></html> - - - Ignore duplicate files/directories - - - - - - - Maximum depth (0=unlimited): - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - <html><head/><body><p>This value allows you to limit the depth of the directory hierarchy you are indexing, starting from the base directory. If you allow Retroshare to follow symbolic links and unchecked &quot;Ignore duplicate files/directories&quot;, this option will avoid that Retroshare loops indefinitly while parsing directories.</p></body></html> - - - - - - - - - - - Ignore files ending with: - - - - - - - <html><head/><body><p>Add any combination of suffixes separated by &quot;;&quot; for instance &quot;~;.bak;.old&quot;</p></body></html> - - - - - - - - - - - ignore files starting with: - - - - - - - <html><head/><body><p>Add any combination of prefixes, separated by &quot;;&quot; for instance &quot;.;~&quot;</p></body></html> - - - - - - - - - - - - Incoming Directory - - - - - - - - true - - - - - - - - 31 - 31 - - - - - 31 - 31 - - - - Browse - - - - - - - :/images/directoryselect_24x24_shadow.png:/images/directoryselect_24x24_shadow.png - - - - 24 - 24 - - - - - - - - - - <html><head/><body><p><span style=" font-weight:600;">WARNING</span>: Some collection may contains a lot of files.</p><p>With this option you cannot check the collection contents before download.</p></body></html> - - - Automatically donwload RsCollection file content (Not recommended) - - - - - - - - - - Partials Directory - - - - - - true - - - - - - - - 31 - 31 - - - - - 31 - 31 - - - - Browse - - - - - - - :/images/directoryselect_24x24_shadow.png:/images/directoryselect_24x24_shadow.png - - - - 24 - 24 - - - - - - - - - - - Transfer options - - - - - - + + + Directories + + + + + + Shared Directories + + - + - Maximum simultaneous downloads: + Edit Share - + + + true + - Maximum uploads per friend (0 = no limit) + Automatically share incoming directory (Recommended) + + + true - - - Default chunk strategy: - - + + + + + + 0 + 0 + + + + Auto-check shared directories every + + + true + + + + + + + minute(s) + + + 1 + + + 5000 + + + 10 + + + + - - - Safety disk space limit : - - + + + + + <html><head/><body><p>Tells Retroshare to follow the links. Loops and duplicate directories are automatically taken care of. If unchecked, Retroshare will just ignore symbolic links to both files and directories.</p></body></html> + + + follow symbolic links + + + true + + + + - - - Max. tunnel req. forwarded per second: - - + + + + + <html><head/><body><p>When following sybolic links, Retroshare can encounter the same directory/file more than once. If checked, this option will make tell Retroshare to silently ignore the file. This option saves Retroshare indexing against directory loops.</p></body></html> + + + Ignore duplicate files/directories + + + + + + + Maximum depth (0=unlimited): + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + <html><head/><body><p>This value allows you to limit the depth of the directory hierarchy you are indexing, starting from the base directory. If you allow Retroshare to follow symbolic links and unchecked &quot;Ignore duplicate files/directories&quot;, this option will avoid that Retroshare loops indefinitly while parsing directories.</p></body></html> + + + + - - - End-to-end encryption: - - + + + + + Ignore files ending with: + + + + + + + <html><head/><body><p>Add any combination of suffixes separated by &quot;;&quot; for instance &quot;~;.bak;.old&quot;</p></body></html> + + + + - + + + + + ignore files starting with: + + + + + + + <html><head/><body><p>Add any combination of prefixes, separated by &quot;;&quot; for instance &quot;.;~&quot;</p></body></html> + + + + + + + + + + + + Incoming Directory + + + + + + + + true + + + + + + + + 31 + 31 + + + + + 31 + 31 + + + + Browse + + + + + + + :/images/directoryselect_24x24_shadow.png:/images/directoryselect_24x24_shadow.png + + + + 24 + 24 + + + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">WARNING</span>: Some collection may contains a lot of files.</p><p>With this option you cannot check the collection contents before download.</p></body></html> + - Allow direct download: + Automatically donwload RsCollection file content (Not recommended) - - - - - - + + + + + + Partials Directory + + + + + true - - 1 - - - 1 - - - 5 - - - - - - - - true + + + + + 31 + 31 + + + + + 31 + 31 + - <html><head/><body><p><span style=" font-weight:600;">Streaming </span>causes the transfer to request 1MB file chunks in increasing order, facilitating preview while downloading. <span style=" font-weight:600;">Random</span> is purely random and favors swarming behavior. <span style=" font-weight:600;">Progressive</span> is a compromise, selecting the next chunk at random within less than 50MB after the end of the partial file. That allows some randomness while preventing large empty file initialization times.</p></body></html> + Browse - - - Streaming - - - - - Progressive - - - - - Random - - - - - - - - <html><head/><body><p>Retroshare will suspend all transfers and config file saving if the disk space goes below this limit. That prevents loss of information on some systems. A popup window will warn you when that happens.</p></body></html> + + - - MB + + + :/images/directoryselect_24x24_shadow.png:/images/directoryselect_24x24_shadow.png - - 50 + + + 24 + 24 + - - 1000 - - - 10 - - - 100 - - - - - - - <html><head/><body><p>This value controls how many tunnel request your peer can forward per second. </p><p>If you have a large internet bandwidth, you may raise this up to 30-40, to allow statistically longer tunnels to pass. Be very careful though, since this generates many small packets that can significantly slow down your own file transfer. </p><p>The default value is 20. If you're not sure, keep it that way.</p></body></html> - - - 5 - - - 100 - - - 1 - - - 20 - - - - - - - <html><head/><body><p>Anonymous tunnels can be end-o-end encrypted. In order to maintain backward compatibility, this can be made optional (choosing &quot;Accepted&quot;), but in the end, all Retroshare nodes will be switched to &quot;Enforced&quot;, meaning that all anonymous transfers will be end-to-end encrypted. With &quot;Accepted&quot;, it is likely that you will transfer using twice as many tunnels, since there is no way to know that an encrypted and a clear tunnel actually transfer from the same source.</p></body></html> - - - - Accepted - - - - - Enforced - - - - - - - - <html><head/><body><p>How RS manage direct download setting.</p></body></html> - - - - Yes - - - - - No - - - - - Per user - - - - - - - - - Trust friend nodes with banned files - - - - - - - true - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> + + + + + + true + + + <!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:'Sans'; font-size:9pt; 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-size:8pt; font-weight:600;">RetroShare</span><span style=" font-size:8pt;"> is capable of transferring data and search requests between peers that are not necessarily friends. This traffic however only transits through a connected list of friends and is anonymous.</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">You can separately setup share flags for each shared directory in the shared files dialog to be:</span></p> -<ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-size:8pt;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Browsable by friends</span>: files are seen by your friends.</li> -<li style=" font-size:8pt;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Anonymously shared</span>: files are anonymously reachable through distant F2F tunnels.</li></ul></body></html> - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - +</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:'Sans'; font-size:8pt; font-weight:600;">RetroShare</span><span style=" font-family:'Sans'; font-size:8pt;"> is capable of transferring data and search requests between peers that are not necessarily friends. This traffic however only transits through a connected list of friends and is anonymous.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans'; font-size:8pt;"><br /></p> +<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:'Sans'; font-size:8pt;">You can separately setup share flags for each shared directory in the shared files dialog to be:</span></p> +<ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Sans'; font-size:8pt;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Browsable by friends</span>: files are seen by your friends.</li> +<li style=" font-family:'Sans'; font-size:8pt;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Anonymously shared</span>: files are anonymously reachable through distant F2F tunnels.</li></ul></body></html>
+ + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Transfer options + + + + + + Transfer options + + + + + + + + + + Maximum simultaneous downloads: + + + + + + + Maximum uploads per friend (0 = no limit) + + + + + + + Default chunk strategy: + + + + + + + Safety disk space limit : + + + + + + + Max. tunnel req. forwarded per second: + + + + + + + End-to-end encryption: + + + + + + + Allow direct download: + + + + + + + + + + + true + + + 1 + + + 1 + + + 5 + + + + + + + + + + true + + + <html><head/><body><p><span style=" font-weight:600;">Streaming </span>causes the transfer to request 1MB file chunks in increasing order, facilitating preview while downloading. <span style=" font-weight:600;">Random</span> is purely random and favors swarming behavior. <span style=" font-weight:600;">Progressive</span> is a compromise, selecting the next chunk at random within less than 50MB after the end of the partial file. That allows some randomness while preventing large empty file initialization times.</p></body></html> + + + + Streaming + + + + + Progressive + + + + + Random + + + + + + + + <html><head/><body><p>Retroshare will suspend all transfers and config file saving if the disk space goes below this limit. That prevents loss of information on some systems. A popup window will warn you when that happens.</p></body></html> + + + MB + + + 50 + + + 1000 + + + 10 + + + 100 + + + + + + + <html><head/><body><p>This value controls how many tunnel request your peer can forward per second. </p><p>If you have a large internet bandwidth, you may raise this up to 30-40, to allow statistically longer tunnels to pass. Be very careful though, since this generates many small packets that can significantly slow down your own file transfer. </p><p>The default value is 20. If you're not sure, keep it that way.</p></body></html> + + + 5 + + + 100 + + + 1 + + + 20 + + + + + + + <html><head/><body><p>Anonymous tunnels can be end-o-end encrypted. In order to maintain backward compatibility, this can be made optional (choosing &quot;Accepted&quot;), but in the end, all Retroshare nodes will be switched to &quot;Enforced&quot;, meaning that all anonymous transfers will be end-to-end encrypted. With &quot;Accepted&quot;, it is likely that you will transfer using twice as many tunnels, since there is no way to know that an encrypted and a clear tunnel actually transfer from the same source.</p></body></html> + + + + Accepted + + + + + Enforced + + + + + + + + <html><head/><body><p>How RS manage direct download setting.</p></body></html> + + + + Yes + + + + + No + + + + + Per user + + + + + + + + + + + + Trust friend nodes with banned files + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/retroshare-gui/src/gui/settings/WebuiPage.h b/retroshare-gui/src/gui/settings/WebuiPage.h index ebbf0045c..f38662f5e 100644 --- a/retroshare-gui/src/gui/settings/WebuiPage.h +++ b/retroshare-gui/src/gui/settings/WebuiPage.h @@ -43,7 +43,7 @@ public: /** Loads the settings for this page */ virtual void load(); - virtual QPixmap iconPixmap() const { return QPixmap(":/icons/settings/webinterface.svg") ; } + virtual QPixmap iconPixmap() const { return FilesDefs::getPixmapFromQtResourcePath(":/icons/settings/webinterface.svg") ; } virtual QString pageName() const { return tr("Webinterface") ; } virtual QString helpText() const; diff --git a/retroshare-gui/src/gui/settings/rsettingswin.h b/retroshare-gui/src/gui/settings/rsettingswin.h index 7a9864fff..a3242aaa4 100755 --- a/retroshare-gui/src/gui/settings/rsettingswin.h +++ b/retroshare-gui/src/gui/settings/rsettingswin.h @@ -21,10 +21,12 @@ #ifndef RSETTINGSWIN_HPP_ #define RSETTINGSWIN_HPP_ +#include "gui/common/FilesDefs.h" #include #include +#include + #include "ui_settingsw.h" -#include "mainpage.h" class FloatingHelpBrowser; @@ -44,7 +46,7 @@ public: void postModDirectories(bool update_local); - virtual QIcon iconPixmap() const { return QIcon(IMAGE_PREFERENCES) ; } //MainPage + virtual QIcon iconPixmap() const { return FilesDefs::getIconFromQtResourcePath(IMAGE_PREFERENCES) ; } //MainPage virtual QString pageName() const { return tr("Preferences") ; } //MainPage protected: diff --git a/retroshare-gui/src/gui/settings/rsharesettings.cpp b/retroshare-gui/src/gui/settings/rsharesettings.cpp index 8888795f6..ea0bbcb69 100644 --- a/retroshare-gui/src/gui/settings/rsharesettings.cpp +++ b/retroshare-gui/src/gui/settings/rsharesettings.cpp @@ -113,10 +113,10 @@ void RshareSettings::initSettings() #else static QStringList styles = QStyleFactory::keys(); #if defined(Q_OS_WIN) - if (styles.contains("windowsvista", Qt::CaseInsensitive)) + if (styles.contains("Fusion", Qt::CaseInsensitive)) + setDefault(SETTING_STYLE, "Fusion"); + else if (styles.contains("windowsvista", Qt::CaseInsensitive)) setDefault(SETTING_STYLE, "windowsvista"); - else if (styles.contains("windowsxp", Qt::CaseInsensitive)) - setDefault(SETTING_STYLE, "windowsxp"); else #endif { @@ -1157,7 +1157,7 @@ void RshareSettings::setWebinterfaceEnabled(bool enabled) QString RshareSettings::getWebinterfaceFilesDirectory() { #ifdef WINDOWS_SYS - return valueFromGroup("Webinterface","directory","data/webui/").toString(); + return valueFromGroup("Webinterface","directory","./webui/").toString(); #else return valueFromGroup("Webinterface","directory","/usr/share/retroshare/webui/").toString(); #endif diff --git a/retroshare-gui/src/gui/statistics/BwCtrlWindow.h b/retroshare-gui/src/gui/statistics/BwCtrlWindow.h index d47867d4f..4126eb6bc 100644 --- a/retroshare-gui/src/gui/statistics/BwCtrlWindow.h +++ b/retroshare-gui/src/gui/statistics/BwCtrlWindow.h @@ -24,7 +24,7 @@ #include -#include "RsAutoUpdatePage.h" +#include #include "gui/common/RSGraphWidget.h" #include "ui_BwCtrlWindow.h" diff --git a/retroshare-gui/src/gui/statistics/DhtWindow.h b/retroshare-gui/src/gui/statistics/DhtWindow.h index 8199b5a1d..795a80a36 100644 --- a/retroshare-gui/src/gui/statistics/DhtWindow.h +++ b/retroshare-gui/src/gui/statistics/DhtWindow.h @@ -21,7 +21,8 @@ #ifndef RSDHT_WINDOW_H #define RSDHT_WINDOW_H -#include "RsAutoUpdatePage.h" +#include + #include "ui_DhtWindow.h" class DhtWindow : public RsAutoUpdatePage/*, public Ui::DhtWindow*/ { diff --git a/retroshare-gui/src/gui/statistics/GlobalRouterStatistics.cpp b/retroshare-gui/src/gui/statistics/GlobalRouterStatistics.cpp index 0a992353b..1fc583b18 100644 --- a/retroshare-gui/src/gui/statistics/GlobalRouterStatistics.cpp +++ b/retroshare-gui/src/gui/statistics/GlobalRouterStatistics.cpp @@ -126,7 +126,7 @@ void GlobalRouterStatistics::CustomPopupMenu( QPoint ) QTreeWidgetItem *item = treeWidget->currentItem(); if (item) { - contextMnu.addAction(QIcon(":/images/info16.png"), tr("Details"), this, SLOT(personDetails())); + contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/info16.png"), tr("Details"), this, SLOT(personDetails())); } diff --git a/retroshare-gui/src/gui/statistics/GlobalRouterStatistics.h b/retroshare-gui/src/gui/statistics/GlobalRouterStatistics.h index c8d132740..c8e49fb4a 100644 --- a/retroshare-gui/src/gui/statistics/GlobalRouterStatistics.h +++ b/retroshare-gui/src/gui/statistics/GlobalRouterStatistics.h @@ -24,7 +24,8 @@ #include #include -#include "RsAutoUpdatePage.h" +#include + #include "ui_GlobalRouterStatistics.h" class GlobalRouterStatisticsWidget ; diff --git a/retroshare-gui/src/gui/statistics/GxsIdStatistics.cpp b/retroshare-gui/src/gui/statistics/GxsIdStatistics.cpp new file mode 100644 index 000000000..a09053deb --- /dev/null +++ b/retroshare-gui/src/gui/statistics/GxsIdStatistics.cpp @@ -0,0 +1,588 @@ +/******************************************************************************* + * gui/statistics/GlobalRouterStatistics.cpp * + * * + * Copyright (c) 2011 Retroshare Team * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "GxsIdStatistics.h" + +#include "util/DateTime.h" +#include "util/QtVersion.h" +#include "util/misc.h" +#include "util/qtthreadsutils.h" + +static QColor colorScale(float f) +{ + if(f == 0) + return QColor::fromHsv(0,0,192) ; + else + return QColor::fromHsv((int)((1.0-f)*280),200,255) ; +} + +GxsIdStatistics::GxsIdStatistics(QWidget *parent) + : RsAutoUpdatePage(4000,parent) +{ + setupUi(this) ; + + _stats_F->setWidget(_tst_CW = new GxsIdStatisticsWidget); + m_bProcessSettings = false; + + // load settings + processSettings(true); +} + +GxsIdStatistics::~GxsIdStatistics() +{ + // save settings + processSettings(false); +} + +void GxsIdStatistics::processSettings(bool bLoad) +{ + m_bProcessSettings = true; + + Settings->beginGroup(QString("GlobalRouterStatistics")); + + if (bLoad) { + // load settings + + // state of splitter + //splitter->restoreState(Settings->value("Splitter").toByteArray()); + } else { + // save settings + + // state of splitter + //Settings->setValue("Splitter", splitter->saveState()); + + } + + Settings->endGroup(); + + m_bProcessSettings = false; +} + +void GxsIdStatistics::updateDisplay() +{ + _tst_CW->updateContent() ; + + static rstime_t last_data_update_time = 0; + rstime_t now = time(NULL); + + if(now > last_data_update_time + 60) + { + last_data_update_time = now; + _tst_CW->updateData(); + } +} + +static QString getServiceName(uint32_t s) +{ + switch(s) + { + default: + case 0x0011 /* GOSSIP_DISCOVERY */ : return QObject::tr("Discovery"); + case 0x0012 /* CHAT */ : return QObject::tr("Chat"); + case 0x0013 /* MSG */ : return QObject::tr("Messages"); + case 0x0014 /* TURTLE */ : return QObject::tr("Turtle"); + case 0x0016 /* HEARTBEAT */ : return QObject::tr("Heartbeat"); + case 0x0017 /* FILE_TRANSFER */ : return QObject::tr("File transfer"); + case 0x0018 /* GROUTER */ : return QObject::tr("Global router"); + case 0x0019 /* FILE_DATABASE */ : return QObject::tr("File database"); + case 0x0020 /* SERVICEINFO */ : return QObject::tr("Service info"); + case 0x0021 /* BANDWIDTH_CONTROL */ : return QObject::tr("Bandwidth control"); + case 0x0022 /* MAIL */ : return QObject::tr("Mail"); + case 0x0023 /* DIRECT_MAIL */ : return QObject::tr("Mail"); + case 0x0024 /* DISTANT_MAIL */ : return QObject::tr("Distant mail"); + case 0x0026 /* SERVICE_CONTROL */ : return QObject::tr("Service control"); + case 0x0027 /* DISTANT_CHAT */ : return QObject::tr("Distant chat"); + case 0x0028 /* GXS_TUNNEL */ : return QObject::tr("GXS Tunnel"); + case 0x0101 /* BANLIST */ : return QObject::tr("Ban list"); + case 0x0102 /* STATUS */ : return QObject::tr("Status"); + case 0x0200 /* NXS */ : return QObject::tr("NXS"); + case 0x0211 /* GXSID */ : return QObject::tr("Identities"); + case 0x0212 /* PHOTO */ : return QObject::tr("GXS Photo"); + case 0x0213 /* WIKI */ : return QObject::tr("GXS Wiki"); + case 0x0214 /* WIRE */ : return QObject::tr("GXS TheWire"); + case 0x0215 /* FORUMS */ : return QObject::tr("Forums"); + case 0x0216 /* POSTED */ : return QObject::tr("Boards"); + case 0x0217 /* CHANNELS */ : return QObject::tr("Channels"); + case 0x0218 /* GXSCIRCLE */ : return QObject::tr("Circles"); + /// entiti not gxs, but used with :dentities. + case 0x0219 /* REPUTATION */ : return QObject::tr("Reputation"); + case 0x0220 /* GXS_RECOGN */ : return QObject::tr("Recogn"); + case 0x0230 /* GXS_TRANS */ : return QObject::tr("GXS Transport"); + case 0x0240 /* JSONAPI */ : return QObject::tr("JSon API"); + + } +} + +static QString getUsageStatisticsName(RsIdentityUsage::UsageCode code) +{ + switch(code) + { + default: + case RsIdentityUsage::UNKNOWN_USAGE : return QObject::tr("Unknown"); + case RsIdentityUsage::GROUP_ADMIN_SIGNATURE_CREATION : return QObject::tr("Group admin signature creation"); + case RsIdentityUsage::GROUP_ADMIN_SIGNATURE_VALIDATION : return QObject::tr("Group admin signature validation"); + case RsIdentityUsage::GROUP_AUTHOR_SIGNATURE_CREATION : return QObject::tr("Group author signature creation"); + case RsIdentityUsage::GROUP_AUTHOR_SIGNATURE_VALIDATION : return QObject::tr("Group author signature validation"); + case RsIdentityUsage::MESSAGE_AUTHOR_SIGNATURE_CREATION : return QObject::tr("Message author signature creation"); + case RsIdentityUsage::MESSAGE_AUTHOR_SIGNATURE_VALIDATION : return QObject::tr("Message author signature validation"); + case RsIdentityUsage::GROUP_AUTHOR_KEEP_ALIVE : return QObject::tr("Routine group author signature check."); + case RsIdentityUsage::MESSAGE_AUTHOR_KEEP_ALIVE : return QObject::tr("Routine message author signature check"); + case RsIdentityUsage::CHAT_LOBBY_MSG_VALIDATION : return QObject::tr("Chat room signature validation"); + case RsIdentityUsage::GLOBAL_ROUTER_SIGNATURE_CHECK : return QObject::tr("Global router message validation"); + case RsIdentityUsage::GLOBAL_ROUTER_SIGNATURE_CREATION : return QObject::tr("Global router message creation"); + case RsIdentityUsage::GXS_TUNNEL_DH_SIGNATURE_CHECK : return QObject::tr("DH Key exchange validation for GXS tunnel"); + case RsIdentityUsage::GXS_TUNNEL_DH_SIGNATURE_CREATION : return QObject::tr("DH Key exchange creation for GXS tunnel"); + case RsIdentityUsage::IDENTITY_NEW_FROM_GXS_SYNC : return QObject::tr("New identity from GXS sync"); + case RsIdentityUsage::IDENTITY_NEW_FROM_DISCOVERY : return QObject::tr("New friend identity from discovery"); + case RsIdentityUsage::IDENTITY_NEW_FROM_EXPLICIT_REQUEST : return QObject::tr("New identity requested from friend node"); + case RsIdentityUsage::IDENTITY_GENERIC_SIGNATURE_CHECK : return QObject::tr("Generic signature validation"); + case RsIdentityUsage::IDENTITY_GENERIC_SIGNATURE_CREATION : return QObject::tr("Generic signature creation"); + case RsIdentityUsage::IDENTITY_GENERIC_ENCRYPTION : return QObject::tr("Generic data decryption"); + case RsIdentityUsage::IDENTITY_GENERIC_DECRYPTION : return QObject::tr("Generic data encryption"); + case RsIdentityUsage::CIRCLE_MEMBERSHIP_CHECK : return QObject::tr("Circle membership checking"); + } +} + +void GxsIdStatisticsWidget::updateData() +{ + // get the info, stats, histograms and pass them + + RsThread::async([this]() + { + // 1 - get group data + +#ifdef DEBUG_FORUMS + std::cerr << "Retrieving post data for post " << mThreadId << std::endl; +#endif + + auto pids = new std::list() ; + rsIdentity->getIdentitiesSummaries(*pids) ; + + RsQThreadUtils::postToObject( [pids,this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ + + const auto& ids(*pids); + + time_t now = time(NULL); + mPublishDateHist = Histogram(now - mNbWeeks*7*86400,now,mNbWeeks); + mLastUsedHist = Histogram(now - 3600*mNbHours,now,mNbHours); + mTotalIdentities = 0; + mUsageMap.clear(); + mPerServiceUsageMap.clear(); + + for(auto& meta:ids) + { + RsIdentityDetails det; + + if(!rsIdentity->getIdDetails(RsGxsId(meta.mGroupId),det)) + continue; + + mPublishDateHist.insert((double)meta.mPublishTs); + mLastUsedHist.insert((double)det.mLastUsageTS); + + for(auto it:det.mUseCases) + { + auto it2 = mUsageMap.find(it.first.mUsageCode); + if(it2 == mUsageMap.end()) + mUsageMap[it.first.mUsageCode] = 0 ; + + ++mUsageMap[it.first.mUsageCode]; + + uint32_t s = static_cast(it.first.mServiceId); + auto it3 = mPerServiceUsageMap.find(s); + if(it3 == mPerServiceUsageMap.end()) + mPerServiceUsageMap[s] = 0; + + ++mPerServiceUsageMap[s]; + } + + ++mTotalIdentities; + } + + delete pids; + + }, this ); + }); +} + +void GxsIdStatisticsWidget::updateContent() +{ + // Now draw the info int the widget's pixmap + + float size = QFontMetricsF(font()).height() ; + float fact = size/14.0 ; + + QPixmap tmppixmap(mMaxWidth, mMaxHeight); + tmppixmap.fill(Qt::transparent); + setFixedHeight(mMaxHeight); + + QPainter painter(&tmppixmap); + painter.initFrom(this); + painter.setPen(QColor::fromRgb(0,0,0)) ; + + QFont times_f(font());//"Times") ; + QFont monospace_f("Monospace") ; + monospace_f.setStyleHint(QFont::TypeWriter) ; + monospace_f.setPointSize(font().pointSize()) ; + + QFontMetricsF fm_monospace(monospace_f) ; + QFontMetricsF fm_times(times_f) ; + + int cellx = fm_monospace.width(QString(" ")) ; + int celly = fm_monospace.height() ; + + // Display general statistics + + int ox=5*fact,oy=15*fact ; + + painter.setFont(times_f) ; + painter.drawText(ox,oy,tr("Total identities: ")+QString::number(mTotalIdentities)) ; oy += celly*2 ; + + uint32_t total_per_type = 0; + for(auto it:mUsageMap) + total_per_type += it.second; + + painter.setFont(times_f) ; + painter.drawText(ox,oy,tr("Usage types") + "(" + QString::number(total_per_type) + " hits): ") ; oy += 2*celly; + + for(auto it:mUsageMap) + { + painter.drawText(ox+2*cellx,oy, getUsageStatisticsName(it.first) + ": " + QString::number(it.second)) ; + oy += celly ; + } + oy += celly ; + + // Display per-service statistics + + uint32_t total_per_service = 0; + for(auto it:mPerServiceUsageMap) + total_per_service += it.second; + + painter.setFont(times_f) ; + painter.drawText(ox,oy,tr("Usage per service") + "(" + QString::number(total_per_service) + " hits): ") ; oy += 2*celly; + + for(auto it:mPerServiceUsageMap) + { + painter.drawText(ox+2*cellx,oy, getServiceName(it.first) + ": " + QString::number(it.second)) ; + oy += celly ; + } + oy += celly ; + + // Draw the creation time histogram + + painter.setFont(times_f) ; + painter.drawText(ox,oy,tr("Identity age (in weeks):")) ; oy += celly ; + + uint32_t hist_height = 10; + oy += hist_height*celly; + + painter.drawLine(QPoint(ox+4*cellx,oy),QPoint(ox+4*cellx+cellx*mNbWeeks*2,oy)); + painter.drawLine(QPoint(ox+4*cellx,oy),QPoint(ox+4*cellx,oy-celly*hist_height)); + + uint32_t max_entry=0; + for(int i=0;i each [] shows a square (one per friend node) that is the routing probabilities for all connected friends + // computed using the "computeRoutingProbabilitites()" method. + // + // Own key ids + // key service id description + // + // Data items + // Msg id Local origin Destination Time Status + // + QPixmap tmppixmap(maxWidth, maxHeight); + tmppixmap.fill(Qt::transparent); + setFixedHeight(maxHeight); + + QPainter painter(&tmppixmap); + painter.initFrom(this); + painter.setPen(QColor::fromRgb(0,0,0)) ; + + QFont times_f(font());//"Times") ; + QFont monospace_f("Monospace") ; + monospace_f.setStyleHint(QFont::TypeWriter) ; + monospace_f.setPointSize(font().pointSize()) ; + + QFontMetricsF fm_monospace(monospace_f) ; + QFontMetricsF fm_times(times_f) ; + + static const int cellx = fm_monospace.width(QString(" ")) ; + static const int celly = fm_monospace.height() ; + + maxHeight = 500*fact ; + + // std::cerr << "Drawing into pixmap of size " << maxWidth << "x" << maxHeight << std::endl; + // draw... + int ox=5*fact,oy=5*fact ; + + + painter.setFont(times_f) ; + painter.drawText(ox,oy+celly,tr("Managed keys")+":" + QString::number(matrix_info.published_keys.size())) ; oy += celly*2 ; + + painter.setFont(monospace_f) ; + for(std::map::const_iterator it(matrix_info.published_keys.begin());it!=matrix_info.published_keys.end();++it) + { + QString packet_string ; + packet_string += QString::fromStdString(it->second.authentication_key.toStdString()) ; + packet_string += tr(" : Service ID =")+" "+QString::number(it->second.service_id,16) ; + packet_string += " \""+QString::fromUtf8(it->second.description_string.c_str()) + "\"" ; + + painter.drawText(ox+2*cellx,oy+celly,packet_string ) ; oy += celly ; + } + oy += celly ; + + + std::map > tos ; + + // Now draw the matrix + + QString prob_string ; + painter.setFont(times_f) ; + QString Q = tr("Routing matrix (") ; + + painter.drawText(ox+0*cellx,oy+fm_times.height(),Q) ; + + // draw scale + + for(int i=0;i<100*fact;++i) + { + painter.setPen(colorScale(i/100.0/fact)) ; + painter.drawLine(ox+fm_times.width(Q)+i,oy+fm_times.height()*0.5,ox+fm_times.width(Q)+i,oy+fm_times.height()) ; + } + painter.setPen(QColor::fromRgb(0,0,0)) ; + + painter.drawText(ox+fm_times.width(Q) + 102*fact,oy+celly,")") ; + + oy += celly ; + oy += celly ; + + //static const int MaxKeySize = 20*fact ; + painter.setFont(monospace_f) ; + + int n=0; + QString ids; + std::vector current_probs ; + int current_oy = 0 ; + + mMinWheelZoneX = ox+2*cellx ; + mMinWheelZoneY = oy ; + + RsGxsId current_id ; + float current_width=0 ; + + for(std::map >::const_iterator it(matrix_info.per_friend_probabilities.begin());it!=matrix_info.per_friend_probabilities.end();++it,++n) + if(n >= mCurrentN-PARTIAL_VIEW_SIZE/2 && n <= mCurrentN+PARTIAL_VIEW_SIZE/2) + { + ids = QString::fromStdString(it->first.toStdString())+" : " ; + painter.drawText(ox+2*cellx,oy+celly,ids) ; + + for(uint32_t i=0;isecond[i])) ; + + if(n == mCurrentN) + { + current_probs = it->second ; + current_oy = oy ; + current_id = it->first ; + current_width = ox+matrix_info.friend_ids.size()*cellx+fm_monospace.width(ids); + } + + oy += celly ; + } + mMaxWheelZoneX = ox+matrix_info.friend_ids.size()*cellx + fm_monospace.width(ids); + + RsIdentityDetails iddetails ; + if(rsIdentity->getIdDetails(current_id,iddetails)) + painter.drawText(current_width+cellx, current_oy+celly, QString::fromUtf8(iddetails.mNickname.c_str())) ; + else + painter.drawText(current_width+cellx, current_oy+celly, tr("[Unknown identity]")) ; + + mMaxWheelZoneY = oy+celly ; + + painter.setPen(QColor::fromRgb(0,0,0)) ; + + painter.setPen(QColor::fromRgb(127,127,127)); + painter.drawRect(ox+2*cellx,current_oy+0.15*celly,fm_monospace.width(ids)+cellx*matrix_info.friend_ids.size()- 2*cellx,celly) ; + + float total_length = (matrix_info.friend_ids.size()+2)*cellx ; + + if(!current_probs.empty()) + for(uint32_t i=0;igetPeerDetails(matrix_info.friend_ids[i], peer_ssl_details); + + painter.drawLine(x1,y1,x1,y2); + painter.drawLine(x1,y2,x1 + total_length - i*cellx,y2) ; + painter.drawText(cellx+ x1 + total_length - i*cellx,y2+(0.35)*celly, QString::fromUtf8(peer_ssl_details.name.c_str()) + " - " + QString::fromUtf8(peer_ssl_details.location.c_str()) + " ("+QString::number(current_probs[i])+")"); + } + oy += celly * (2+matrix_info.friend_ids.size()); + + oy += celly ; + oy += celly ; + + // update the pixmap + // + pixmap = tmppixmap; + maxHeight = oy ; +} +#endif + +void GxsIdStatisticsWidget::paintEvent(QPaintEvent */*event*/) +{ + QStylePainter(this).drawPixmap(0, 0, pixmap); +} + +void GxsIdStatisticsWidget::resizeEvent(QResizeEvent *event) +{ + QRect rect = geometry(); + + mMaxWidth = rect.width(); + mMaxHeight = rect.height() ; + + QWidget::resizeEvent(event); + updateContent(); +} diff --git a/retroshare-gui/src/gui/statistics/GxsIdStatistics.h b/retroshare-gui/src/gui/statistics/GxsIdStatistics.h new file mode 100644 index 000000000..fc0ab93fd --- /dev/null +++ b/retroshare-gui/src/gui/statistics/GxsIdStatistics.h @@ -0,0 +1,95 @@ +/******************************************************************************* + * gui/statistics/GxsIdStatistics.h * + * * + * Copyright (c) 2011 Retroshare Team * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#pragma once + +#include +#include +#include + +#include + +#include "Histogram.h" +#include "ui_GxsIdStatistics.h" + +// In this statistics panel we show: +// +// - histograms +// * age histogram of GXS ids (creation time) +// * last usage histogram +// * number of IDs used in each service as reported by UsageStatistics +// +// (note: we could use that histogram class for packets statistics, so we made a separate class) +// +// And general statistics: +// +// - total number of IDs +// - total number of signed IDs +// - total number of own IDs +// +class GxsIdStatisticsWidget ; + +class GxsIdStatistics: public RsAutoUpdatePage, public Ui::GxsIdStatistics +{ + Q_OBJECT + + public: + GxsIdStatistics(QWidget *parent = NULL) ; + ~GxsIdStatistics(); + + void updateContent() ; + + private: + + void processSettings(bool bLoad); + bool m_bProcessSettings; + + virtual void updateDisplay() ; + + GxsIdStatisticsWidget *_tst_CW ; +} ; + +class GxsIdStatisticsWidget: public QWidget +{ + Q_OBJECT + + public: + GxsIdStatisticsWidget(QWidget *parent = NULL) ; + + virtual void paintEvent(QPaintEvent *event) ; + virtual void resizeEvent(QResizeEvent *event); + + void updateContent() ; + void updateData(); + private: + static QString speedString(float f) ; + + QPixmap pixmap ; + int mMaxWidth,mMaxHeight ; + uint32_t mNbWeeks; + uint32_t mNbHours; + uint32_t mTotalIdentities; + Histogram mPublishDateHist ; + Histogram mLastUsedHist ; + + std::map mUsageMap; + std::map mPerServiceUsageMap; +}; + diff --git a/retroshare-gui/src/gui/statistics/GxsIdStatistics.ui b/retroshare-gui/src/gui/statistics/GxsIdStatistics.ui new file mode 100644 index 000000000..0c42ab780 --- /dev/null +++ b/retroshare-gui/src/gui/statistics/GxsIdStatistics.ui @@ -0,0 +1,49 @@ + + + GxsIdStatistics + + + + 0 + 0 + 1468 + 659 + + + + Router Statistics + + + + + + Qt::Vertical + + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + true + + + + + 0 + 0 + 1450 + 641 + + + + + + + + + + + diff --git a/retroshare-gui/src/gui/statistics/GxsTransportStatistics.cpp b/retroshare-gui/src/gui/statistics/GxsTransportStatistics.cpp index b5a42264e..c258cb4bc 100644 --- a/retroshare-gui/src/gui/statistics/GxsTransportStatistics.cpp +++ b/retroshare-gui/src/gui/statistics/GxsTransportStatistics.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -45,9 +46,13 @@ #include "util/QtVersion.h" #include "gui/common/UIStateHelper.h" #include "util/misc.h" +#include "util/qtthreadsutils.h" #include "gui/gxs/GxsIdLabel.h" #include "gui/gxs/GxsIdDetails.h" #include "gui/gxs/GxsIdTreeWidgetItem.h" +#include "gui/Identity/IdDialog.h" +#include "gui/MainWindow.h" +#include "gui/common/FilesDefs.h" #define COL_PENDING_ID 0 #define COL_PENDING_DESTINATION 1 @@ -60,11 +65,13 @@ #define COL_PENDING_DESTINATION_ID 8 #define COL_GROUP_GRP_ID 0 -#define COL_GROUP_NUM_MSGS 1 -#define COL_GROUP_SIZE_MSGS 2 -#define COL_GROUP_SUBSCRIBED 3 -#define COL_GROUP_POPULARITY 4 -#define COL_GROUP_UNIQUE_ID 5 +#define COL_GROUP_PUBLISHTS 1 +#define COL_GROUP_NUM_MSGS 2 +#define COL_GROUP_SIZE_MSGS 3 +#define COL_GROUP_SUBSCRIBED 4 +#define COL_GROUP_POPULARITY 5 +#define COL_GROUP_UNIQUE_ID 6 +#define COL_GROUP_AUTHOR_ID 7 //static const int PARTIAL_VIEW_SIZE = 9 ; //static const int MAX_TUNNEL_REQUESTS_DISPLAY = 10 ; @@ -78,15 +85,13 @@ //#define DEBUG_GXSTRANS_STATS 1 GxsTransportStatistics::GxsTransportStatistics(QWidget *parent) - : RsGxsUpdateBroadcastPage(rsGxsTrans,parent) + : MainPage(parent) { setupUi(this) ; mStateHelper = new UIStateHelper(this); mStateHelper->addWidget(GXSTRANS_GROUP_META, treeWidget); - mTransQueue = new TokenQueue(rsGxsTrans->getTokenService(), this); - m_bProcessSettings = false; mLastGroupReqTS = 0 ; @@ -95,8 +100,10 @@ GxsTransportStatistics::GxsTransportStatistics(QWidget *parent) QHeaderView_setSectionResizeMode(groupTreeWidget->header(), QHeaderView::ResizeToContents); connect(treeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(CustomPopupMenu(QPoint))); - - treeWidget->setColumnHidden(COL_PENDING_DESTINATION_ID,true); + connect(groupTreeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(CustomPopupMenuGroups(QPoint))); + + treeWidget->setColumnHidden(COL_PENDING_DESTINATION_ID,true); + groupTreeWidget->setColumnHidden(COL_GROUP_AUTHOR_ID,true); // load settings processSettings(true); @@ -140,7 +147,20 @@ void GxsTransportStatistics::CustomPopupMenu( QPoint ) QTreeWidgetItem *item = treeWidget->currentItem(); if (item) { - contextMnu.addAction(QIcon(":/images/info16.png"), tr("Details"), this, SLOT(personDetails())); + contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/info16.png"), tr("View details"), this, SLOT(personDetails())); + + } + + contextMnu.exec(QCursor::pos()); +} + +void GxsTransportStatistics::CustomPopupMenuGroups( QPoint ) +{ + QMenu contextMnu( this ); + + QTreeWidgetItem *item = groupTreeWidget->currentItem(); + if (item) { + contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/info16.png"), tr("View details"), this, SLOT(showAuthorInPeople())); } @@ -154,7 +174,8 @@ void GxsTransportStatistics::updateDisplay(bool) std::cerr << "GxsTransportStatistics::updateDisplay()" << std::endl; #endif - requestGroupMeta(); + loadGroups(); + mLastGroupReqTS = now ; } @@ -201,7 +222,7 @@ void GxsTransportStatistics::updateContent() { RsGxsTrans::GxsTransStatistics transinfo ; - rsGxsTrans->getStatistics(transinfo) ; + rsGxsTrans->getDataStatistics(transinfo) ; // clear @@ -275,9 +296,10 @@ void GxsTransportStatistics::updateContent() groupTreeWidget->addTopLevelItem(item); groupTreeWidget->setItemExpanded(item,openned_groups.find(it->first) != openned_groups.end()); - QString msg_time_string = (stat.last_publish_TS>0)?QString(" (Last msg: %1)").arg(QDateTime::fromTime_t((uint)stat.last_publish_TS).toString()):"" ; + QString msg_time_string = (stat.last_publish_TS>0)?QString("(Last msg: %1)").arg(QDateTime::fromTime_t((uint)stat.last_publish_TS).toString()):"" ; - item->setData(COL_GROUP_NUM_MSGS, Qt::DisplayRole, QString::number(stat.mNumMsgs) + msg_time_string) ; + item->setData(COL_GROUP_PUBLISHTS, Qt::DisplayRole, msg_time_string) ; + item->setData(COL_GROUP_NUM_MSGS, Qt::DisplayRole, QString::number(stat.mNumMsgs) ) ; item->setData(COL_GROUP_GRP_ID, Qt::DisplayRole, QString::fromStdString(it->first.toStdString())) ; item->setData(COL_GROUP_SIZE_MSGS, Qt::DisplayRole, QString::number(stat.mTotalSizeOfMsgs)) ; item->setData(COL_GROUP_SUBSCRIBED,Qt::DisplayRole, stat.subscribed?tr("Yes"):tr("No")) ; @@ -308,6 +330,8 @@ void GxsTransportStatistics::updateContent() rsIdentity->getIdDetails(meta.mAuthorId,idDetails); QPixmap pixmap ; + QDateTime qdatetime; + qdatetime.setTime_t(meta.mPublishTs); if(idDetails.mAvatar.mSize == 0 || !GxsIdDetails::loadPixmapFromData(idDetails.mAvatar.mData, idDetails.mAvatar.mSize, pixmap,GxsIdDetails::SMALL)) pixmap = GxsIdDetails::makeDefaultIcon(meta.mAuthorId,GxsIdDetails::SMALL); @@ -315,7 +339,9 @@ void GxsTransportStatistics::updateContent() sitem->setIcon(COL_GROUP_GRP_ID, QIcon(pixmap)); sitem->setData(COL_GROUP_UNIQUE_ID, Qt::DisplayRole,QString::fromStdString(meta.mMsgId.toStdString())); - sitem->setData(COL_GROUP_NUM_MSGS,Qt::DisplayRole, QDateTime::fromTime_t(meta.mPublishTs).toString()); + sitem->setData(COL_GROUP_AUTHOR_ID, Qt::DisplayRole, QString::fromStdString(meta.mAuthorId.toStdString())) ; + sitem->setText(COL_GROUP_PUBLISHTS, QDateTime::fromTime_t(meta.mPublishTs).toString()); + sitem->setData(COL_GROUP_PUBLISHTS, Qt::UserRole, qdatetime); } } } @@ -333,92 +359,27 @@ void GxsTransportStatistics::personDetails() dialog->show(); } -void GxsTransportStatistics::loadRequest(const TokenQueue *queue, const TokenRequest &req) +void GxsTransportStatistics::showAuthorInPeople() { -#ifdef DEBUG_GXSTRANS_STATS - std::cerr << "GxsTransportStatistics::loadRequest() UserType: " << req.mUserType << std::endl; -#endif + QTreeWidgetItem *item = groupTreeWidget->currentItem(); + std::string id = item->text(COL_GROUP_AUTHOR_ID).toStdString(); - if (queue != mTransQueue) - { - std::cerr << "Wrong queue!" << std::endl; + if (id.empty()) { + return; + } + + /* window will destroy itself! */ + IdDialog *idDialog = dynamic_cast(MainWindow::getPage(MainWindow::People)); + + if (!idDialog) return ; - } - /* now switch on req */ - switch(req.mUserType) - { - case GXSTRANS_GROUP_META: loadGroupMeta(req.mToken); - break; - - case GXSTRANS_GROUP_STAT: loadGroupStat(req.mToken); - break; - - case GXSTRANS_MSG_META: loadMsgMeta(req.mToken); - break; - - default: - std::cerr << "GxsTransportStatistics::loadRequest() ERROR: INVALID TYPE"; - std::cerr << std::endl; - break; - } - - updateContent(); + MainWindow::showWindow(MainWindow::People); + idDialog->navigate(RsGxsId(id)); } -void GxsTransportStatistics::requestGroupMeta() -{ - mStateHelper->setLoading(GXSTRANS_GROUP_META, true); - -#ifdef DEBUG_GXSTRANS_STATS - std::cerr << "GxsTransportStatisticsWidget::requestGroupMeta()"; - std::cerr << std::endl; -#endif - - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_GROUP_META; - - uint32_t token; - mTransQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_SUMMARY, opts, GXSTRANS_GROUP_META); -} -void GxsTransportStatistics::requestGroupStat(const RsGxsGroupId &groupId) -{ - uint32_t token; - rsGxsTrans->getTokenService()->requestGroupStatistic(token, groupId); - mTransQueue->queueRequest(token, 0, RS_TOKREQ_ANSTYPE_ACK, GXSTRANS_GROUP_STAT); -} -void GxsTransportStatistics::requestMsgMeta(const RsGxsGroupId& grpId) -{ - mStateHelper->setLoading(GXSTRANS_MSG_META, true); - -#ifdef DEBUG_GXSTRANS_STATS - std::cerr << "GxsTransportStatisticsWidget::requestGroupMeta()"; - std::cerr << std::endl; -#endif - - RsTokReqOptions opts; - opts.mReqType = GXS_REQUEST_TYPE_MSG_META; - - std::list grouplist ; - grouplist.push_back(grpId) ; - - uint32_t token; - rsGxsTrans->getTokenService()->requestMsgInfo(token, RS_TOKREQ_ANSTYPE_SUMMARY, opts, grouplist); - mTransQueue->queueRequest(token, 0, RS_TOKREQ_ANSTYPE_ACK, GXSTRANS_MSG_META); -} - -void GxsTransportStatistics::loadGroupStat(const uint32_t &token) -{ - GxsGroupStatistic stats; - rsGxsTrans->getGroupStatistic(token, stats); - -#ifdef DEBUG_GXSTRANS_STATS - std::cerr << "Loading group stats: " << stats.mGrpId << ", num msgs=" << stats.mNumMsgs << ", total size=" << stats.mTotalSizeOfMsgs << std::endl; -#endif - dynamic_cast(mGroupStats[stats.mGrpId]) = stats ; -} - -void GxsTransportStatistics::loadGroupMeta(const uint32_t& token) +#ifdef TO_REMOVE +void GxsTransportStatistics::loadGroupMeta(const std::vector& groupInfo) { mStateHelper->setLoading(GXSTRANS_GROUP_META, false); @@ -427,22 +388,11 @@ void GxsTransportStatistics::loadGroupMeta(const uint32_t& token) std::cerr << std::endl; #endif - std::list groupInfo; - std::list::iterator vit; - - if (!rsGxsTrans->getGroupSummary(token,groupInfo)) - { - std::cerr << "GxsTransportStatistics::loadGroupMeta() Error getting GroupMeta"; - std::cerr << std::endl; - mStateHelper->setActive(GXSTRANS_GROUP_META, false); - return; - } - mStateHelper->setActive(GXSTRANS_GROUP_META, true); std::set existing_groups ; - for(vit = groupInfo.begin(); vit != groupInfo.end(); ++vit) + for(auto vit = groupInfo.begin(); vit != groupInfo.end(); ++vit) { existing_groups.insert(vit->mGroupId) ; @@ -451,8 +401,8 @@ void GxsTransportStatistics::loadGroupMeta(const uint32_t& token) std::cerr << "GxsTransportStatisticsWidget::loadGroupMeta() GroupId: " << vit->mGroupId << " Group: " << vit->mGroupName << std::endl; #endif - requestGroupStat(vit->mGroupId) ; - requestMsgMeta(vit->mGroupId) ; + loadGroupStats(vit->mGroupId) ; + loadMsgMetas(vit->mGroupId) ; RsGxsTransGroupStatistics& s(mGroupStats[vit->mGroupId]); s.popularity = vit->mPop ; @@ -469,17 +419,75 @@ void GxsTransportStatistics::loadGroupMeta(const uint32_t& token) ++it; } -void GxsTransportStatistics::loadMsgMeta(const uint32_t& token) +void GxsTransportStatistics::loadGroupStats(const RsGxsGroupId& groupId) { - mStateHelper->setLoading(GXSTRANS_MSG_META, false); +#ifdef DEBUG_GXSTRANS_STATS + std::cerr << "Loading group stats: " << stats.mGrpId << ", num msgs=" << stats.mNumMsgs << ", total size=" << stats.mTotalSizeOfMsgs << std::endl; +#endif - GxsMsgMetaMap m ; + RsThread::async([this]() + { + // 1 - get message data from p3GxsForums - if (!rsGxsTrans->getMsgSummary(token,m)) - return ; +#ifdef DEBUG_FORUMS + std::cerr << "Retrieving post data for post " << mThreadId << std::endl; +#endif + GxsGroupStatistic stats; + rsGxsTrans->getGroupStatistic(groupId,stats); - for(GxsMsgMetaMap::const_iterator it(m.begin());it!=m.end();++it) - for(uint32_t i=0;isecond.size();++i) - mGroupStats[it->first].addMessageMeta(it->first,it->second[i]) ; + RsQThreadUtils::postToObject( [stats,this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ + + dynamic_cast(mGroupStats[stats.mGrpId]) = stats ; + + mStateHelper->setLoading(GXSTRANS_GROUP_STAT, false); + + }, this ); + + }); + +} +#endif + + +void GxsTransportStatistics::loadGroups() +{ + mStateHelper->setLoading(GXSTRANS_GROUP_META, true); + + RsThread::async([this]() + { + // 1 - get message data from p3GxsForums + +#ifdef DEBUG_FORUMS + std::cerr << "Retrieving post data for post " << mThreadId << std::endl; +#endif + auto stats = std::make_unique< + std::map >(); + + if(!rsGxsTrans->getGroupStatistics(*stats)) + { + RS_ERR("Cannot retrieve group statistics in GxsTransportStatistics"); + return; + } + + RsQThreadUtils::postToObject( + [stats = std::move(stats), this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ + + // TODO: consider making mGroupStats an unique_ptr to avoid copying + mGroupStats = *stats; + updateContent(); + mStateHelper->setLoading(GXSTRANS_GROUP_META, false); + + }, this ); + + }); } + diff --git a/retroshare-gui/src/gui/statistics/GxsTransportStatistics.h b/retroshare-gui/src/gui/statistics/GxsTransportStatistics.h index a1591ad16..0b9970c1c 100644 --- a/retroshare-gui/src/gui/statistics/GxsTransportStatistics.h +++ b/retroshare-gui/src/gui/statistics/GxsTransportStatistics.h @@ -25,9 +25,10 @@ #include #include #include +#include + +#include -#include "util/TokenQueue.h" -#include "RsAutoUpdatePage.h" #include "ui_GxsTransportStatistics.h" #include "gui/gxs/RsGxsUpdateBroadcastPage.h" #include "util/rstime.h" @@ -35,32 +36,7 @@ class GxsTransportStatisticsWidget ; class UIStateHelper; -class RsGxsTransGroupStatistics: public GxsGroupStatistic -{ -public: - RsGxsTransGroupStatistics() - { - last_publish_TS = 0; - popularity = 0; - subscribed = false; - } - - void addMessageMeta(const RsGxsGroupId& grp,const RsMsgMetaData& meta) - { - messages_metas[meta.mMsgId] = meta ; - last_publish_TS = std::max(last_publish_TS,meta.mPublishTs) ; - mGrpId = grp ; - } - - bool subscribed ; - int popularity ; - - rstime_t last_publish_TS; - - std::map messages_metas ; -}; - -class GxsTransportStatistics: public RsGxsUpdateBroadcastPage, public TokenResponse, public Ui::GxsTransportStatistics +class GxsTransportStatistics: public MainPage, public Ui::GxsTransportStatistics { Q_OBJECT @@ -71,30 +47,24 @@ public: // Cache for peer names. static QString getPeerName(const RsPeerId& peer_id) ; - virtual void loadRequest(const TokenQueue *queue, const TokenRequest &req) ; - void updateContent() ; private slots: /** Create the context popup menu and it's submenus */ void CustomPopupMenu( QPoint point ); + void CustomPopupMenuGroups( QPoint point ) ; + void personDetails(); + void showAuthorInPeople(); private: void updateDisplay(bool complete) ; - void loadGroupMeta(const uint32_t& token); - void loadGroupStat(const uint32_t& token); - void loadMsgMeta(const uint32_t& token); - - void requestGroupMeta(); - void requestMsgMeta(const RsGxsGroupId& groupId); - void requestGroupStat(const RsGxsGroupId &groupId); + void loadGroups(); void processSettings(bool bLoad); bool m_bProcessSettings; GxsTransportStatisticsWidget *_tst_CW ; - TokenQueue *mTransQueue ; UIStateHelper *mStateHelper; uint32_t mLastGroupReqTS ; diff --git a/retroshare-gui/src/gui/statistics/GxsTransportStatistics.ui b/retroshare-gui/src/gui/statistics/GxsTransportStatistics.ui index e2c7449c0..8ce780e81 100644 --- a/retroshare-gui/src/gui/statistics/GxsTransportStatistics.ui +++ b/retroshare-gui/src/gui/statistics/GxsTransportStatistics.ui @@ -41,7 +41,7 @@ true - false + true @@ -108,6 +108,12 @@ Qt::CustomContextMenu + + true + + + true + Group ID / Author @@ -115,7 +121,12 @@ - Number of messages / Publish TS + Publish TS + + + + + Number of messages diff --git a/retroshare-gui/src/gui/statistics/Histogram.cpp b/retroshare-gui/src/gui/statistics/Histogram.cpp new file mode 100644 index 000000000..6cd1c4906 --- /dev/null +++ b/retroshare-gui/src/gui/statistics/Histogram.cpp @@ -0,0 +1,55 @@ +/******************************************************************************* + * gui/statistics/Histogram.cpp * + * * + * Copyright (c) 2020 Retroshare Team * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include + +#include "Histogram.h" + +Histogram::Histogram() + : mStart(0),mEnd(1.0),mBins(10,0) +{} + +Histogram::Histogram(double start, double end, int bins) + : mStart(start),mEnd(end),mBins(bins,0) +{ + if(mEnd <= mStart) + std::cerr << "Null histogram created! Please check your parameters" << std::endl; +} + +void Histogram::draw(QPainter *painter) const +{ +} + +void Histogram::insert(double val) +{ + long int bin = (uint32_t)floor((val - mStart)/(mEnd - mStart) * mBins.size()); + + if(bin >= 0 && bin < mBins.size()) + ++mBins[bin]; +} + +std::ostream& operator<<(std::ostream& o,const Histogram& h) +{ + o << "Histogram: [" << h.mStart << "..." << h.mEnd << "] " << h.mBins.size() << " bins." << std::endl; + for(uint32_t i=0;i * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include +#include + +class QPainter; + +class Histogram +{ + public: + Histogram(); + Histogram(double start, double end, int bins); + + void draw(QPainter *painter) const ; + void insert(double val); + + const std::vector& entries() const { return mBins; } + + private: + double mStart; + double mEnd; + + std::vector mBins; + + friend std::ostream& operator<<(std::ostream& o,const Histogram& h); +}; + diff --git a/retroshare-gui/src/gui/statistics/RttStatistics.h b/retroshare-gui/src/gui/statistics/RttStatistics.h index 2cefe3bb7..32c8b4f1b 100644 --- a/retroshare-gui/src/gui/statistics/RttStatistics.h +++ b/retroshare-gui/src/gui/statistics/RttStatistics.h @@ -22,8 +22,9 @@ #include #include +#include + #include "ui_RttStatistics.h" -#include "RsAutoUpdatePage.h" #include class RttStatisticsWidget ; diff --git a/retroshare-gui/src/gui/statistics/StatisticsWindow.cpp b/retroshare-gui/src/gui/statistics/StatisticsWindow.cpp index bbda5101a..3d3868135 100644 --- a/retroshare-gui/src/gui/statistics/StatisticsWindow.cpp +++ b/retroshare-gui/src/gui/statistics/StatisticsWindow.cpp @@ -34,9 +34,11 @@ #include #include +#include "gui/common/FilesDefs.h" #include #include +#include #include #include #include @@ -52,6 +54,7 @@ #define IMAGE_DHT ":/icons/DHT128.png" #define IMAGE_TURTLE ":/icons/turtle128.png" +#define IMAGE_IDENTITIES ":/icons/avatar_128.png" #define IMAGE_BWGRAPH ":/icons/bandwidth128.png" #define IMAGE_GLOBALROUTER ":/icons/GRouter128.png" #define IMAGE_GXSTRANSPORT ":/icons/transport128.png" @@ -138,19 +141,22 @@ void StatisticsWindow::initStackedPage() QAction *action; ui->stackPages->add(bwdlg = new BwCtrlWindow(ui->stackPages), - action = createPageAction(QIcon(IMAGE_BWGRAPH), tr("Bandwidth"), grp)); + action = createPageAction(FilesDefs::getIconFromQtResourcePath(IMAGE_BWGRAPH), tr("Bandwidth"), grp)); ui->stackPages->add(trsdlg = new TurtleRouterStatistics(ui->stackPages), - action = createPageAction(QIcon(IMAGE_TURTLE), tr("Turtle Router"), grp)); + action = createPageAction(FilesDefs::getIconFromQtResourcePath(IMAGE_TURTLE), tr("Turtle Router"), grp)); + ui->stackPages->add(gxsiddlg = new GxsIdStatistics(ui->stackPages), + action = createPageAction(FilesDefs::getIconFromQtResourcePath(IMAGE_IDENTITIES), tr("Identities"), grp)); + ui->stackPages->add(grsdlg = new GlobalRouterStatistics(ui->stackPages), - action = createPageAction(QIcon(IMAGE_GLOBALROUTER), tr("Global Router"), grp)); + action = createPageAction(FilesDefs::getIconFromQtResourcePath(IMAGE_GLOBALROUTER), tr("Global Router"), grp)); ui->stackPages->add(gxsdlg = new GxsTransportStatistics(ui->stackPages), - action = createPageAction(QIcon(IMAGE_GXSTRANSPORT), tr("Gxs Transport"), grp)); + action = createPageAction(FilesDefs::getIconFromQtResourcePath(IMAGE_GXSTRANSPORT), tr("Gxs Transport"), grp)); ui->stackPages->add(rttdlg = new RttStatistics(ui->stackPages), - action = createPageAction(QIcon(IMAGE_RTT), tr("RTT Statistics"), grp)); + action = createPageAction(FilesDefs::getIconFromQtResourcePath(IMAGE_RTT), tr("RTT Statistics"), grp)); bool showdht = true; RsPeerDetails detail; @@ -162,7 +168,7 @@ void StatisticsWindow::initStackedPage() if(showdht) { ui->stackPages->add(dhtw = new DhtWindow(ui->stackPages), - action = createPageAction(QIcon(IMAGE_DHT), tr("DHT"), grp)); + action = createPageAction(FilesDefs::getIconFromQtResourcePath(IMAGE_DHT), tr("DHT"), grp)); } /*std::cerr << "Looking for interfaces in existing plugins:" << std::endl; @@ -175,7 +181,7 @@ void StatisticsWindow::initStackedPage() if(rsPlugins->plugin(i)->qt_icon() != NULL) icon = *rsPlugins->plugin(i)->qt_icon() ; else - icon = QIcon(":images/extension_48.png") ; + icon = FilesDefs::getIconFromQtResourcePath(":images/extension_48.png") ; std::cerr << " Addign widget page for plugin " << rsPlugins->plugin(i)->getPluginName() << std::endl; MainPage *pluginPage = rsPlugins->plugin(i)->qt_page(); diff --git a/retroshare-gui/src/gui/statistics/StatisticsWindow.h b/retroshare-gui/src/gui/statistics/StatisticsWindow.h index db2d75939..2450812d7 100644 --- a/retroshare-gui/src/gui/statistics/StatisticsWindow.h +++ b/retroshare-gui/src/gui/statistics/StatisticsWindow.h @@ -38,6 +38,7 @@ class TurtleRouterStatistics; class GlobalRouterStatistics; class GxsTransportStatistics; class RttStatistics; +class GxsIdStatistics; class StatisticsWindow : public QMainWindow { Q_OBJECT @@ -57,6 +58,7 @@ public: BwCtrlWindow *bwdlg; TurtleRouterStatistics *trsdlg; RttStatistics *rttdlg; + GxsIdStatistics *gxsiddlg; public slots: diff --git a/retroshare-gui/src/gui/statistics/TurtleRouterDialog.h b/retroshare-gui/src/gui/statistics/TurtleRouterDialog.h index 4fc00b004..94e1ce269 100644 --- a/retroshare-gui/src/gui/statistics/TurtleRouterDialog.h +++ b/retroshare-gui/src/gui/statistics/TurtleRouterDialog.h @@ -22,9 +22,11 @@ #include #include + +#include + #include "ui_TurtleRouterDialog.h" #include "ui_TurtleRouterStatistics.h" -#include "RsAutoUpdatePage.h" class TurtleRouterDialog: public RsAutoUpdatePage, public Ui::TurtleRouterDialogForm diff --git a/retroshare-gui/src/gui/statistics/TurtleRouterStatistics.h b/retroshare-gui/src/gui/statistics/TurtleRouterStatistics.h index f7613de94..7cba37c18 100644 --- a/retroshare-gui/src/gui/statistics/TurtleRouterStatistics.h +++ b/retroshare-gui/src/gui/statistics/TurtleRouterStatistics.h @@ -23,8 +23,10 @@ #include #include #include + +#include + #include "ui_TurtleRouterStatistics.h" -#include "RsAutoUpdatePage.h" class TurtleRouterStatisticsWidget ; diff --git a/retroshare-gui/src/gui/statusbar/SoundStatus.cpp b/retroshare-gui/src/gui/statusbar/SoundStatus.cpp index 531133008..374a61b12 100644 --- a/retroshare-gui/src/gui/statusbar/SoundStatus.cpp +++ b/retroshare-gui/src/gui/statusbar/SoundStatus.cpp @@ -18,6 +18,7 @@ * * *******************************************************************************/ +#include "gui/common/FilesDefs.h" #include #include @@ -54,6 +55,6 @@ SoundStatus::SoundStatus(QWidget *parent) void SoundStatus::mute(bool isMute) { - imageButton->setIcon(QIcon(isMute ? IMAGE_MUTE_ON : IMAGE_MUTE_OFF)); + imageButton->setIcon(FilesDefs::getIconFromQtResourcePath(isMute ? IMAGE_MUTE_ON : IMAGE_MUTE_OFF)); imageButton->setToolTip(isMute ? tr("Sound is off, click to turn it on") : tr("Sound is on, click to turn it off")); } diff --git a/retroshare-gui/src/gui/statusbar/SysTrayStatus.cpp b/retroshare-gui/src/gui/statusbar/SysTrayStatus.cpp index b465e83e3..1f52163ec 100644 --- a/retroshare-gui/src/gui/statusbar/SysTrayStatus.cpp +++ b/retroshare-gui/src/gui/statusbar/SysTrayStatus.cpp @@ -18,6 +18,7 @@ * * *******************************************************************************/ +#include "gui/common/FilesDefs.h" #include #include #include @@ -36,7 +37,7 @@ SysTrayStatus::SysTrayStatus(QWidget *parent) : hbox->setSpacing(0); imageButton = new QPushButton(this); - imageButton->setIcon(QIcon(IMAGE_NOONLINE)); + imageButton->setIcon(FilesDefs::getIconFromQtResourcePath(IMAGE_NOONLINE)); imageButton->setFlat(true); imageButton->setCheckable(false); imageButton->setFocusPolicy(Qt::ClickFocus); diff --git a/retroshare-gui/src/gui/statusbar/ToasterDisable.cpp b/retroshare-gui/src/gui/statusbar/ToasterDisable.cpp index f59c5e29e..273245972 100644 --- a/retroshare-gui/src/gui/statusbar/ToasterDisable.cpp +++ b/retroshare-gui/src/gui/statusbar/ToasterDisable.cpp @@ -23,6 +23,7 @@ #include "ToasterDisable.h" #include "gui/notifyqt.h" +#include "gui/common/FilesDefs.h" #define IMAGE_TOASTERDISABLE ":/images/toasterDisable.png" #define IMAGE_TOASTERENABLE ":/images/toasterEnable.png" @@ -57,7 +58,7 @@ ToasterDisable::ToasterDisable(QWidget *parent) void ToasterDisable::disable(bool isDisable) { - imageButton->setIcon(QPixmap(isDisable ? IMAGE_TOASTERDISABLE : IMAGE_TOASTERENABLE)); + imageButton->setIcon(FilesDefs::getPixmapFromQtResourcePath(isDisable ? IMAGE_TOASTERDISABLE : IMAGE_TOASTERENABLE)); imageButton->setToolTip(isDisable ? tr("All Toasters are disabled") : tr("Toasters are enabled")); imageButton->setChecked(isDisable); } diff --git a/retroshare-gui/src/gui/statusbar/dhtstatus.cpp b/retroshare-gui/src/gui/statusbar/dhtstatus.cpp index 209399157..9d998137f 100644 --- a/retroshare-gui/src/gui/statusbar/dhtstatus.cpp +++ b/retroshare-gui/src/gui/statusbar/dhtstatus.cpp @@ -27,6 +27,7 @@ #include "retroshare/rsconfig.h" #include "retroshare/rspeers.h" +#include "gui/common/FilesDefs.h" #include "util/misc.h" @@ -46,7 +47,7 @@ DHTStatus::DHTStatus(QWidget *parent) hbox->addWidget(statusDHT); dhtstatusLabel = new QLabel( this ); - dhtstatusLabel->setPixmap(QPixmap(":/icons/bullet_grey_128.png")); + dhtstatusLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/bullet_grey_128.png")); hbox->addWidget(dhtstatusLabel); spaceLabel = new QLabel( "|", this ); @@ -56,7 +57,7 @@ DHTStatus::DHTStatus(QWidget *parent) dhtnetworkLabel = new QLabel( this ); dhtnetworkLabel->setVisible(false); int S = QFontMetricsF(dhtnetworkLabel->font()).height(); - dhtnetworkLabel->setPixmap(QPixmap(":/images/dht32.png").scaledToHeight(S,Qt::SmoothTransformation)); + dhtnetworkLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/dht32.png").scaledToHeight(S,Qt::SmoothTransformation)); hbox->addWidget(dhtnetworkLabel); dhtnetworksizeLabel = new QLabel( "0 (0) ",this ); @@ -84,7 +85,7 @@ void DHTStatus::getDHTStatus() if (!(config.DHTActive)) { // GRAY. - dhtstatusLabel->setPixmap(QPixmap(":/icons/bullet_grey_128.png").scaledToHeight(S,Qt::SmoothTransformation)); + dhtstatusLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/bullet_grey_128.png").scaledToHeight(S,Qt::SmoothTransformation)); dhtstatusLabel->setToolTip( text + tr("DHT Off")); spaceLabel->setVisible(false); @@ -102,7 +103,7 @@ void DHTStatus::getDHTStatus() // YELLOW or GREEN. if (config.netDhtRsNetSize < MIN_RS_NET_SIZE) { - dhtstatusLabel->setPixmap(QPixmap(":/icons/bullet_yellow_128.png").scaledToHeight(S,Qt::SmoothTransformation)); + dhtstatusLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/bullet_yellow_128.png").scaledToHeight(S,Qt::SmoothTransformation)); dhtstatusLabel->setToolTip( text + tr("DHT Searching for RetroShare Peers")); spaceLabel->setVisible(true); @@ -114,7 +115,7 @@ void DHTStatus::getDHTStatus() } else { - dhtstatusLabel->setPixmap(QPixmap(":/icons/bullet_green_128.png").scaledToHeight(S,Qt::SmoothTransformation)); + dhtstatusLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/bullet_green_128.png").scaledToHeight(S,Qt::SmoothTransformation)); dhtstatusLabel->setToolTip( text + tr("DHT Good")); spaceLabel->setVisible(true); @@ -128,7 +129,7 @@ void DHTStatus::getDHTStatus() else { // RED - some issue. - dhtstatusLabel->setPixmap(QPixmap(":/icons/bullet_red_128.png").scaledToHeight(S,Qt::SmoothTransformation)); + dhtstatusLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/bullet_red_128.png").scaledToHeight(S,Qt::SmoothTransformation)); dhtstatusLabel->setToolTip( text + tr("No peer found in DHT")); spaceLabel->setVisible(false); diff --git a/retroshare-gui/src/gui/statusbar/discstatus.cpp b/retroshare-gui/src/gui/statusbar/discstatus.cpp index 71f59d041..64ac7813e 100644 --- a/retroshare-gui/src/gui/statusbar/discstatus.cpp +++ b/retroshare-gui/src/gui/statusbar/discstatus.cpp @@ -23,6 +23,7 @@ #include "discstatus.h" #include "gui/settings/rsharesettings.h" +#include "gui/common/FilesDefs.h" #include @@ -36,7 +37,7 @@ DiscStatus::DiscStatus(QWidget *parent) hbox->setSpacing(6); QLabel *iconLabel = new QLabel(this); - iconLabel->setPixmap(QPixmap(":/images/uploads.png")); + iconLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/uploads.png")); iconLabel->setToolTip(tr("Waiting outgoing discovery operations")); hbox->addWidget(iconLabel); @@ -45,7 +46,7 @@ DiscStatus::DiscStatus(QWidget *parent) hbox->addWidget(sendLabel); iconLabel = new QLabel(this); - iconLabel->setPixmap(QPixmap(":/images/download.png")); + iconLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/download.png")); iconLabel->setToolTip(tr("Waiting incoming discovery operations")); hbox->addWidget(iconLabel); diff --git a/retroshare-gui/src/gui/statusbar/hashingstatus.cpp b/retroshare-gui/src/gui/statusbar/hashingstatus.cpp index 997ca5f8f..6d85935f1 100644 --- a/retroshare-gui/src/gui/statusbar/hashingstatus.cpp +++ b/retroshare-gui/src/gui/statusbar/hashingstatus.cpp @@ -27,6 +27,7 @@ #include "hashingstatus.h" #include "gui/common/ElidedLabel.h" #include "gui/notifyqt.h" +#include "gui/common/FilesDefs.h" HashingStatus::HashingStatus(QWidget *parent) : QWidget(parent) @@ -52,8 +53,10 @@ HashingStatus::HashingStatus(QWidget *parent) hashloader->hide(); statusHashing->hide(); - mEventHandlerId=0; - rsEvents->registerEventsHandler(RsEventType::SHARED_DIRECTORIES, [this](std::shared_ptr event) { handleEvent(event); }, mEventHandlerId ); + mEventHandlerId=0; + rsEvents->registerEventsHandler( + [this](std::shared_ptr event) { handleEvent(event); }, + mEventHandlerId, RsEventType::SHARED_DIRECTORIES ); } void HashingStatus::handleEvent(std::shared_ptr event) @@ -122,7 +125,7 @@ void HashingStatus::mousePressEvent(QMouseEvent *) if(rsFiles->hashingProcessPaused()) { movie->stop() ; - hashloader->setPixmap(QPixmap(":/images/resume.png")) ; + hashloader->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/resume.png")) ; mLastText = statusHashing->text(); statusHashing->setText(QObject::tr("[Hashing is paused]")); diff --git a/retroshare-gui/src/gui/statusbar/natstatus.cpp b/retroshare-gui/src/gui/statusbar/natstatus.cpp index b8e4a7d7a..15e2a7067 100644 --- a/retroshare-gui/src/gui/statusbar/natstatus.cpp +++ b/retroshare-gui/src/gui/statusbar/natstatus.cpp @@ -25,6 +25,7 @@ #include "retroshare/rsiface.h" #include "retroshare/rsconfig.h" +#include "gui/common/FilesDefs.h" NATStatus::NATStatus(QWidget *parent) : QWidget(parent) @@ -38,7 +39,7 @@ NATStatus::NATStatus(QWidget *parent) hbox->addWidget(statusNAT); iconLabel = new QLabel(this); - iconLabel->setPixmap(QPixmap(":/images/grayled.png")); + iconLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/grayled.png")); // iconLabel doesn't change over time, so we didn't need a minimum size hbox->addWidget(iconLabel); @@ -62,14 +63,14 @@ void NATStatus::getNATStatus() default: case RsNetState::BAD_UNKNOWN: { - iconLabel->setPixmap(QPixmap(":/icons/bullet_yellow_128.png").scaledToHeight(S,Qt::SmoothTransformation)) ; + iconLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/bullet_yellow_128.png").scaledToHeight(S,Qt::SmoothTransformation)) ; iconLabel->setToolTip( text + tr("Network Status Unknown")) ; } break ; case RsNetState::BAD_OFFLINE: { - iconLabel->setPixmap(QPixmap(":/icons/bullet_grey_129.png").scaledToHeight(S,Qt::SmoothTransformation)) ; + iconLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/bullet_grey_129.png").scaledToHeight(S,Qt::SmoothTransformation)) ; iconLabel->setToolTip( text + tr("Offline")) ; } break ; @@ -77,14 +78,14 @@ void NATStatus::getNATStatus() // BAD. (RED) case RsNetState::BAD_NATSYM: { - iconLabel->setPixmap(QPixmap(":/icons/bullet_red_128.png").scaledToHeight(S,Qt::SmoothTransformation)) ; + iconLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/bullet_red_128.png").scaledToHeight(S,Qt::SmoothTransformation)) ; iconLabel->setToolTip( text + tr("Nasty Firewall")) ; } break ; case RsNetState::BAD_NODHT_NAT: { - iconLabel->setPixmap(QPixmap(":/icons/bullet_red_128.png").scaledToHeight(S,Qt::SmoothTransformation)) ; + iconLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/bullet_red_128.png").scaledToHeight(S,Qt::SmoothTransformation)) ; iconLabel->setToolTip( text + tr("DHT Disabled and Firewalled")) ; } break ; @@ -92,21 +93,21 @@ void NATStatus::getNATStatus() // CAUTION. (ORANGE) case RsNetState::WARNING_RESTART: { - iconLabel->setPixmap(QPixmap(":/icons/bullet_yellow_128.png").scaledToHeight(S,Qt::SmoothTransformation)) ; + iconLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/bullet_yellow_128.png").scaledToHeight(S,Qt::SmoothTransformation)) ; iconLabel->setToolTip( text + tr("Network Restarting")) ; } break ; case RsNetState::WARNING_NATTED: { - iconLabel->setPixmap(QPixmap(":/icons/bullet_yellow_128.png").scaledToHeight(S,Qt::SmoothTransformation)) ; + iconLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/bullet_yellow_128.png").scaledToHeight(S,Qt::SmoothTransformation)) ; iconLabel->setToolTip( text + tr("Behind Firewall")) ; } break ; case RsNetState::WARNING_NODHT: { - iconLabel->setPixmap(QPixmap(":/icons/bullet_yellow_128.png").scaledToHeight(S,Qt::SmoothTransformation)) ; + iconLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/bullet_yellow_128.png").scaledToHeight(S,Qt::SmoothTransformation)) ; iconLabel->setToolTip( text + tr("DHT Disabled")) ; } break ; @@ -114,14 +115,14 @@ void NATStatus::getNATStatus() // GOOD (GREEN) case RsNetState::GOOD: { - iconLabel->setPixmap(QPixmap(":/icons/bullet_green_128.png").scaledToHeight(S,Qt::SmoothTransformation)) ; + iconLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/bullet_green_128.png").scaledToHeight(S,Qt::SmoothTransformation)) ; iconLabel->setToolTip( text + tr("RetroShare Server")) ; } break ; case RsNetState::ADV_FORWARD: { - iconLabel->setPixmap(QPixmap(":/icons/bullet_green_128.png").scaledToHeight(S,Qt::SmoothTransformation)) ; + iconLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/bullet_green_128.png").scaledToHeight(S,Qt::SmoothTransformation)) ; iconLabel->setToolTip( text + tr("Forwarded Port")) ; } break ; diff --git a/retroshare-gui/src/gui/statusbar/peerstatus.cpp b/retroshare-gui/src/gui/statusbar/peerstatus.cpp index 322109b05..4f1c9811f 100644 --- a/retroshare-gui/src/gui/statusbar/peerstatus.cpp +++ b/retroshare-gui/src/gui/statusbar/peerstatus.cpp @@ -22,6 +22,7 @@ #include #include "peerstatus.h" +#include "gui/common/FilesDefs.h" PeerStatus::PeerStatus(QWidget *parent) : QWidget(parent) @@ -33,7 +34,7 @@ PeerStatus::PeerStatus(QWidget *parent) iconLabel = new QLabel( this ); int S = QFontMetricsF(iconLabel->font()).height(); - iconLabel->setPixmap(QPixmap(":/icons/avatar_grey_128.png").scaledToHeight(S,Qt::SmoothTransformation)); + iconLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/avatar_grey_128.png").scaledToHeight(S,Qt::SmoothTransformation)); hbox->addWidget(iconLabel); statusPeers = new QLabel( tr("Friends: 0/0"), this ); @@ -59,7 +60,7 @@ void PeerStatus::getPeerStatus(unsigned int nFriendCount, unsigned int nOnlineCo int S = QFontMetricsF(iconLabel->font()).height(); if (nOnlineCount > 0) - iconLabel->setPixmap(QPixmap(":/icons/avatar_128.png").scaledToHeight(S,Qt::SmoothTransformation)); + iconLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/avatar_128.png").scaledToHeight(S,Qt::SmoothTransformation)); else - iconLabel->setPixmap(QPixmap(":/icons/avatar_grey_128.png").scaledToHeight(S,Qt::SmoothTransformation)); + iconLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/avatar_grey_128.png").scaledToHeight(S,Qt::SmoothTransformation)); } diff --git a/retroshare-gui/src/gui/statusbar/ratesstatus.cpp b/retroshare-gui/src/gui/statusbar/ratesstatus.cpp index edec6608e..8e4f606f7 100644 --- a/retroshare-gui/src/gui/statusbar/ratesstatus.cpp +++ b/retroshare-gui/src/gui/statusbar/ratesstatus.cpp @@ -24,6 +24,7 @@ #include "ratesstatus.h" #include #include "util/misc.h" +#include "gui/common/FilesDefs.h" #include @@ -35,7 +36,7 @@ RatesStatus::RatesStatus(QWidget *parent) hbox->setSpacing(6); iconLabel = new QLabel( this ); - iconLabel->setPixmap(QPixmap(":/images/up0down0.png")); + iconLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/up0down0.png")); // iconLabel doesn't change over time, so we didn't need a minimum size hbox->addWidget(iconLabel); @@ -64,7 +65,7 @@ void RatesStatus::getRatesStatus(float downKb, uint64_t down, float upKb, uint64 QString up = (upKb > 0)?"1":"0"; QString dw = (downKb > 0)?"1":"0"; - iconLabel->setPixmap(QPixmap(QString(":/images/") + iconLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(QString(":/images/") + "up" + up + "down" + dw + ".png")); diff --git a/retroshare-gui/src/gui/statusbar/torstatus.cpp b/retroshare-gui/src/gui/statusbar/torstatus.cpp index cef3636a0..1b76f45c7 100644 --- a/retroshare-gui/src/gui/statusbar/torstatus.cpp +++ b/retroshare-gui/src/gui/statusbar/torstatus.cpp @@ -31,6 +31,7 @@ #include "TorControl/TorManager.h" #include "TorControl/TorControl.h" +#include "gui/common/FilesDefs.h" #include @@ -46,7 +47,7 @@ TorStatus::TorStatus(QWidget *parent) hbox->addWidget(statusTor); torstatusLabel = new QLabel( this ); - torstatusLabel->setPixmap(QPixmap(":/icons/no-tor.png")); + torstatusLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/no-tor.png")); hbox->addWidget(torstatusLabel); _compactMode = false; @@ -117,24 +118,24 @@ void TorStatus::getTorStatus() if(torstatus == Tor::TorControl::TorOffline || !online || !tor_control_ok) { // RED - some issue. - torstatusLabel->setPixmap(QPixmap(":/icons/tor-stopping.png").scaledToHeight(1.5*S,Qt::SmoothTransformation)); + torstatusLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/tor-stopping.png").scaledToHeight(1.5*S,Qt::SmoothTransformation)); torstatusLabel->setToolTip( text + tr("Tor is currently offline")); } else if(torstatus == Tor::TorControl::TorReady && online && tor_control_ok) { - torstatusLabel->setPixmap(QPixmap(":/icons/tor-on.png").scaledToHeight(1.5*S,Qt::SmoothTransformation)); + torstatusLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/tor-on.png").scaledToHeight(1.5*S,Qt::SmoothTransformation)); torstatusLabel->setToolTip( text + tr("Tor is OK")); } else // torstatus == Tor::TorControl::TorUnknown { // GRAY. - torstatusLabel->setPixmap(QPixmap(":/icons/no-tor.png").scaledToHeight(1.5*S,Qt::SmoothTransformation)); + torstatusLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/no-tor.png").scaledToHeight(1.5*S,Qt::SmoothTransformation)); torstatusLabel->setToolTip( text + tr("No tor configuration")); } } else { - torstatusLabel->setPixmap(QPixmap(":/icons/tor-stopping.png").scaledToHeight(S,Qt::SmoothTransformation)); + torstatusLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/tor-stopping.png").scaledToHeight(S,Qt::SmoothTransformation)); torstatusLabel->setToolTip( text + tr("Tor is currently offline")); } } diff --git a/retroshare-gui/src/gui/style/StyleDialog.cpp b/retroshare-gui/src/gui/style/StyleDialog.cpp index 8ccae9edc..7d7d5c1b7 100644 --- a/retroshare-gui/src/gui/style/StyleDialog.cpp +++ b/retroshare-gui/src/gui/style/StyleDialog.cpp @@ -24,6 +24,7 @@ #include "StyleDialog.h" #include "gui/style/RSStyle.h" +#include "gui/common/FilesDefs.h" /** Default constructor */ StyleDialog::StyleDialog(RSStyle &style, QWidget *parent) @@ -32,7 +33,7 @@ StyleDialog::StyleDialog(RSStyle &style, QWidget *parent) /* Invoke Qt Designer generated QObject setup routine */ ui.setupUi(this); - ui.headerFrame->setHeaderImage(QPixmap(":/images/library.png")); + ui.headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/icons/collections.png")); ui.headerFrame->setHeaderText(tr("Define Style")); /* Load window postion */ diff --git a/retroshare-gui/src/gui/unfinished/ApplicationWindow.cpp b/retroshare-gui/src/gui/unfinished/ApplicationWindow.cpp index 9a16582e3..2a0420e7e 100644 --- a/retroshare-gui/src/gui/unfinished/ApplicationWindow.cpp +++ b/retroshare-gui/src/gui/unfinished/ApplicationWindow.cpp @@ -49,19 +49,10 @@ /* Images for toolbar icons */ #define IMAGE_RETROSHARE ":/images/RetroShare16.png" #define IMAGE_ABOUT ":/images/informations_24x24.png" -#define IMAGE_STATISTIC ":/images/ksysguard32.png" -#define IMAGE_GAMES ":/images/kgames.png" -#define IMAGE_PHOTO ":/images/lphoto.png" -#define IMAGE_BWGRAPH ":/images/ksysguard.png" -#define IMAGE_CLOSE ":/images/close_normal.png" -#define IMAGE_CALENDAR ":/images/calendar.png" -#define IMAGE_LIBRARY ":/images/library.png" +#define IMAGE_LIBRARY ":/icons/collections.png" #define IMAGE_PLUGINS ":/images/extension_32.png" -#define IMAGE_GXSFORUMS ":/images/konversation.png" #define IMAGE_WIKI ":/images/wikibook_32.png" #define IMAGE_POSTED ":/images/posted_32.png" -#define IMAGE_GXSCHANNELS ":/images/channels.png" -#define IMAGE_IDENTITY ":/images/identity/identities_32.png" #define IMAGE_CIRCLES ":/images/user/agt_forum24.png" @@ -82,24 +73,24 @@ ApplicationWindow::ApplicationWindow(QWidget* parent, Qt::WindowFlags flags) //StatisticDialog *statisticDialog = NULL; //ui.stackPages->add(statisticDialog = new StatisticDialog(ui.stackPages), - // createPageAction(QIcon(IMAGE_STATISTIC), tr("Statistics"), grp)); + // createPageAction(FilesDefs::getIconFromQtResourcePath(IMAGE_STATISTIC), tr("Statistics"), grp)); //GamesDialog *gamesDialog = NULL; //ui.stackPages->add(gamesDialog = new GamesDialog(ui.stackPages), - // createPageAction(QIcon(IMAGE_GAMES), tr("Games Launcher"), grp)); + // createPageAction(QIcon(), tr("Games Launcher"), grp)); //CalDialog *calDialog = NULL; //ui.stackPages->add(calDialog = new CalDialog(ui.stackPages), - // createPageAction(QIcon(IMAGE_CALENDAR), tr("Shared Calendars"), grp)); + // createPageAction(QIcon(), tr("Shared Calendars"), grp)); #if 0 IdDialog *idDialog = NULL; ui.stackPages->add(idDialog = new IdDialog(ui.stackPages), - action = createPageAction(QIcon(IMAGE_IDENTITY), tr("Identities"), grp)); + action = createPageAction(QIcon(), tr("Identities"), grp)); #ifdef RS_USE_CIRCLES CirclesDialog *circlesDialog = NULL; ui.stackPages->add(circlesDialog = new CirclesDialog(ui.stackPages), - action = createPageAction(QIcon(IMAGE_CIRCLES ), tr("Circles"), grp)); + action = createPageAction(FilesDefs::getIconFromQtResourcePath(IMAGE_CIRCLES ), tr("Circles"), grp)); mNotify.push_back(QPair(circlesDialog, action)); #endif #endif @@ -107,37 +98,26 @@ ApplicationWindow::ApplicationWindow(QWidget* parent, Qt::WindowFlags flags) #ifdef RS_USE_PHOTOSHARE PhotoShare *photoShare = NULL; ui.stackPages->add(photoShare = new PhotoShare(ui.stackPages), - action = createPageAction(QIcon(IMAGE_PHOTO), tr("Photos"), grp)); + action = createPageAction(QIcon(), tr("Photos"), grp)); mNotify.push_back(QPair(photoShare, action)); #endif /*PostedDialog *postedDialog = NULL; ui.stackPages->add(postedDialog = new PostedDialog(ui.stackPages), - action = createPageAction(QIcon(IMAGE_POSTED), tr("Posted Links"), grp)); + action = createPageAction(FilesDefs::getIconFromQtResourcePath(IMAGE_POSTED), tr("Posted Links"), grp)); postedDialog->setup(); mNotify.push_back(QPair(postedDialog, action)); WikiDialog *wikiDialog = NULL; ui.stackPages->add(wikiDialog = new WikiDialog(ui.stackPages), - action = createPageAction(QIcon(IMAGE_WIKI), tr("Wiki Pages"), grp)); + action = createPageAction(FilesDefs::getIconFromQtResourcePath(IMAGE_WIKI), tr("Wiki Pages"), grp)); mNotify.push_back(QPair(wikiDialog, action));*/ -#if 0 - GxsForumsDialog *gxsforumsDialog = NULL; - ui.stackPages->add(gxsforumsDialog = new GxsForumsDialog(ui.stackPages), - action = createPageAction(QIcon(IMAGE_GXSFORUMS), tr("GxsForums"), grp)); - mNotify.push_back(QPair(gxsforumsDialog, action)); - - ChannelDialog *gxschannelDialog = NULL; - ui.stackPages->add(gxschannelDialog = new ChannelDialog(ui.stackPages), - action = createPageAction(QIcon(IMAGE_GXSCHANNELS), tr("GxsChannels"), grp)); - gxschannelDialog->setup(); -#endif // THESE HAVE TO BE CONVERTED TO VEG FORMAT #if USE_VEG_SERVICE WireDialog *wireDialog = NULL; ui.stackPages->add(wireDialog = new WireDialog(ui.stackPages), - action = createPageAction(QIcon(IMAGE_BWGRAPH), tr("The Wire"), grp)); + action = createPageAction(QIcon(), tr("The Wire"), grp)); mNotify.push_back(QPair(wireDialog, action)); #endif diff --git a/retroshare-gui/src/gui/unfinished/ExampleDialog.cpp b/retroshare-gui/src/gui/unfinished/ExampleDialog.cpp index 1cef2f28b..e263f75e9 100644 --- a/retroshare-gui/src/gui/unfinished/ExampleDialog.cpp +++ b/retroshare-gui/src/gui/unfinished/ExampleDialog.cpp @@ -41,7 +41,6 @@ /* Images for context menu icons */ #define IMAGE_REMOVEFRIEND ":/images/removefriend16.png" -#define IMAGE_EXPIORTFRIEND ":/images/exportpeers_16x16.png" #define IMAGE_CHAT ":/images/chat.png" /* Images for Status icons */ #define IMAGE_ONLINE ":/images/im-user.png" @@ -93,7 +92,7 @@ void ExampleDialog::peertreeWidgetCostumPopupMenu( QPoint point ) QMenu contextMnu( this ); QMouseEvent *mevent = new QMouseEvent( QEvent::MouseButtonPress, point, Qt::RightButton, Qt::RightButton, Qt::NoModifier ); - voteupAct = new QAction(QIcon(IMAGE_EXPIORTFRIEND), tr( "Vote Up" ), this ); + voteupAct = new QAction(QIcon(), tr( "Vote Up" ), this ); connect( voteupAct , SIGNAL( triggered() ), this, SLOT( voteup() ) ); votedownAct = new QAction(QIcon(IMAGE_REMOVEFRIEND), tr( "Vote Down" ), this ); diff --git a/retroshare-gui/src/gui/unfinished/GamesDialog.cpp b/retroshare-gui/src/gui/unfinished/GamesDialog.cpp index 7673d5c7e..9b9aee3a6 100644 --- a/retroshare-gui/src/gui/unfinished/GamesDialog.cpp +++ b/retroshare-gui/src/gui/unfinished/GamesDialog.cpp @@ -53,7 +53,6 @@ const uint32_t GAME_PEER_ID = 4; /* Images for context menu icons */ #define IMAGE_REMOVEFRIEND ":/images/removefriend16.png" -#define IMAGE_EXPIORTFRIEND ":/images/exportpeers_16x16.png" #define IMAGE_CHAT ":/images/chat.png" /* Images for Status icons */ #define IMAGE_ONLINE ":/images/im-user.png" @@ -327,7 +326,7 @@ void GamesDialog::gameListPopupMenu( QPoint point ) QMenu contextMnu( this ); QMouseEvent *mevent = new QMouseEvent( QEvent::MouseButtonPress, point, Qt::RightButton, Qt::RightButton, Qt::NoModifier ); - QAction *deleteAct = new QAction(QIcon(IMAGE_EXPIORTFRIEND), tr( "Cancel Game" ), this ); + QAction *deleteAct = new QAction(QIcon(), tr( "Cancel Game" ), this ); connect( deleteAct , SIGNAL( triggered() ), this, SLOT( deleteGame() ) ); contextMnu.clear(); diff --git a/retroshare-gui/src/gui/unfinished/LibraryDialog.cpp b/retroshare-gui/src/gui/unfinished/LibraryDialog.cpp index 409073aba..b257c58c7 100644 --- a/retroshare-gui/src/gui/unfinished/LibraryDialog.cpp +++ b/retroshare-gui/src/gui/unfinished/LibraryDialog.cpp @@ -42,14 +42,11 @@ #include /* Images for context menu icons */ -#define IMAGE_DOWNLOAD ":/images/download16.png" #define IMAGE_PLAY ":/images/start.png" #define IMAGE_HASH_BUSY ":/images/settings.png" -#define IMAGE_HASH_DONE ":/images/friendsfolder24.png" #define IMAGE_MSG ":/images/message-mail.png" -#define IMAGE_ATTACHMENT ":/images/attachment.png" +#define IMAGE_ATTACHMENT ":/icons/png/attachements.png" #define IMAGE_FRIEND ":/images/peers_16x16.png" -#define IMAGE_PROGRESS ":/images/browse-looking.gif" #define IMAGE_LIBRARY ":/images/library.png" diff --git a/retroshare-gui/src/gui/unfinished/PhotoDialog.cpp b/retroshare-gui/src/gui/unfinished/PhotoDialog.cpp index 65c644a5d..42fd5acee 100644 --- a/retroshare-gui/src/gui/unfinished/PhotoDialog.cpp +++ b/retroshare-gui/src/gui/unfinished/PhotoDialog.cpp @@ -42,12 +42,11 @@ /* Images for context menu icons */ #define IMAGE_REMOVEFRIEND ":/images/removefriend16.png" -#define IMAGE_EXPIORTFRIEND ":/images/exportpeers_16x16.png" #define IMAGE_REMOVE ":/images/cancel.png" #define IMAGE_CHAT ":/images/chat.png" /* Images for Status icons */ #define IMAGE_PEER ":/images/user/identity16.png" -#define IMAGE_PHOTOS ":/images/image16.png" +#define IMAGE_PHOTOS ":/icons/png/photo.png" #define PHOTO_ICON_SIZE 90 @@ -135,7 +134,7 @@ void PhotoDialog::peerTreeWidgetCustomPopupMenu( QPoint point ) QMenu contextMnu( this ); QMouseEvent *mevent = new QMouseEvent( QEvent::MouseButtonPress, point, Qt::RightButton, Qt::RightButton, Qt::NoModifier ); - QAction *ins = new QAction(QIcon(IMAGE_EXPIORTFRIEND), tr( "Insert Show Lists" ), this ); + QAction *ins = new QAction(QIcon(), tr( "Insert Show Lists" ), this ); connect( ins , SIGNAL( triggered() ), this, SLOT( insertShowLists() ) ); contextMnu.clear(); @@ -182,7 +181,6 @@ void PhotoDialog::photoTreeWidgetCustomPopupMenu( QPoint point ) connect(rateUnratedAct, SIGNAL(triggered()), this, SLOT(rateUnrated())); QMenu *ratingMenu = new QMenu(tr("Rating"), this); - ratingMenu->setIcon(QIcon(":/images/rating.png")); ratingMenu->addAction(rateExcellenAct); ratingMenu->addAction(rateGoodAct); ratingMenu->addAction(rateAverageAct); diff --git a/retroshare-gui/src/main.cpp b/retroshare-gui/src/main.cpp index f0130ac86..b550749b1 100644 --- a/retroshare-gui/src/main.cpp +++ b/retroshare-gui/src/main.cpp @@ -30,6 +30,7 @@ CrashStackTrace gCrashStackTrace; #include #include +#include "gui/common/FilesDefs.h" #include "gui/FriendsDialog.h" #include "gui/GenCertDialog.h" #include "gui/MainWindow.h" @@ -136,7 +137,7 @@ static void displayWarningAboutDSAKeys() msgBox.setInformativeText(QObject::tr("DSA keys are not yet supported by this version of RetroShare. All these nodes will be unusable. We're very sorry for that.")); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); - msgBox.setWindowIcon(QIcon(":/icons/logo_128.png")); + msgBox.setWindowIcon(FilesDefs::getIconFromQtResourcePath(":/icons/logo_128.png")); msgBox.exec(); } @@ -270,7 +271,7 @@ feenableexcept(FE_INVALID | FE_DIVBYZERO); msgBox.setInformativeText(QObject::tr("Choose between:
  • Ok to copy the existing keyring from gnupg (safest bet), or
  • Close without saving to start fresh with an empty keyring (you will be asked to create a new PGP key to work with RetroShare, or import a previously saved pgp keypair).
  • Cancel to quit and forge a keyring by yourself (needs some PGP skills)
")); msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Discard | QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Ok); - msgBox.setWindowIcon(QIcon(":/icons/logo_128.png")); + msgBox.setWindowIcon(FilesDefs::getIconFromQtResourcePath(":/icons/logo_128.png")); int ret = msgBox.exec(); @@ -299,7 +300,7 @@ feenableexcept(FE_INVALID | FE_DIVBYZERO); displayWarningAboutDSAKeys(); QMessageBox mb(QMessageBox::Critical, QObject::tr("RetroShare"), "", QMessageBox::Ok); - mb.setWindowIcon(QIcon(":/icons/logo_128.png")); + mb.setWindowIcon(FilesDefs::getIconFromQtResourcePath(":/icons/logo_128.png")); switch (initResult) { @@ -440,7 +441,7 @@ feenableexcept(FE_INVALID | FE_DIVBYZERO); } } - QSplashScreen splashScreen(QPixmap(":/images/logo/logo_splash.png")/* , Qt::WindowStaysOnTopHint*/); + QSplashScreen splashScreen(FilesDefs::getPixmapFromQtResourcePath(":/images/logo/logo_splash.png")/* , Qt::WindowStaysOnTopHint*/); splashScreen.show(); splashScreen.showMessage(rshare.translate("SplashScreen", "Load configuration"), Qt::AlignHCenter | Qt::AlignBottom); @@ -580,11 +581,6 @@ feenableexcept(FE_INVALID | FE_DIVBYZERO); #endif #endif // RS_JSONAPI - // This is done using a timer, because the passphrase request from notify is asynchrouneous and therefore clearing the - // passphrase here makes it request for a passphrase when creating the default chat identity. - - QTimer::singleShot(10000, notify, SLOT(resetCachedPassphrases())) ; - /* dive into the endless loop */ int ti = rshare.exec(); delete w ; diff --git a/retroshare-gui/src/qss/blacknight.qss b/retroshare-gui/src/qss/blacknight.qss index b20649895..62adfe013 100644 --- a/retroshare-gui/src/qss/blacknight.qss +++ b/retroshare-gui/src/qss/blacknight.qss @@ -23,7 +23,7 @@ FriendSelectionWidget qproperty-textColorOnline: lightBlue; } -FriendList +NewFriendList { qproperty-textColorStatusOffline: white; qproperty-textColorStatusAway: gray; @@ -51,6 +51,10 @@ background: black; color: lightgray; border-color: transparent; } +QTreeView::item, QTreeWidget::item, QListWidget::item{ + color: lightgray; +} + QDialog, QMainWindow{ background-color: qlineargradient(spread:reflect, x1:0, y1:0, x2:0, y2:1, stop:0 rgb(25, 25, 25), stop:0.05 rgb(0, 0, 0), stop:0.95 rgb(0, 0, 0), stop:1 rgb(25, 25, 25)); } @@ -255,6 +259,12 @@ QTextEdit { color: white; } +RSTextBrowser, MimeTextEdit +{ + /*qproperty-textColorQuote: rgb(125, 125, 255);*/ + qproperty-textColorQuotes: ColorList(#789922 #039bd5 #800000 #800080 #008080 #b10dc9 #85144b #3d9970); +} + /* OpModeStatus need to be at end to overload other values*/ OpModeStatus { qproperty-opMode_Full_Color: #007000; diff --git a/retroshare-gui/src/qss/qdarkstyle-v2.qss b/retroshare-gui/src/qss/qdarkstyle-v2.qss index c9e951921..4f6ee1cae 100644 --- a/retroshare-gui/src/qss/qdarkstyle-v2.qss +++ b/retroshare-gui/src/qss/qdarkstyle-v2.qss @@ -51,6 +51,9 @@ QWidget { selection-background-color: #1464A0; selection-color: #F0F0F0; } +QTreeView::item, QTreeWidget::item, QListWidget::item{ + color: #F0F0F0; +} QWidget:disabled { background-color: #19232D; @@ -1940,7 +1943,7 @@ PlotWidget { padding: 0px; /* to fix cut labels in plots #134 */ } -FriendList { +NewFriendList { qproperty-textColorStatusAway: lightgray; qproperty-textColorStatusBusy: lightgray; qproperty-textColorStatusOnline: green; @@ -2139,3 +2142,23 @@ PostedCardView QFrame#mainFrame [new=false]{ PostedCardView > QFrame#mainFrame[new=true] { background-color: #005000; } + +WireGroupItem QFrame#wire_frame{ + background: transparent; +} + +GxsChannelDialog GroupTreeWidget QTreeWidget#treeWidget::item{ + /*background-color: #F00000;*/ + /*padding: 20px;*/ +} + +RSTextBrowser, MimeTextEdit +{ + /*qproperty-textColorQuote: rgb(125, 125, 255);*/ + qproperty-textColorQuotes: ColorList(#789922 #039bd5 #800000 #800080 #008080 #b10dc9 #85144b #3d9970); +} + +PulseTopLevel QFrame#frame, PulseViewGroup QFrame#frame, PulseReply QFrame#frame { + border: 2px solid #38444d; + border-radius: 6px; +} diff --git a/retroshare-gui/src/qss/qdarkstyle.qss b/retroshare-gui/src/qss/qdarkstyle.qss index ef8495956..af56cfc8d 100644 --- a/retroshare-gui/src/qss/qdarkstyle.qss +++ b/retroshare-gui/src/qss/qdarkstyle.qss @@ -60,6 +60,9 @@ QWidget border-image: none; outline: 0; } +QTreeView::item, QTreeWidget::item, QListWidget::item{ + color: silver; +} QWidget:item:hover { @@ -90,6 +93,10 @@ ForumsDialog, GxsForumThreadWidget qproperty-textColorUnreadChildren: red; qproperty-textColorNotSubscribed: white; qproperty-textColorMissing: darkred; + qproperty-textColorPinned: #D07000; + + qproperty-backgroundColorPinned: #202020; + qproperty-backgroundColorFiltered: darkGreen; } QMenuBar @@ -833,7 +840,7 @@ QDockWidget::close-button:pressed, QDockWidget::float-button:pressed { background: rgba(255, 255, 255, 10); } -FriendList { +NewFriendList { qproperty-textColorStatusAway: lightgray; qproperty-textColorStatusBusy: lightgray; qproperty-textColorStatusOnline: green; @@ -1291,3 +1298,25 @@ PostedCardView QFrame#mainFrame [new=false]{ PostedCardView > QFrame#mainFrame[new=true] { background-color: #005000; } + +WireGroupItem QFrame#wire_frame +{ + border: 1px solid #38444d; + background: transparent; +} + +RSTextBrowser, MimeTextEdit +{ + /*qproperty-textColorQuote: rgb(125, 125, 255);*/ + qproperty-textColorQuotes: ColorList(#789922 #039bd5 #800000 #800080 #008080 #b10dc9 #85144b #3d9970); +} + +ChatWidget QFrame#pluginTitleFrame +{ + background: transparent; +} + +PulseTopLevel QFrame#frame, PulseViewGroup QFrame#frame, PulseReply QFrame#frame { + border: 2px solid #38444d; + border-radius: 6px; +} diff --git a/retroshare-gui/src/retroshare-gui.pro b/retroshare-gui/src/retroshare-gui.pro index 37d8aa335..c03332148 100644 --- a/retroshare-gui/src/retroshare-gui.pro +++ b/retroshare-gui/src/retroshare-gui.pro @@ -25,8 +25,8 @@ CONFIG += console TARGET = retroshare DEFINES += TARGET=\\\"$${TARGET}\\\" -DEPENDPATH *= $${PWD} $${RS_INCLUDE_DIR} retroshare-gui -INCLUDEPATH *= $${PWD} retroshare-gui +DEPENDPATH *= $${PWD} $${RS_INCLUDE_DIR} +INCLUDEPATH *= $${PWD} !include("../../libretroshare/src/use_libretroshare.pri"):error("Including") @@ -59,7 +59,7 @@ rs_gui_cmark { DUMMYCMARKINPUT = FORCE CMAKE_GENERATOR_OVERRIDE="" - win32-g++:CMAKE_GENERATOR_OVERRIDE="-G \"MSYS Makefiles\"" + win32-g++|win32-clang-g++:CMAKE_GENERATOR_OVERRIDE="-G \"MSYS Makefiles\"" gencmarklib.name = Generating libcmark. gencmarklib.input = DUMMYCMARKINPUT gencmarklib.output = $$clean_path($${CMARK_BUILD_PATH}/src/libcmark.a) @@ -73,6 +73,7 @@ rs_gui_cmark { mkdir -p $${CMARK_BUILD_PATH} && cd $${CMARK_BUILD_PATH} && \ cmake \ -DCMAKE_CXX_COMPILER=$$QMAKE_CXX \ + \"-DCMAKE_CXX_FLAGS=$${QMAKE_CXXFLAGS}\" \ $${CMAKE_GENERATOR_OVERRIDE} \ -DCMAKE_INSTALL_PREFIX=. \ -B. \ @@ -114,6 +115,11 @@ CONFIG += gxscircles #CONFIG += framecatcher #CONFIG += blogs +## To enable unfinished services +#CONFIG += wikipoos +#CONFIG += gxsthewire +#CONFIG += gxsphotoshare + DEFINES += RS_RELEASE_VERSION RCC_DIR = temp/qrc UI_DIR = temp/ui @@ -204,7 +210,7 @@ win32-x-g++ { #################################### Windows ##################################### -win32-g++ { +win32-g++|win32-clang-g++ { CONFIG(debug, debug|release) { # show console output CONFIG += console @@ -339,11 +345,16 @@ openbsd-* { LIBS *= -rdynamic } +################################### COMMON stuff ################################## + wikipoos { PRE_TARGETDEPS *= $$OUT_PWD/../../supportlibs/pegmarkdown/lib/libpegmarkdown.a LIBS *= $$OUT_PWD/../../supportlibs/pegmarkdown/lib/libpegmarkdown.a + LIBS *= -lglib-2.0 } +################################### HEADERS & SOURCES ############################# + # Tor controller HEADERS += TorControl/AddOnionCommand.h \ @@ -427,7 +438,9 @@ HEADERS += rshare.h \ gui/FileTransfer/BannedFilesDialog.h \ gui/statistics/TurtleRouterDialog.h \ gui/statistics/TurtleRouterStatistics.h \ + gui/statistics/GxsIdStatistics.h \ gui/statistics/dhtgraph.h \ + gui/statistics/Histogram.h \ gui/statistics/BandwidthGraphWindow.h \ gui/statistics/turtlegraph.h \ gui/statistics/BandwidthStatsWidget.h \ @@ -745,6 +758,7 @@ FORMS += gui/StartDialog.ui \ gui/statistics/DhtWindow.ui \ gui/statistics/TurtleRouterDialog.ui \ gui/statistics/TurtleRouterStatistics.ui \ + gui/statistics/GxsIdStatistics.ui \ gui/statistics/GlobalRouterStatistics.ui \ gui/statistics/GxsTransportStatistics.ui \ gui/statistics/StatisticsWindow.ui \ @@ -985,8 +999,10 @@ SOURCES += main.cpp \ gui/statistics/BandwidthGraphWindow.cpp \ gui/statistics/BandwidthStatsWidget.cpp \ gui/statistics/DhtWindow.cpp \ + gui/statistics/Histogram.cpp \ gui/statistics/TurtleRouterDialog.cpp \ gui/statistics/TurtleRouterStatistics.cpp \ + gui/statistics/GxsIdStatistics.cpp \ gui/statistics/GlobalRouterStatistics.cpp \ gui/statistics/GxsTransportStatistics.cpp \ gui/statistics/StatisticsWindow.cpp \ @@ -1144,51 +1160,49 @@ unfinished_services { gxsphotoshare { - #DEFINES += RS_USE_PHOTOSHARE + #DEFINES += RS_USE_PHOTOSHARE # to enable in unfinished. + DEFINES += RS_USE_PHOTO # enable in MainWindow HEADERS += \ + gui/PhotoShare/AlbumGroupDialog.h \ + gui/PhotoShare/AlbumExtra.h \ gui/PhotoShare/PhotoDrop.h \ gui/PhotoShare/AlbumItem.h \ gui/PhotoShare/AlbumDialog.h \ - gui/PhotoShare/AlbumCreateDialog.h \ gui/PhotoShare/PhotoItem.h \ gui/PhotoShare/PhotoShareItemHolder.h \ gui/PhotoShare/PhotoShare.h \ gui/PhotoShare/PhotoSlideShow.h \ - gui/PhotoShare/PhotoDialog.h \ - gui/PhotoShare/PhotoCommentItem.h \ - gui/PhotoShare/AddCommentDialog.h - + gui/PhotoShare/PhotoDialog.h + FORMS += \ + gui/PhotoShare/AlbumExtra.ui \ gui/PhotoShare/PhotoItem.ui \ gui/PhotoShare/PhotoDialog.ui \ gui/PhotoShare/AlbumItem.ui \ gui/PhotoShare/AlbumDialog.ui \ - gui/PhotoShare/AlbumCreateDialog.ui \ gui/PhotoShare/PhotoShare.ui \ - gui/PhotoShare/PhotoSlideShow.ui \ - gui/PhotoShare/PhotoCommentItem.ui \ - gui/PhotoShare/AddCommentDialog.ui - + gui/PhotoShare/PhotoSlideShow.ui + SOURCES += \ + gui/PhotoShare/AlbumGroupDialog.cpp \ + gui/PhotoShare/AlbumExtra.cpp \ gui/PhotoShare/PhotoItem.cpp \ gui/PhotoShare/PhotoDialog.cpp \ gui/PhotoShare/PhotoDrop.cpp \ gui/PhotoShare/AlbumItem.cpp \ gui/PhotoShare/AlbumDialog.cpp \ - gui/PhotoShare/AlbumCreateDialog.cpp \ gui/PhotoShare/PhotoShareItemHolder.cpp \ gui/PhotoShare/PhotoShare.cpp \ - gui/PhotoShare/PhotoSlideShow.cpp \ - gui/PhotoShare/PhotoCommentItem.cpp \ - gui/PhotoShare/AddCommentDialog.cpp - + gui/PhotoShare/PhotoSlideShow.cpp + RESOURCES += gui/PhotoShare/Photo_images.qrc } wikipoos { + DEFINES += RS_USE_WIKI DEPENDPATH += ../../supportlibs/pegmarkdown INCLUDEPATH += ../../supportlibs/pegmarkdown @@ -1215,18 +1229,43 @@ wikipoos { gxsthewire { - HEADERS += gui/TheWire/PulseItem.h \ - gui/TheWire/WireDialog.h \ + DEFINES += RS_USE_WIRE + + HEADERS += gui/TheWire/WireDialog.h \ + gui/TheWire/WireGroupItem.h \ + gui/TheWire/WireGroupDialog.h \ + gui/TheWire/WireGroupExtra.h \ gui/TheWire/PulseAddDialog.h \ - - FORMS += gui/TheWire/PulseItem.ui \ - gui/TheWire/WireDialog.ui \ + gui/TheWire/PulseViewItem.h \ + gui/TheWire/PulseTopLevel.h \ + gui/TheWire/PulseViewGroup.h \ + gui/TheWire/PulseReply.h \ + gui/TheWire/PulseReplySeperator.h \ + gui/TheWire/PulseMessage.h \ + + FORMS += gui/TheWire/WireDialog.ui \ + gui/TheWire/WireGroupItem.ui \ + gui/TheWire/WireGroupExtra.ui \ gui/TheWire/PulseAddDialog.ui \ + gui/TheWire/PulseTopLevel.ui \ + gui/TheWire/PulseViewGroup.ui \ + gui/TheWire/PulseReply.ui \ + gui/TheWire/PulseReplySeperator.ui \ + gui/TheWire/PulseMessage.ui \ - SOURCES += gui/TheWire/PulseItem.cpp \ - gui/TheWire/WireDialog.cpp \ + SOURCES += gui/TheWire/WireDialog.cpp \ + gui/TheWire/WireGroupItem.cpp \ + gui/TheWire/WireGroupDialog.cpp \ + gui/TheWire/WireGroupExtra.cpp \ gui/TheWire/PulseAddDialog.cpp \ - + gui/TheWire/PulseViewItem.cpp \ + gui/TheWire/PulseTopLevel.cpp \ + gui/TheWire/PulseViewGroup.cpp \ + gui/TheWire/PulseReply.cpp \ + gui/TheWire/PulseReplySeperator.cpp \ + gui/TheWire/PulseMessage.cpp \ + + RESOURCES += gui/TheWire/TheWire_images.qrc } identities { @@ -1317,24 +1356,28 @@ gxschannels { HEADERS += gui/gxschannels/GxsChannelDialog.h \ gui/gxschannels/GxsChannelGroupDialog.h \ gui/gxschannels/CreateGxsChannelMsg.h \ - gui/gxschannels/GxsChannelPostsWidget.h \ - gui/gxschannels/GxsChannelFilesWidget.h \ + gui/gxschannels/GxsChannelPostsWidgetWithModel.h \ + gui/gxschannels/GxsChannelPostsModel.h \ + gui/gxschannels/GxsChannelPostFilesModel.h \ + gui/gxschannels/GxsChannelPostThumbnail.h \ gui/gxschannels/GxsChannelFilesStatusWidget.h \ gui/feeds/GxsChannelGroupItem.h \ gui/feeds/GxsChannelPostItem.h \ gui/gxschannels/GxsChannelUserNotify.h - FORMS += gui/gxschannels/GxsChannelPostsWidget.ui \ - gui/gxschannels/GxsChannelFilesWidget.ui \ + FORMS += \ + gui/gxschannels/GxsChannelPostsWidgetWithModel.ui \ gui/gxschannels/GxsChannelFilesStatusWidget.ui \ gui/gxschannels/CreateGxsChannelMsg.ui \ gui/feeds/GxsChannelGroupItem.ui \ gui/feeds/GxsChannelPostItem.ui SOURCES += gui/gxschannels/GxsChannelDialog.cpp \ - gui/gxschannels/GxsChannelPostsWidget.cpp \ - gui/gxschannels/GxsChannelFilesWidget.cpp \ - gui/gxschannels/GxsChannelFilesStatusWidget.cpp \ + gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp \ + gui/gxschannels/GxsChannelPostsModel.cpp \ + gui/gxschannels/GxsChannelPostFilesModel.cpp \ + gui/gxschannels/GxsChannelPostThumbnail.cpp \ + gui/gxschannels/GxsChannelFilesStatusWidget.cpp \ gui/gxschannels/GxsChannelGroupDialog.cpp \ gui/gxschannels/CreateGxsChannelMsg.cpp \ gui/feeds/GxsChannelGroupItem.cpp \ @@ -1403,9 +1446,6 @@ gxsgui { gui/gxs/GxsMessageFramePostWidget.h \ gui/gxs/GxsGroupFeedItem.h \ gui/gxs/GxsFeedItem.h \ - gui/gxs/RsGxsUpdateBroadcastBase.h \ - gui/gxs/RsGxsUpdateBroadcastWidget.h \ - gui/gxs/RsGxsUpdateBroadcastPage.h \ gui/gxs/GxsGroupShareKey.h \ gui/gxs/GxsUserNotify.h \ gui/gxs/GxsFeedWidget.h \ @@ -1440,9 +1480,6 @@ gxsgui { gui/gxs/GxsMessageFramePostWidget.cpp \ gui/gxs/GxsGroupFeedItem.cpp \ gui/gxs/GxsFeedItem.cpp \ - gui/gxs/RsGxsUpdateBroadcastBase.cpp \ - gui/gxs/RsGxsUpdateBroadcastWidget.cpp \ - gui/gxs/RsGxsUpdateBroadcastPage.cpp \ gui/gxs/GxsUserNotify.cpp \ gui/gxs/GxsFeedWidget.cpp \ util/TokenQueue.cpp \ @@ -1452,3 +1489,16 @@ gxsgui { } + + +wikipoos { + HEADERS += \ + gui/gxs/RsGxsUpdateBroadcastBase.h \ + gui/gxs/RsGxsUpdateBroadcastWidget.h \ + gui/gxs/RsGxsUpdateBroadcastPage.h + + SOURCES += \ + gui/gxs/RsGxsUpdateBroadcastBase.cpp \ + gui/gxs/RsGxsUpdateBroadcastWidget.cpp \ + gui/gxs/RsGxsUpdateBroadcastPage.cpp \ +} diff --git a/retroshare-gui/src/rshare.cpp b/retroshare-gui/src/rshare.cpp index 3898a899a..cbed6698a 100644 --- a/retroshare-gui/src/rshare.cpp +++ b/retroshare-gui/src/rshare.cpp @@ -42,6 +42,7 @@ #include #include +#include "gui/common/FilesDefs.h" #include #include #include @@ -258,7 +259,7 @@ Rshare::Rshare(QStringList args, int &argc, char **argv, const QString &dir) #ifndef __APPLE__ /* set default window icon */ - setWindowIcon(QIcon(":/icons/logo_128.png")); + setWindowIcon(FilesDefs::getIconFromQtResourcePath(":/icons/logo_128.png")); #endif diff --git a/retroshare-gui/src/util/ClickableLabel.h b/retroshare-gui/src/util/ClickableLabel.h index ec1c66b8d..65070fd67 100644 --- a/retroshare-gui/src/util/ClickableLabel.h +++ b/retroshare-gui/src/util/ClickableLabel.h @@ -38,10 +38,10 @@ signals: protected: void mousePressEvent(QMouseEvent* event); - void enterEvent(QEvent *ev) override { setStyleSheet("QLabel { border: 1px solid #3A3939; }");} + void enterEvent(QEvent * /* ev */ ) override { setStyleSheet("QLabel { border: 2px solid #039bd5; }");} - void leaveEvent(QEvent *ev) override { setStyleSheet("QLabel { border: 2px solid #CCCCCC; border-radius: 3px; }");} + void leaveEvent(QEvent * /* ev */ ) override { setStyleSheet("QLabel { border: 2px solid #CCCCCC; border-radius: 3px; }");} }; -#endif // CLICKABLELABEL_H \ No newline at end of file +#endif // CLICKABLELABEL_H diff --git a/retroshare-gui/src/util/HandleRichText.cpp b/retroshare-gui/src/util/HandleRichText.cpp index c500a6bb9..466e540de 100644 --- a/retroshare-gui/src/util/HandleRichText.cpp +++ b/retroshare-gui/src/util/HandleRichText.cpp @@ -31,6 +31,8 @@ #include "gui/RetroShareLink.h" #include "util/ObjectPainter.h" #include "util/imageutil.h" + +#include "util/rsdebug.h" #include "util/rstime.h" #ifdef USE_CMARK @@ -40,6 +42,8 @@ #include +//#define DEBUG_SAVESPACE 1 + /** * The type of embedding we'd like to do */ @@ -554,18 +558,24 @@ static QString saveSpace(const QString text) if(cursChar==QLatin1Char('>')) { if(!echapChar && i>0) {outBrackets=true; firstOutBracket=true;} } else if(cursChar==QLatin1Char('\t')) { - if(outBrackets && firstOutBracket && (keyName!="style")) savedSpaceText.replace(i, 1, "  "); + if(outBrackets && firstOutBracket && (keyName!="style")) { savedSpaceText.replace(i, 1, "  "); i+= 11; } } else if(cursChar==QLatin1Char(' ')) { - if(outBrackets && firstOutBracket && (keyName!="style")) savedSpaceText.replace(i, 1, " "); + if(outBrackets && firstOutBracket && (keyName!="style")) { savedSpaceText.replace(i, 1, " "); i+= 5; } } else if(cursChar==QChar(0xA0)) { - if(outBrackets && firstOutBracket && (keyName!="style")) savedSpaceText.replace(i, 1, " "); + if(outBrackets && firstOutBracket && (keyName!="style")) { savedSpaceText.replace(i, 1, " "); i+= 5; } } else if(cursChar==QLatin1Char('<')) { if(!echapChar) {outBrackets=false; getKeyName=true; keyName.clear();} } else firstOutBracket=false; echapChar=(cursChar==QLatin1Char('\\')); } - +#ifdef DEBUG_SAVESPACE + RsDbg() << __PRETTY_FUNCTION__ << "Text to save:" << std::endl + << text.toStdString() << std::endl + << "---------------------- Saved Text:" << std::endl + << savedSpaceText.toStdString() << std::endl; +#endif + return savedSpaceText; } @@ -1215,8 +1225,10 @@ QString RsHtml::makeQuotedText(RSTextBrowser *browser) } QStringList sl = text.split(QRegExp("[\r\n]"),QString::SkipEmptyParts); text = sl.join("\n> "); + text.replace("\n> >","\n>>"); // Don't add space for already quotted lines. text.replace(QChar(-4)," ");//Char used when image on text. - return QString("> ") + text; + QString quote = (text.left(1) == ">") ? QString(">") : QString("> "); + return quote + text; } void RsHtml::insertSpoilerText(QTextCursor cursor) diff --git a/retroshare-gui/src/util/RsGxsUpdateBroadcast.cpp b/retroshare-gui/src/util/RsGxsUpdateBroadcast.cpp index efd54a8e7..c228923e8 100644 --- a/retroshare-gui/src/util/RsGxsUpdateBroadcast.cpp +++ b/retroshare-gui/src/util/RsGxsUpdateBroadcast.cpp @@ -1,7 +1,7 @@ /******************************************************************************* * util/RsGxsUpdateBroadcast.cpp * * * - * Copyright (c) 2014 Retroshare Team * + * Copyright (C) 2014-2020 Retroshare Team * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Affero General Public License as * @@ -22,6 +22,7 @@ #include "RsGxsUpdateBroadcast.h" #include "gui/notifyqt.h" +#include "util/qtthreadsutils.h" #include @@ -32,17 +33,20 @@ // now the update notify works through rsnotify and notifyqt // so the single instance per service is not really needed anymore -QMap updateBroadcastMap; +static QMap updateBroadcastMap; RsGxsUpdateBroadcast::RsGxsUpdateBroadcast(RsGxsIfaceHelper *ifaceImpl) : - QObject(NULL), mIfaceImpl(ifaceImpl) + QObject(nullptr), mIfaceImpl(ifaceImpl), mEventHandlerId(0) { - mEventHandlerId = 0; // forces initialization in registerEventsHandler() - - rsEvents->registerEventsHandler(RsEventType::GXS_CHANGES, [this](std::shared_ptr event) - { - onChangesReceived(*dynamic_cast(event.get())); - }, mEventHandlerId ); + rsEvents->registerEventsHandler( + [this](std::shared_ptr event) + { + RsQThreadUtils::postToObject( + [=]() + { onChangesReceived(*dynamic_cast(event.get())); }, + this ); + }, + mEventHandlerId, RsEventType::GXS_CHANGES ); } RsGxsUpdateBroadcast::~RsGxsUpdateBroadcast() diff --git a/retroshare-gui/src/util/RsGxsUpdateBroadcast.h b/retroshare-gui/src/util/RsGxsUpdateBroadcast.h index 1ec733bfa..1b700900d 100644 --- a/retroshare-gui/src/util/RsGxsUpdateBroadcast.h +++ b/retroshare-gui/src/util/RsGxsUpdateBroadcast.h @@ -31,7 +31,7 @@ struct RsGxsChanges; typedef uint32_t TurtleRequestId ; -class RsGxsUpdateBroadcast : public QObject +class RS_DEPRECATED RsGxsUpdateBroadcast : public QObject { Q_OBJECT diff --git a/retroshare-gui/src/util/RsSyntaxHighlighter.cpp b/retroshare-gui/src/util/RsSyntaxHighlighter.cpp index 9de76aaa3..ed05b702c 100644 --- a/retroshare-gui/src/util/RsSyntaxHighlighter.cpp +++ b/retroshare-gui/src/util/RsSyntaxHighlighter.cpp @@ -23,35 +23,90 @@ RsSyntaxHighlighter::RsSyntaxHighlighter(QTextEdit *parent) : QSyntaxHighlighter(parent) { - + quotationFormats.append(QTextCharFormat()); } - -void RsSyntaxHighlighter::highlightBlock(const QString &text) +QColor RsSyntaxHighlighter::textColorQuote () const { - QRegExp endl("[\\r\\n\\x2028]"); //Usually 0x2028 cahracter is used for newline, no idea why - int index = 0; - QStringList lines = text.split(endl); - foreach (const QString &line, lines) { - if(line.trimmed().startsWith('>')) { - setFormat(index, line.length(), quotationFormat); - } - index += line.length() + 1; - } - //Make it work with the compact chat style - if(lines.length() > 0){ - int i = lines[0].indexOf(": >"); - if(i != -1) { - setFormat(i+2, lines[0].length()-i-2, quotationFormat); - } - } + return quotationFormats.at(0).foreground().color(); +} +QVariant RsSyntaxHighlighter::textColorQuotes() const +{ + QList l; + foreach(auto i, quotationFormats) + l.append(QVariant(i.foreground().color())); + + return QVariant(l); } void RsSyntaxHighlighter::setTextColorQuote(QColor textColorQuote) { - quotationFormat.setForeground(textColorQuote); + quotationFormats[0].setForeground(textColorQuote); this->rehighlight(); } +void RsSyntaxHighlighter::setTextColorQuotes(QVariant textColorQuotes) +{ + QStringList parList = textColorQuotes.toStringList(); + if ((parList.size() == 2) && (parList.at(0).toLower() == "colorlist")) + { + QStringList colList = parList.at(1).split(" "); + quotationFormats.clear(); + for(int i = 0; i < colList.size(); i++) + { + quotationFormats.append(QTextCharFormat()); + quotationFormats[i].setForeground(QColor(colList[i])); + } + } + + this->rehighlight(); +} + +void RsSyntaxHighlighter::highlightBlock(const QString &text) +{ + if (text == "") return; + + QRegExp endl("[\\r\\n\\x2028]"); //Usually 0x2028 character is used for newline, no idea why + int index = 0; + QStringList lines = text.split(endl); + foreach (const QString &cLine, lines) { + QString line =cLine; + line.replace(QChar::Nbsp,QChar::Space); + int count = 0; + for( int i=0; i' ) + count++; + else if( line[i] != QChar::Space ) + break; + } + //Make it work with the compact chat style + int start = line.indexOf(": >"); + if( start != -1 ) + start += 2; // Start at ">" not ":" + else + start = 0; + + if(count && start > count ) { + // Found but already quotted + start = 0; + } else { + if (start && !count) { + // Start to count after name: > + for(int i=start; i' ) + count++; + else if( line[i] != QChar::Space ) + break; + } + } + } + + if(count) { + setFormat(index + start, line.length() - start, quotationFormats.at(qMin(count-1,quotationFormats.size()-1))); + } + index += line.length() + 1; + } +} + //Dumping the raw unicode string into the console in Base64 encoding /* QByteArray uniline; diff --git a/retroshare-gui/src/util/RsSyntaxHighlighter.h b/retroshare-gui/src/util/RsSyntaxHighlighter.h index 4d01a37d9..4955401e4 100644 --- a/retroshare-gui/src/util/RsSyntaxHighlighter.h +++ b/retroshare-gui/src/util/RsSyntaxHighlighter.h @@ -30,22 +30,22 @@ class RsSyntaxHighlighter : public QSyntaxHighlighter Q_OBJECT Q_PROPERTY(QColor textColorQuote READ textColorQuote WRITE setTextColorQuote) + Q_PROPERTY(QVariant textColorQuotes READ textColorQuotes WRITE setTextColorQuotes) public: RsSyntaxHighlighter(QTextEdit *parent = 0); - QColor textColorQuote() const { return quotationFormat.foreground().color(); }; + QColor textColorQuote () const; + QVariant textColorQuotes() const; + +public slots: + void setTextColorQuote (QColor textColorQuote); + void setTextColorQuotes(QVariant textColorQuotes); protected: void highlightBlock(const QString &text); private: - QTextCharFormat quotationFormat; - -signals: - -public slots: - void setTextColorQuote(QColor textColorQuote); - + QList quotationFormats; }; #endif // RSSYNTAXHIGHLIGHTER_H diff --git a/retroshare-gui/src/util/misc.cpp b/retroshare-gui/src/util/misc.cpp index a66de053c..8a9525d18 100644 --- a/retroshare-gui/src/util/misc.cpp +++ b/retroshare-gui/src/util/misc.cpp @@ -28,6 +28,8 @@ #include #include "misc.h" +#include "util/rsdebug.h" +#include "gui/common/FilesDefs.h" // return best userfriendly storage unit (B, KiB, MiB, GiB, TiB) // use Binary prefix standards from IEC 60027-2 @@ -306,8 +308,10 @@ QPixmap misc::getOpenThumbnailedPicture(QWidget *parent, const QString &caption, if (!getOpenFileName(parent, RshareSettings::LASTDIR_IMAGES, caption, tr("Pictures (*.png *.jpeg *.xpm *.jpg *.tiff *.gif)"), fileName)) return QPixmap(); - return QPixmap(fileName).scaledToHeight(height, Qt::SmoothTransformation).copy( 0, 0, width, height); - //return QPixmap(fileName).scaledToHeight(width, height, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); + if(width > 0 && height > 0) + return FilesDefs::getPixmapFromQtResourcePath(fileName).scaledToHeight(height, Qt::SmoothTransformation).copy( 0, 0, width, height); + else + return FilesDefs::getPixmapFromQtResourcePath(fileName); } bool misc::getOpenFileName(QWidget *parent, RshareSettings::enumLastDir type @@ -405,3 +409,28 @@ QString misc::getExistingDirectory(QWidget *parent, const QString &caption, cons return QFileDialog::getExistingDirectory(parent, caption, dir, QFileDialog::DontUseNativeDialog | QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); #endif } + +/*! + * Clear a Layout content + * \param layout: Layout to Clear + */ +void misc::clearLayout(QLayout * layout) { + if (! layout) + return; + + while (auto item = layout->takeAt(0)) + { + //First get all pointers, else item may be deleted when last object removed and get SIGSEGV + auto *widget = item->widget(); + auto *spacer = item->spacerItem(); + //Then Clear Layout + clearLayout(item->layout()); + //Last clear objects + if (widget) + widget->deleteLater(); + if (spacer) + delete spacer; + + //delete item;//Auto deleted by Qt. + } +} diff --git a/retroshare-gui/src/util/misc.h b/retroshare-gui/src/util/misc.h index 6bf37f4b0..f09280ecf 100644 --- a/retroshare-gui/src/util/misc.h +++ b/retroshare-gui/src/util/misc.h @@ -27,6 +27,7 @@ #include #include #include +#include #include "gui/settings/rsharesettings.h" @@ -182,6 +183,10 @@ class misc : public QObject static QString getExistingDirectory(QWidget *parent = nullptr , const QString &caption = QString() , const QString &dir = QString()); + + //Clear QLayout + static void clearLayout(QLayout *layout); + }; // Trick to get a portable sleep() function diff --git a/retroshare-gui/src/util/printpreview.cpp b/retroshare-gui/src/util/printpreview.cpp index dc53f126a..cae4214d2 100644 --- a/retroshare-gui/src/util/printpreview.cpp +++ b/retroshare-gui/src/util/printpreview.cpp @@ -207,7 +207,7 @@ PrintPreview::PrintPreview(const QTextDocument *document, QWidget *parent) tb->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); QAction *a; - a = new QAction(QIcon(":/images/textedit/fileprint.png"), tr("&Print..."), this); + a = new QAction(QIcon(""), tr("&Print..."), this); a->setShortcut(Qt::CTRL + Qt::Key_P); connect(a, SIGNAL(triggered()), this, SLOT(print())); tb->addAction(a); @@ -219,11 +219,11 @@ PrintPreview::PrintPreview(const QTextDocument *document, QWidget *parent) tb->addSeparator(); - a = new QAction(QIcon(":/images/textedit/zoomin.png"), tr("Zoom In"), this); + a = new QAction(QIcon(""), tr("Zoom In"), this); connect(a, SIGNAL(triggered()), view, SLOT(zoomIn())); tb->addAction(a); - a = new QAction(QIcon(":/images/textedit/zoomout.png"), tr("Zoom Out"), this); + a = new QAction(QIcon(""), tr("Zoom Out"), this); connect(a, SIGNAL(triggered()), view, SLOT(zoomOut())); tb->addAction(a); diff --git a/retroshare-gui/src/util/qtthreadsutils.h b/retroshare-gui/src/util/qtthreadsutils.h index 155b71b7e..1c377b35d 100644 --- a/retroshare-gui/src/util/qtthreadsutils.h +++ b/retroshare-gui/src/util/qtthreadsutils.h @@ -1,7 +1,7 @@ /******************************************************************************* * util/qthreadutils.h * * * - * Copyright (C) 2018 Gioacchino Mazzurco * + * Copyright (C) 2018-2020 Gioacchino Mazzurco * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Affero General Public License as * @@ -27,7 +27,9 @@ #include #include + #include +#include namespace RsQThreadUtils { @@ -44,7 +46,7 @@ void postToObject(F &&fun, QObject *obj = qApp) QObject src; auto type = obj->metaObject(); QObject::connect( &src, &QObject::destroyed, obj, - [fun, type, obj] + [fun = std::move(fun), type, obj] { // ensure that the object is not being destructed if (obj->metaObject()->inherits(type)) fun(); diff --git a/retroshare-service/src/retroshare-service.pro b/retroshare-service/src/retroshare-service.pro index 75cb0b581..c9c884cee 100644 --- a/retroshare-service/src/retroshare-service.pro +++ b/retroshare-service/src/retroshare-service.pro @@ -29,6 +29,8 @@ QT -= gui SOURCES += retroshare-service.cc +################################# Linux ########################################## + android-* { QT += androidextras @@ -56,11 +58,14 @@ appimage { INSTALLS += desktop_files } + unix { target.path = "$${RS_BIN_DIR}" INSTALLS += target } +################################# MacOSX ########################################## + macx { # ENABLE THIS OPTION FOR Univeral Binary BUILD. #CONFIG += ppc x86 @@ -81,7 +86,9 @@ macx { INCLUDEPATH += . $$INC_DIR } -win32-g++ { +################################# Windows ########################################## + +win32-g++|win32-clang-g++ { CONFIG(debug, debug|release) { # show console output CONFIG += console @@ -130,3 +137,6 @@ win32-g++ { QMAKE_PRE_LINK = $(CHK_DIR_EXISTS) lib || $(MKDIR) lib } } + +################################### COMMON stuff ################################## + diff --git a/retroshare.pri b/retroshare.pri index 3eea6b95a..500300cfd 100644 --- a/retroshare.pri +++ b/retroshare.pri @@ -111,6 +111,19 @@ no_direct_chat:CONFIG -= direct_chat CONFIG *= bitdht no_bitdht:CONFIG -= bitdht +# The DHT stunner is used to determine the NAT type using other RS DHT peers and the STUN (Session Traversal Utilities for NAT) protocol. +# To disable DHT stunner append the following assignation to qmake command line +# "CONFIG+=no_use_dht_stunner" +CONFIG *= use_dht_stunner +no_use_dht_stunner:CONFIG -= use_dht_stunner + +# The DHT stunner can be used to figure out our external IP. As this purely relying on random DHT peers that answer our request, it can easily be abused. +# Therefore, it is turned off by default. +# To enable external ip determination (additionally) based on the dht stunner append the following assignation to qmake +# command line "CONFIG+=use_dht_stunner_ext_ip" +CONFIG *= no_use_dht_stunner_ext_ip +use_dht_stunner_ext_ip:CONFIG -= no_use_dht_stunner_ext_ip + # To select your MacOsX version append the following assignation to qmake # command line "CONFIG+=rs_macos10.11" where 10.11 depends your version macx:CONFIG *= rs_macos10.11 @@ -127,13 +140,18 @@ rs_macos10.15:CONFIG -= rs_macos10.11 CONFIG *= no_rs_jsonapi rs_jsonapi:CONFIG -= no_rs_jsonapi +# Disable i2p BOB support for automatically setting up an i2p tunnel for RS +# "CONFIG+=no_rs_bob" +CONFIG *= rs_bob +no_rs_bob:CONFIG -= rs_bob + # To enable channel indexing append the following assignation to qmake command -# line "CONFIG+=rs_deep_channel_index" -CONFIG *= no_rs_deep_channel_index -rs_deep_channel_index:CONFIG -= no_rs_deep_channel_index +# line "CONFIG+=rs_deep_channels_index" +CONFIG *= no_rs_deep_channels_index +rs_deep_channels_index:CONFIG -= no_rs_deep_channels_index # To enable deep files indexing append the following assignation to qmake -# command line "CONFIG+=rs_files_index" +# command line "CONFIG+=rs_deep_files_index" CONFIG *= no_rs_deep_files_index rs_deep_files_index:CONFIG -= no_rs_deep_files_index @@ -178,6 +196,15 @@ no_rs_service_webui_terminal_password:CONFIG -= rs_service_webui_terminal_passwo CONFIG *= rs_service_terminal_login no_rs_service_terminal_login:CONFIG -= rs_service_terminal_login +# To disable Diffie Hellman group check at init append the following assignation +# to qmake command line "CONFIG+=no_rs_dh_init_check" +# this check is not strictly needed and on some platform is very slow. +# On Android it takes at least one minute at startup which is untolerable for +# most phone users +CONFIG+=rs_dh_init_check +no_rs_dh_init_check:CONFIG -= rs_dh_init_check + + # Specify host precompiled jsonapi-generator path, appending the following # assignation to qmake command line # 'JSONAPI_GENERATOR_EXE=/myBuildDir/jsonapi-generator'. Required for JSON API @@ -403,7 +430,7 @@ defined in command line") DEFINES += RS_MINI_VERSION=$${RS_MINI_VERSION} DEFINES += RS_EXTRA_VERSION=\\\"$${RS_EXTRA_VERSION}\\\" } else { - RS_GIT_DESCRIBE = $$system(git describe) + RS_GIT_DESCRIBE = $$system(git describe --long --match v*.*.*) contains(RS_GIT_DESCRIBE, ^v\d+\.\d+\.\d+.*) { RS_GIT_DESCRIBE_SPLIT = $$split(RS_GIT_DESCRIBE, v) RS_GIT_DESCRIBE_SPLIT = $$split(RS_GIT_DESCRIBE_SPLIT, .) @@ -492,6 +519,14 @@ rs_gxs_trans { bitdht { DEFINES *= RS_USE_BITDHT + + use_dht_stunner { + CONFIG *= useDhtStunner + + use_dht_stunner_ext_ip { + DEFINES *= ALLOW_DHT_STUNNER + } + } } direct_chat { @@ -520,6 +555,10 @@ rs_webui { DEFINES *= RS_WEBUI } +rs_bob { + DEFINES *= RS_USE_I2P_BOB +} + rs_deep_channels_index:DEFINES *= RS_DEEP_CHANNEL_INDEX rs_deep_files_index:DEFINES *= RS_DEEP_FILES_INDEX @@ -531,6 +570,8 @@ rs_use_native_dialogs:DEFINES *= RS_NATIVEDIALOGS rs_broadcast_discovery:DEFINES *= RS_BROADCAST_DISCOVERY +no_rs_dh_init_check:DEFINES *= RS_DISABLE_DIFFIE_HELLMAN_INIT_CHECK + debug { QMAKE_CXXFLAGS -= -O2 -fomit-frame-pointer QMAKE_CFLAGS -= -O2 -fomit-frame-pointer @@ -599,7 +640,7 @@ android-* { RS_THREAD_LIB = } -win32-g++ { +win32-g++|win32-clang-g++ { !isEmpty(EXTERNAL_LIB_DIR) { message(Use pre-compiled libraries in $${EXTERNAL_LIB_DIR}.) PREFIX = $$system_path($$EXTERNAL_LIB_DIR) @@ -651,6 +692,10 @@ win32-g++ { message(***retroshare.pri:Win32 PREFIX $$PREFIX INCLUDEPATH $$INCLUDEPATH QMAKE_LIBDIR $$QMAKE_LIBDIR DEFINES $$DEFINES) } +win32-clang-g++ { + QMAKE_CXXFLAGS += -femulated-tls +} + macx-* { rs_macos10.8 { message(***retroshare.pri: Set Target and SDK to MacOS 10.8 ) diff --git a/tests/unittests/libretroshare/gxs/common/data_support.cc b/tests/unittests/libretroshare/gxs/common/data_support.cc index bad481de9..332dcd128 100644 --- a/tests/unittests/libretroshare/gxs/common/data_support.cc +++ b/tests/unittests/libretroshare/gxs/common/data_support.cc @@ -146,7 +146,6 @@ void init_item(RsGxsMsgMetaData* metaMsg) init_random(metaMsg->mGroupId) ; init_random(metaMsg->mMsgId) ; - metaMsg->refcount = 1; init_random(metaMsg->mThreadId) ; init_random(metaMsg->mParentId) ; init_random(metaMsg->mOrigMsgId) ; diff --git a/tests/unittests/libretroshare/gxs/gen_exchange/rsdummyservices.h b/tests/unittests/libretroshare/gxs/gen_exchange/rsdummyservices.h index a4f09ca1c..f4fd05bac 100644 --- a/tests/unittests/libretroshare/gxs/gen_exchange/rsdummyservices.h +++ b/tests/unittests/libretroshare/gxs/gen_exchange/rsdummyservices.h @@ -64,7 +64,6 @@ public: std::string msgData; - std::ostream &print(std::ostream &out, uint16_t /*indent*/ = 0){ return out; } void clear() { msgData.clear(); } }; @@ -79,7 +78,6 @@ public: std::string grpData; void clear() { grpData.clear(); } - std::ostream &print(std::ostream &out, uint16_t /*indent*/ = 0){ return out; } }; diff --git a/tests/unittests/libretroshare/gxs/nxs_test/nxsdummyservices.cc b/tests/unittests/libretroshare/gxs/nxs_test/nxsdummyservices.cc index c752c67c2..925e90be2 100644 --- a/tests/unittests/libretroshare/gxs/nxs_test/nxsdummyservices.cc +++ b/tests/unittests/libretroshare/gxs/nxs_test/nxsdummyservices.cc @@ -164,15 +164,6 @@ bool rs_nxs_test::RsDummyPgpUtils::VerifySignBin(const void* /*data*/, return true; } -bool rs_nxs_test::RsDummyPgpUtils::askForDeferredSelfSignature(const void* /*data*/, - const uint32_t /*len*/, - unsigned char* /*sign*/, - unsigned int* /*signlen*/, - int& /*signature_result*/, - std::string /*reason*/ - ) { - return true; -} diff --git a/tests/unittests/libretroshare/gxs/nxs_test/nxsdummyservices.h b/tests/unittests/libretroshare/gxs/nxs_test/nxsdummyservices.h index 56da81fcb..aee8f989e 100644 --- a/tests/unittests/libretroshare/gxs/nxs_test/nxsdummyservices.h +++ b/tests/unittests/libretroshare/gxs/nxs_test/nxsdummyservices.h @@ -170,7 +170,6 @@ namespace rs_nxs_test bool parseSignature(unsigned char *sign, unsigned int signlen, RsPgpId& issuer) const; bool VerifySignBin(const void *data, uint32_t len, unsigned char *sign, unsigned int signlen, const PGPFingerprintType& withfingerprint); - bool askForDeferredSelfSignature(const void *data, const uint32_t len, unsigned char *sign, unsigned int *signlen, int& signature_result , std::string reason = ""); private: diff --git a/tests/unittests/libretroshare/services/gxs/FakePgpAuxUtils.cc b/tests/unittests/libretroshare/services/gxs/FakePgpAuxUtils.cc index d24bcca4e..2d274897a 100644 --- a/tests/unittests/libretroshare/services/gxs/FakePgpAuxUtils.cc +++ b/tests/unittests/libretroshare/services/gxs/FakePgpAuxUtils.cc @@ -101,13 +101,4 @@ bool FakePgpAuxUtils::getGPGAllList(std::list &ids) return true; } -bool FakePgpAuxUtils::askForDeferredSelfSignature(const void* /*data*/, const uint32_t /*len*/, unsigned char *sign, unsigned int *signlen,int& signature_result, std::string /*reason = ""*/ ) -{ - for(unsigned int i = 0; i < *signlen; i++) - { - sign[i] = 0; - } - signature_result = 1; - return true; -} diff --git a/tests/unittests/libretroshare/services/gxs/FakePgpAuxUtils.h b/tests/unittests/libretroshare/services/gxs/FakePgpAuxUtils.h index 0cb48dbff..63b20b85d 100644 --- a/tests/unittests/libretroshare/services/gxs/FakePgpAuxUtils.h +++ b/tests/unittests/libretroshare/services/gxs/FakePgpAuxUtils.h @@ -33,7 +33,6 @@ public: virtual bool parseSignature(unsigned char *sign, unsigned int signlen, RsPgpId& issuer) const; virtual bool VerifySignBin(const void *data, uint32_t len, unsigned char *sign, unsigned int signlen, const PGPFingerprintType& withfingerprint); - virtual bool askForDeferredSelfSignature(const void *data, const uint32_t len, unsigned char *sign, unsigned int *signlen, int& signature_result , std::string reason); virtual void addPeerListToPgpList(const std::list &ids); virtual void addPeerIdToPgpList(const RsPeerId &id); diff --git a/tests/unittests/libretroshare/services/gxs/gxstestservice.cc b/tests/unittests/libretroshare/services/gxs/gxstestservice.cc index e30309942..8d1799507 100644 --- a/tests/unittests/libretroshare/services/gxs/gxstestservice.cc +++ b/tests/unittests/libretroshare/services/gxs/gxstestservice.cc @@ -82,8 +82,6 @@ void GxsTestService::notifyChanges(std::vector& changes) { std::cerr << "GxsTestService::notifyChanges() New stuff"; std::cerr << std::endl; - - RsGxsIfaceHelper::receiveChanges(changes); } /* Specific Service Data */