diff --git a/.gitignore b/.gitignore
index 6f9356d05..606e522a3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: (C) 2004-2019 Retroshare Team
+# SPDX-FileCopyrightText: (C) 2004-2022 Retroshare Team
# SPDX-License-Identifier: CC0-1.0
*.o
@@ -16,4 +16,22 @@ Thumbs.db
.kdev4
*.kdev4
+.qmake.stash
+
!supportlibs/libsam3/Makefile
+
+# QtCreator cruft
+*CMakeLists.txt.user*
+
+# Build artifacts
+/jsonapi-generator/src/jsonapi-generator
+/jsonapi-generator/src/jsonapi-generator-doxygen-final.conf
+/jsonapi-generator/src/jsonapi-includes.inl
+/jsonapi-generator/src/jsonapi-wrappers.inl
+/jsonapi-generator/src/xml/
+/retroshare-friendserver/src/retroshare-friendserver
+/retroshare-gui/src/include/
+/retroshare-gui/src/retroshare
+/retroshare-gui/src/temp/
+/retroshare-service/src/retroshare-service
+/*.tar.?z
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 8e96b8124..45fed1bb2 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,7 +1,7 @@
-image: docker:stable
+image: docker:latest
services:
- - docker:stable-dind
+ - docker:dind
stages:
- build
@@ -18,6 +18,7 @@ variables:
build-ubuntu-test-image:
stage: build
script:
+ - docker --version
- >
docker login "$CI_REGISTRY"
--username "$CI_REGISTRY_USER"
@@ -35,6 +36,7 @@ build-ubuntu-test-image:
test-ubuntu:
stage: test
script:
+ - docker --version
- >
docker login "$CI_REGISTRY"
--username "$CI_REGISTRY_USER"
@@ -67,43 +69,3 @@ test-ubuntu:
docker exec retroshare
curl --verbose http://127.0.0.1:9092/rsMsgs/getChatLobbyList | jq
- docker container stop retroshare
-
-build-android-arm-apk:
- stage: build
- script:
- - mkdir Dockercontext
- - >
- docker build -t retroshare:android_arm_latest
- $($CI_PROJECT_DIR/build_scripts/GitlabCI/getRepoArgs.sh)
- --build-arg RS_SERVICE_QMAKE_EXTRA_OPTS="$RS_SERVICE_QMAKE_EXTRA_OPTS"
- --file $CI_PROJECT_DIR/build_scripts/GitlabCI/Android.Dockerfile
- Dockercontext
-# see https://stackoverflow.com/a/59055906
- - >
- docker cp
- $(docker create --rm retroshare:android_arm_latest):/retroshare-service-android-build/android-build/build/outputs/apk/debug/android-build-debug.apk
- $CI_PROJECT_DIR/RetroShare_Android_Service-arm.apk
- artifacts:
- paths:
- - RetroShare_Android_Service-arm.apk
-
-# Use separate runner to avoid no space left on device
-build-android-arm64-apk:
- stage: build
- script:
- - mkdir Dockercontext
- - >
- docker build -t retroshare:android_arm64_latest
- $($CI_PROJECT_DIR/build_scripts/GitlabCI/getRepoArgs.sh)
- --build-arg ANDROID_PLATFORM_VER=21
- --build-arg ANDROID_NDK_ARCH=arm64
- --build-arg RS_SERVICE_QMAKE_EXTRA_OPTS="$RS_SERVICE_QMAKE_EXTRA_OPTS"
- --file $CI_PROJECT_DIR/build_scripts/GitlabCI/Android.Dockerfile
- Dockercontext
- - >
- docker cp
- $(docker create --rm retroshare:android_arm64_latest):/retroshare-service-android-build/android-build/build/outputs/apk/debug/android-build-debug.apk
- $CI_PROJECT_DIR/RetroShare_Android_Service-arm64.apk
- artifacts:
- paths:
- - RetroShare_Android_Service-arm64.apk
diff --git a/.gitmodules b/.gitmodules
index 9ac26001d..7692f4556 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -10,7 +10,8 @@
[submodule "supportlibs/udp-discovery-cpp"]
path = supportlibs/udp-discovery-cpp
url = https://github.com/truvorskameikin/udp-discovery-cpp.git
- branch = develop
+ branch = master
+# develop branch was removed we were using it at commit f3a3103a6c52e5707629e8d0a7e279a7758fe845
[submodule "supportlibs/rapidjson"]
path = supportlibs/rapidjson
url = https://github.com/Tencent/rapidjson.git
@@ -30,5 +31,8 @@
branch = master
[submodule "libretroshare"]
path = libretroshare
- url = ../libretroshare
+ url = https://github.com/RetroShare/libretroshare.git
branch = master
+[submodule "retroshare-webui"]
+ path = retroshare-webui
+ url = https://github.com/RetroShare/RSNewWebUI.git
diff --git a/README.asciidoc b/README.asciidoc
index 63890af9e..e1f41e74d 100644
--- a/README.asciidoc
+++ b/README.asciidoc
@@ -9,9 +9,10 @@ RetroShare provides file sharing, chat, messages, forums, channels and more.
.Build Status
|===============================================================================
-|GNU/Linux, Android (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 (via Gitlab CI) | image:https://gitlab.com/RetroShare/RetroShare/badges/master/pipeline.svg[link="https://gitlab.com/RetroShare/RetroShare/-/commits/master",title="pipeline status"]
|Windows (via AppVeyor) | image:https://ci.appveyor.com/api/projects/status/github/RetroShare/RetroShare?svg=true[link="https://ci.appveyor.com/project/RetroShare58622/retroshare"]
|macOS, (via Travis CI) | image:https://app.travis-ci.com/RetroShare/RetroShare.svg?branch=master[link="https://app.travis-ci.com/github/RetroShare/RetroShare"]
+|libretroshare GNU/Linux, Android (via Gitlab CI) | image:https://gitlab.com/RetroShare/libretroshare/badges/master/pipeline.svg[link="https://gitlab.com/RetroShare/libretroshare/-/commits/master",title="pipeline status"]
|===============================================================================
== Get the source
diff --git a/RetroShare.pro b/RetroShare.pro
index 58ae659ab..944e8204e 100644
--- a/RetroShare.pro
+++ b/RetroShare.pro
@@ -68,6 +68,13 @@ retroshare_plugins {
plugins.target = plugins
}
+rs_webui {
+ SUBDIRS += retroshare-webui
+ retroshare-webui.file = retroshare-webui/webui.pro
+ retroshare-webui.target = rs_webui
+ retroshare-webui.depends = retroshare_gui
+}
+
wikipoos {
SUBDIRS += pegmarkdown
pegmarkdown.file = supportlibs/pegmarkdown/pegmarkdown.pro
diff --git a/appveyor.yml b/appveyor.yml
index cd706d02c..5bc004fa1 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -258,10 +258,10 @@ after_build:
# artifacts configuration #
#---------------------------------#
-#artifacts:
-# - path: '**\*.exe'
-# - path: filelist.txt
-# - path: $(RS_DEPLOY)
+artifacts:
+ - path: '**\*.exe'
+ - path: filelist.txt
+ - path: $(RS_DEPLOY)
#
# # pushing a single file
diff --git a/build_scripts/GitlabCI/base.Dockerfile b/build_scripts/GitlabCI/base.Dockerfile
index b65ed1776..5fd1bd81d 100644
--- a/build_scripts/GitlabCI/base.Dockerfile
+++ b/build_scripts/GitlabCI/base.Dockerfile
@@ -12,16 +12,26 @@
## To run the container
# docker run -it -p 127.0.0.1:9092:9092 "${CI_REGISTRY_IMAGE}" retroshare-service --jsonApiPort 9092 --jsonApiBindAddress 0.0.0.0
-FROM ubuntu
+FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
+ENV APT_UNAT="--assume-yes --quiet"
-RUN apt-get update -y && apt-get upgrade -y -qq && \
- apt-get install -y -qq build-essential cimg-dev libssl-dev libbz2-dev \
- libsqlite3-dev \
- libsqlcipher-dev libupnp-dev pkg-config libz-dev \
- qt5-default libxapian-dev qttools5-dev doxygen rapidjson-dev \
- git cmake curl
+RUN apt-get update $APT_UNAT && \
+ apt-get upgrade --show-upgraded $APT_UNAT && \
+ apt-get clean $APT_UNAT && \
+ apt-get install --no-install-recommends $APT_UNAT \
+ bash build-essential cimg-dev libssl-dev libbz2-dev \
+ libminiupnpc-dev \
+ libsqlite3-dev libsqlcipher-dev \
+ pkg-config libz-dev \
+ libxapian-dev doxygen rapidjson-dev \
+ git cmake curl python3
+
+## Avoid git cloning spuriously failing with
+# server certificate verification failed. CAfile: none CRLfile: none
+RUN apt-get install --no-install-recommends $APT_UNAT --reinstall \
+ ca-certificates
RUN git clone --depth 1 https://github.com/aetilius/pHash.git && \
rm -rf pHash-build && mkdir pHash-build && cd pHash-build && \
@@ -30,15 +40,23 @@ RUN git clone --depth 1 https://github.com/aetilius/pHash.git && \
rm -rf pHash-build pHash
ARG FRESHCLONE=0
-ARG REPO_URL=https://gitlab.com/RetroShare/RetroShare.git
+ARG REPO_URL=https://github.com/RetroShare/RetroShare.git
ARG REPO_BRANCH=master
ARG REPO_DEPTH="--depth 2000"
-RUN git clone $REPO_DEPTH $REPO_URL -b $REPO_BRANCH && cd RetroShare && \
- git fetch --tags && cd ..
-RUN mkdir RetroShare-build && cd RetroShare-build && \
- qmake ../RetroShare \
- CONFIG+=no_retroshare_plugins \
- CONFIG+=retroshare_service CONFIG+=no_retroshare_gui \
- CONFIG+=rs_jsonapi CONFIG+=rs_deep_search && \
- (make -j$(nproc) || make -j$(nproc) || make) && make install && \
+RUN git clone $REPO_DEPTH $REPO_URL -b $REPO_BRANCH && \
+ cd RetroShare && \
+ git fetch --tags && \
+ git submodule update --init \
+ libbitdht/ libretroshare/ openpgpsdk/ retroshare-webui/ \
+ supportlibs/restbed/ && \
+ cd supportlibs/restbed/ && \
+ git submodule update --init \
+ dependency/asio/ dependency/kashmir/ && \
+ cd ../../../
+
+RUN \
+ mkdir RetroShare-build && cd RetroShare-build && \
+ cmake -B. -S../RetroShare/retroshare-service \
+ -DRS_FORUM_DEEP_INDEX=ON -DRS_JSON_API=ON -DRS_WEBUI=ON && \
+ make -j$(nproc) && make install && \
cd .. && rm -rf RetroShare-build
diff --git a/build_scripts/GitlabCI/gitlabCI.Dockerfile b/build_scripts/GitlabCI/gitlabCI.Dockerfile
index aa9c6cc87..5ae3f4ed3 100644
--- a/build_scripts/GitlabCI/gitlabCI.Dockerfile
+++ b/build_scripts/GitlabCI/gitlabCI.Dockerfile
@@ -2,17 +2,19 @@ FROM registry.gitlab.com/retroshare/retroshare:base
RUN apt-get update -y && apt-get upgrade -y
-ARG REPO_URL=https://gitlab.com/RetroShare/RetroShare.git
+ARG REPO_URL=https://github.com/RetroShare/RetroShare.git
ARG REPO_BRANCH=master
RUN \
cd RetroShare && git remote add testing $REPO_URL && \
git fetch --tags testing $REPO_BRANCH && \
git reset --hard testing/$REPO_BRANCH && \
+ git submodule update --init \
+ libbitdht/ libretroshare/ openpgpsdk/ retroshare-webui/ && \
git --no-pager log --max-count 1
RUN \
mkdir RetroShare-build && cd RetroShare-build && \
- qmake ../RetroShare CONFIG+=no_retroshare_gui \
- CONFIG+=retroshare_service \
- CONFIG+=rs_jsonapi CONFIG+=rs_deep_search && \
- (make -j$(nproc) || make -j$(nproc) || make) && make install && \
+ cmake -B. -S../RetroShare/retroshare-service \
+ -DRS_FORUM_DEEP_INDEX=ON -DRS_JSON_API=ON -DRS_WEBUI=ON \
+ -DRS_WARN_DEPRECATED=OFF -DRS_WARN_LESS=ON && \
+ make -j$(nproc) && make install && \
cd .. && rm -rf RetroShare-build
diff --git a/build_scripts/OBS b/build_scripts/OBS
index df16cb915..0a3997cc1 160000
--- a/build_scripts/OBS
+++ b/build_scripts/OBS
@@ -1 +1 @@
-Subproject commit df16cb915465d058c75277678799ce4dadeae287
+Subproject commit 0a3997cc1355b2c848161dca015b7e2df039707b
diff --git a/build_scripts/OSX/MacOS_X_InstallGuide.md b/build_scripts/OSX/MacOS_X_InstallGuide.md
index 6dafd63c0..4afa90e2c 100644
--- a/build_scripts/OSX/MacOS_X_InstallGuide.md
+++ b/build_scripts/OSX/MacOS_X_InstallGuide.md
@@ -30,32 +30,22 @@ In GitHub Desktop -> Clone Repository -> URL
Add Repository URL: https://github.com/RetroShare/RetroShare.git and Clone
-## ***Choose if you use MacPort or HomeBrew***
+## ***Get XCode & MacOSX SDK***
-### MacPort Installation
+Install XCode following this guide: [XCode](http://guide.macports.org/#installing.xcode)
-Install MacPort and XCode following this guide: [MacPort and XCode](http://guide.macports.org/#installing.xcode)
+To identify the correct version of Xcode to install, you need to know which OS you are running. Go to the [x] menu -> "About This Mac" and read the macOS version number.
-Start XCode to get it updated and to able C compiler to create executables.
+If you are running the macOS Catalina >= 10.15, you can install Xcode directly from App Store using the instructions below.
-#### Install libraries
+You can find older versions of Xcode at [Apple Developer Downloads](https://developer.apple.com/downloads/). Find the appropriate .xip file for your macOS version
- $ sudo port -v selfupdate
- $ sudo port install openssl
- $ sudo port install miniupnpc
- $ sudo port install libmicrohttpd
-
-For VOIP Plugin:
+To install from App Store:
- $ sudo port install speex-devel
- $ sudo port install opencv
- $ sudo port install ffmpeg
+Select [x] menu - > "App Store…".
+Search for Xcode. Download and install.
-Get Your OSX SDK if missing: [MacOSX-SDKs](https://github.com/phracker/MacOSX-SDKs)
-
-### HOMEBREW Installation
-
-Install HomeBrew following this guide: [HomeBrew](http://brew.sh/)
+Once Xcode has installed, you must drag the XCode icon into your Applications folder. After you have done this, open Xcode from the Applications folder by double-clicking on the icon and then follow the remaining instructions below.
Install XCode command line developer tools:
@@ -63,13 +53,41 @@ Install XCode command line developer tools:
Start XCode to get it updated and to able C compiler to create executables.
+Get Your MacOSX SDK if missing: [MacOSX-SDKs](https://github.com/phracker/MacOSX-SDKs)
+
+## ***Choose if you use MacPort or HomeBrew***
+
+### MacPort Installation
+
+Install MacPort following this guide: [MacPort](http://guide.macports.org/#installing.xcode)
+
+#### Install libraries
+
+ $ sudo port -v selfupdate
+ $ sudo port install openssl
+ $ sudo port install miniupnpc
+
+For VOIP Plugin:
+
+ $ sudo port install speex-devel
+ $ sudo port install opencv
+ $ sudo port install ffmpeg
+
+
+### HOMEBREW Installation
+
+Install HomeBrew following this guide: [HomeBrew](http://brew.sh/)
+
#### Install libraries
$ brew install openssl
$ brew install miniupnpc
- $ brew install libmicrohttpd
$ brew install rapidjson
$ brew install sqlcipher
+
+#### Install CMake
+
+ $ brew install cmake
If you have error in linking, run this:
@@ -85,8 +103,7 @@ For VOIP Plugin:
For FeedReader Plugin:
$ brew install libxslt
-
-Get Your OSX SDK if missing: [MacOSX-SDKs](https://github.com/phracker/MacOSX-SDKs)
+ $ brew install libxml2
## Last Settings
@@ -104,11 +121,8 @@ In QtCreator Projects -> Build -> Build Settings -> Build Steps -> Add Additiona
## Set your Mac OS SDK version
-Edit RetroShare.pro
- CONFIG += c++14 rs_macos11.1
-
-and then retroshare.pri
+Edit retroshare.pri and set your installed sdk version example for 11.1 -> rs_macos11.1 (line 135:)
macx:CONFIG *= rs_macos11.1
rs_macos10.8:CONFIG -= rs_macos11.1
@@ -122,7 +136,7 @@ and then retroshare.pri
## Link Include & Libraries
-Edit your retroshare.pri and add to macx-* section
+When required edit your retroshare.pri macx-* section, check if the Include and Lib path are correct (macx-* section)
INCLUDEPATH += "/usr/local/opt/openssl/include"
QMAKE_LIBDIR += "/usr/local/opt/openssl/lib"
@@ -131,13 +145,21 @@ Edit your retroshare.pri and add to macx-* section
alternative via Terminal
- $ qmake INCLUDEPATH+="/usr/local/opt/openssl/include" QMAKE_LIBDIR+="/usr/local/opt/openssl/lib" QMAKE_LIBDIR+="/usr/local/opt/sqlcipher/lib" QMAKE_LIBDIR+="/usr/local/opt/miniupnpc/lib"
+ $ qmake
+ INCLUDEPATH+="/usr/local/opt/openssl/include" \
+ QMAKE_LIBDIR+="/usr/local/opt/openssl/lib" \
+ QMAKE_LIBDIR+="/usr/local/opt/sqlcipher/lib" \
+ QMAKE_LIBDIR+="/usr/local/opt/miniupnpc/lib" \
+ CONFIG+=rs_autologin \
+ CONFIG+=rs_use_native_dialogs \
+ CONFIG+=release \
+ ..
For FeedReader Plugin:
INCLUDEPATH += "/usr/local/opt/libxml2/include/libxml2"
-For building RetroShare with plugins:
+With plugins:
$ qmake \
INCLUDEPATH+="/usr/local/opt/openssl/include" QMAKE_LIBDIR+="/usr/local/opt/openssl/lib" \
@@ -159,13 +181,30 @@ For building RetroShare with plugins:
You can now compile RetroShare into Qt Creator or with Terminal
- cd retroshare
- qmake; make
+ $ cd /path/to/retroshare
+ $ qmake ..
+ $ make
You can change Target and SDK in *./retroshare.pri:82* changing value of QMAKE_MACOSX_DEPLOYMENT_TARGET and QMAKE_MAC_SDK
You can find the compiled application at *./retroshare/retroshare-gui/src/retroshare.app*
+## Issues
+
+If you have issues with openssl (Undefined symbols for architecture x86_64) try to add to *~/.profile* file this or via Terminal
+
+ export PATH="/usr/local/opt/openssl/bin:$PATH"
+ export LDFLAGS="-L/usr/local/opt/openssl/lib"
+ export CPPFLAGS="-I/usr/local/opt/openssl/include"
+ export PKG_CONFIG_PATH="/usr/local/opt/openssl/lib/pkgconfig"
+
+For Qt Creator -> QtCreator Projects -> Build -> Build Settings -> Build Steps -> Add Additional arguments:
+
+ LDFLAGS="-L/usr/local/opt/openssl/lib"
+ CPPFLAGS="-I/usr/local/opt/openssl/include"
+
+
+
## Copy Plugins
$ cp \
@@ -173,3 +212,13 @@ You can find the compiled application at *./retroshare/retroshare-gui/src/retros
./plugins/VOIP/lib/libVOIP.dylib \
./plugins/RetroChess/lib/libRetroChess.dylib \
./retroshare-gui/src/RetroShare.app/Contents/Resources/
+
+### Compile Retroshare-Service & Webui with CMake
+before you can compile overwrite the file "asio/include/asio/detail/config.hpp" here is a fix for macos [
+asio fix](https://github.com/chriskohlhoff/asio/commit/68df16d560c68944809bb2947360fe8035e9ae0a)
+
+ $ cd retroshare-service
+ $ mkdir build-dir
+ $ cd build-dir
+ $ cmake -DRS_WEBUI=ON -DCMAKE_BUILD_TYPE=Release ..
+ $ make
diff --git a/build_scripts/OSX/makeOSXPackage.sh b/build_scripts/OSX/makeOSXPackage.sh
new file mode 100644
index 000000000..24808d487
--- /dev/null
+++ b/build_scripts/OSX/makeOSXPackage.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+APP="RetroShare"
+RSVERSION="0.6.7a"
+QTVERSION="Qt-5.14.1"
+
+# Install the 7z to create dmg archives.
+#brew list p7zip || brew install p7zip
+
+# Package your app
+echo "Packaging retroshare..."
+#cd ${project_dir}/build/macOS/clang/x86_64/release/
+cd retroshare-gui/src/
+
+# Remove build directories that you don't want to deploy
+rm -rf moc
+rm -rf obj
+rm -rf qrc
+
+# This sets the CFBundleVersion & CFBundleShortVersionString string
+/usr/libexec/PlistBuddy -c "Delete :CFBundleGetInfoString" retroshare.app/Contents/Info.plist
+/usr/libexec/PlistBuddy -c "Add :CFBundleVersion string $RSVERSION" retroshare.app/Contents/Info.plist
+/usr/libexec/PlistBuddy -c "Add :CFBundleShortVersionString string $RSVERSION" retroshare.app/Contents/Info.plist
+
+# This automatically creates retroshare.dmg
+
+echo "Creating dmg archive..."
+macdeployqt retroshare.app -dmg
+
+DATE=`date +"%m-%d-%Y"`
+MACVERSION=`sw_vers -productVersion`
+#RSVERSION=`git describe --abbrev=0 --tags`
+GITHEAD=`git rev-parse --short HEAD`
+
+mv $APP.dmg "$APP-$RSVERSION-$GITHEAD-$DATE-MacOS-$MACVERSION-$QTVERSION.dmg"
+
+# You can use the appdmg command line app to create your dmg file if
+# you want to use a custom background and icon arrangement. I'm still
+# working on this for my apps, myself. If you want to do this, you'll
+# remove the -dmg option above.
+# appdmg json-path YourApp_${TRAVIS_TAG}.dmg
+
+# Copy other project files
+cp "../../libbitdht/src/bitdht/bdboot.txt" "retroshare.app/Contents/Resources/"
+cp "../../plugins/FeedReader/lib/libFeedReader.dylib" "retroshare.app/Contents/Resources/"
+cp -R "sounds" "retroshare.app/Contents/Resources/sounds"
+
+# cp "${project_dir}/README.md" "README.md"
+# cp "${project_dir}/LICENSE" "LICENSE"
+# cp "${project_dir}/Qt License" "Qt License"
diff --git a/build_scripts/Windows-msys2/build/build.bat b/build_scripts/Windows-msys2/build/build.bat
index 4d366f28c..4fdaff09f 100644
--- a/build_scripts/Windows-msys2/build/build.bat
+++ b/build_scripts/Windows-msys2/build/build.bat
@@ -32,6 +32,16 @@ if not "%ParamNoupdate%"=="1" (
if "%ParamIndexing%"=="1" %EnvMSYS2Cmd% "pacman --noconfirm --needed -S mingw-w64-%RsMSYS2Architecture%-xapian-core mingw-w64-%RsMSYS2Architecture%-libvorbis mingw-w64-%RsMSYS2Architecture%-flac mingw-w64-%RsMSYS2Architecture%-taglib"
)
+:: Fix webui compilation (TODO: remove when whole RS switched to cmake)
+if "%ParamWebui%"=="1" (
+ pushd "%SourcePath%"
+ copy "%SourcePath%\libretroshare\src\jsonapi\jsonapi-generator-doxygen.conf" "%SourcePath%\jsonapi-generator\src\jsonapi-generator-doxygen.conf" %Quite%
+ copy "%SourcePath%\libretroshare\src\jsonapi\async-method-wrapper-template.cpp.tmpl" "%SourcePath%\jsonapi-generator\src\async-method-wrapper-template.cpp.tmpl" %Quite%
+ copy "%SourcePath%\libretroshare\src\jsonapi\method-wrapper-template.cpp.tmpl" "%SourcePath%\jsonapi-generator\src\method-wrapper-template.cpp.tmpl" %Quite%
+ git update-index --assume-unchanged "jsonapi-generator\src\jsonapi-generator-doxygen.conf" "jsonapi-generator\src\async-method-wrapper-template.cpp.tmpl" "jsonapi-generator\src\method-wrapper-template.cpp.tmpl"
+ popd
+)
+
:: Initialize environment
call "%~dp0env.bat" %*
if errorlevel 2 exit /B 2
@@ -66,6 +76,7 @@ echo %RS_QMAKE_CONFIG% > buildinfo.txt
echo %RsBuildConfig% >> buildinfo.txt
echo %RsArchitecture% >> buildinfo.txt
echo Qt %QtVersion% >> buildinfo.txt
+echo %RsToolchain% >> buildinfo.txt
echo %RsCompiler% >> buildinfo.txt
call "%ToolsPath%\msys2-path.bat" "%SourcePath%" MSYS2SourcePath
@@ -86,11 +97,6 @@ title Build - %SourceName%-%RsBuildConfig% [make]
%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 9d9fa5379..01a7a8f12 100644
--- a/build_scripts/Windows-msys2/build/env-base.bat
+++ b/build_scripts/Windows-msys2/build/env-base.bat
@@ -1,6 +1,4 @@
:: Process commandline parameter
-set Param32=0
-set Param64=0
set ParamRelease=0
set ParamDebug=0
set ParamAutologin=0
@@ -9,17 +7,40 @@ set ParamTor=0
set ParamWebui=0
set ParamClang=0
set ParamIndexing=0
+set ParamFriendserver=0
set ParamNoupdate=0
set CoreCount=%NUMBER_OF_PROCESSORS%
set RS_QMAKE_CONFIG=
+set RsToolchain=
+set tcc=0
:parameter_loop
if "%~1" NEQ "" (
for /f "tokens=1,2 delims==" %%a in ("%~1") do (
if "%%~a"=="32" (
- set Param32=1
+ set RsToolchain=mingw32
+ set /A tcc=tcc+1
) else if "%%~a"=="64" (
- set Param64=1
+ set RsToolchain=mingw64
+ set /A tcc=tcc+1
+ ) else if "%%~a"=="mingw32" (
+ set RsToolchain=mingw32
+ set /A tcc=tcc+1
+ ) else if "%%~a"=="mingw64" (
+ set RsToolchain=mingw64
+ set /A tcc=tcc+1
+ ) else if "%%~a"=="ucrt64" (
+ set RsToolchain=ucrt64
+ set /A tcc=tcc+1
+ ) else if "%%~a"=="clang64" (
+ set RsToolchain=clang64
+ set /A tcc=tcc+1
+ ) else if "%%~a"=="clang32" (
+ set RsToolchain=clang32
+ set /A tcc=tcc+1
+ ) else if "%%~a"=="clangarm64" (
+ set RsToolchain=clangarm64
+ set /A tcc=tcc+1
) else if "%%~a"=="release" (
set ParamRelease=1
) else if "%%~a"=="debug" (
@@ -38,6 +59,8 @@ if "%~1" NEQ "" (
set ParamClang=1
) else if "%%~a"=="indexing" (
set ParamIndexing=1
+ ) else if "%%~a"=="friendserver" (
+ set ParamFriendserver=1
) else if "%%~a"=="noupdate" (
set ParamNoupdate=1
) else if "%%~a"=="CONFIG+" (
@@ -52,22 +75,37 @@ if "%~1" NEQ "" (
goto parameter_loop
)
-if "%Param32%"=="1" (
- if "%Param64%"=="1" (
- echo.
- echo 32-bit or 64-bit?
- goto :usage
- )
-
- set RsBit=32
- set RsArchitecture=x86
- set RsMSYS2Architecture=i686
+if %tcc% NEQ 1 (
+ echo Multiple or no toolchain specified
+ goto :usage
)
-if "%Param64%"=="1" (
- set RsBit=64
+if "%RsToolchain%"=="mingw32" (
+ set RsArchitecture=x86
+ set RsMSYS2Architecture=i686
+ set MSYSTEM=MINGW32
+) else if "%RsToolchain%"=="mingw64" (
set RsArchitecture=x64
set RsMSYS2Architecture=x86_64
+ set MSYSTEM=MINGW64
+) else if "%RsToolchain%"=="ucrt64" (
+ set RsArchitecture=x64
+ set RsMSYS2Architecture=ucrt-x86_64
+ set MSYSTEM=UCRT64
+) else if "%RsToolchain%"=="clang64" (
+ set RsArchitecture=x64
+ set RsMSYS2Architecture=clang-x86_64
+ set MSYSTEM=CLANG64
+ set ParamClang=1
+) else if "%RsToolchain%"=="clang32" (
+ set RsArchitecture=x86
+ set RsMSYS2Architecture=clang-i686
+ set MSYSTEM=CLANG32
+ set ParamClang=1
+) else if "%RsToolchain%"=="clangarm64" (
+ set RsArchitecture=arm64
+ set RsMSYS2Architecture=clang-aarch64
+ set MSYSTEM=CLANGARM64
)
if "%ParamClang%"=="1" (
@@ -76,8 +114,6 @@ if "%ParamClang%"=="1" (
set RsCompiler=GCC
)
-if "%RsBit%"=="" goto :usage
-
if "%ParamRelease%"=="1" (
if "%ParamDebug%"=="1" (
echo.
@@ -104,14 +140,21 @@ 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"
)
+if "%ParamFriendserver%"=="1" (
+ set RS_QMAKE_CONFIG=%RS_QMAKE_CONFIG% "CONFIG+=rs_efs"
+)
+
exit /B 0
:usage
echo.
-echo Usage: 32^|64 release^|debug [autologin plugins webui singlethread clang indexing noupdate] ["CONFIG+=..."]
+echo Usage: 32^|64^|other release^|debug [autologin plugins webui singlethread clang indexing friendserver noupdate] ["CONFIG+=..."]
echo.
echo Mandatory parameter
-echo 32^|64 32-bit or 64-bit Version
+echo 32^|64 32-bit or 64-bit version (same as mingw32 or mingw64)
+echo Or you can specify any other toolchain supported by msys2:
+echo mingw32^|mingw64^|clang32^|clang64^|ucrt64^|clangarm64
+echo More info: https://www.msys2.org/docs/environments
echo release^|debug Build release or debug version
echo.
echo Optional parameter (need clean when changed)
@@ -121,6 +164,7 @@ 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 friendserver Enable friendserver support
echo noupdate Skip updating the libraries
echo "CONFIG+=..." Enable some extra features, you can find the almost complete list in retroshare.pri
echo.
diff --git a/build_scripts/Windows-msys2/build/env.bat b/build_scripts/Windows-msys2/build/env.bat
index bfe2afeba..ccba33db3 100644
--- a/build_scripts/Windows-msys2/build/env.bat
+++ b/build_scripts/Windows-msys2/build/env.bat
@@ -2,8 +2,6 @@ call "%~dp0env-base.bat" %*
if errorlevel 2 exit /B 2
if errorlevel 1 goto error_env
-set MSYSTEM=MINGW%RsBit%
-
set BuildPath=%EnvRootPath%\builds
set DeployPath=%EnvRootPath%\deploy
@@ -14,13 +12,13 @@ if not exist "%DeployPath%" mkdir "%DeployPath%"
call "%ToolsPath%\get-qt-version.bat" QtVersion
if "%QtVersion%"=="" %cecho% error "Cannot get Qt version." & exit /B 1
-set RsMinGWPath=%EnvMSYS2BasePath%\mingw%RsBit%
+set RsMinGWPath=%EnvMSYS2BasePath%\%RsToolchain%
-set RsBuildPath=%BuildPath%\Qt-%QtVersion%-%RsArchitecture%-%RsCompiler%-%RsBuildConfig%
-set RsDeployPath=%DeployPath%\Qt-%QtVersion%%RsType%-%RsArchitecture%-%RsCompiler%-%RsBuildConfig%
+set RsBuildPath=%BuildPath%\Qt-%QtVersion%-%RsToolchain%-%RsCompiler%-%RsBuildConfig%
+set RsDeployPath=%DeployPath%\Qt-%QtVersion%%RsType%-%RsToolchain%-%RsCompiler%-%RsBuildConfig%
set RsPackPath=%DeployPath%
set RsArchiveAdd=
-set RsWebuiPath=%RootPath%\%SourceName%-webui
+set RsWebuiBuildPath=%RsBuildPath%\retroshare-webui\webui
if not exist "%~dp0env-mod.bat" goto no_mod
call "%~dp0env-mod.bat"
diff --git a/build_scripts/Windows-msys2/build/pack.bat b/build_scripts/Windows-msys2/build/pack.bat
index 4948c1541..313da7479 100644
--- a/build_scripts/Windows-msys2/build/pack.bat
+++ b/build_scripts/Windows-msys2/build/pack.bat
@@ -105,6 +105,9 @@ copy "%RsBuildPath%\retroshare-nogui\src\%RsBuildConfig%\retroshare*-nogui.exe"
copy "%RsBuildPath%\retroshare-service\src\%RsBuildConfig%\retroshare*-service.exe" "%RsDeployPath%" %Quite%
copy "%RsBuildPath%\supportlibs\cmark\build\src\libcmark.dll" "%RsDeployPath%" %Quite%
if exist "%RsBuildPath%\libretroshare\src\lib\retroshare.dll" copy "%RsBuildPath%\libretroshare\src\lib\retroshare.dll" "%RsDeployPath%" %Quite%
+if exist "%RsBuildPath%\retroshare-friendserver\src\%RsBuildConfig%\retroshare-friendserver.exe" (
+ copy "%RsBuildPath%\retroshare-friendserver\src\%RsBuildConfig%\retroshare-friendserver.exe" "%RsDeployPath%" %Quite%
+)
echo copy extensions
for /D %%D in ("%RsBuildPath%\plugins\*") do (
@@ -184,12 +187,12 @@ echo copy buildinfo.txt
copy "%RsBuildPath%\buildinfo.txt" "%RsDeployPath%" %Quite%
if "%ParamWebui%"=="1" (
- if exist "%RsWebuiPath%\webui" (
+ if exist "%RsWebuiBuildPath%" (
echo copy webui
mkdir "%RsDeployPath%\webui"
- xcopy /S "%RsWebuiPath%\webui" "%RsDeployPath%\webui" %Quite%
+ xcopy /S "%RsWebuiBuildPath%" "%RsDeployPath%\webui" %Quite%
) else (
- %cecho% error "Webui is enabled, but no webui data found at %RsWebuiPath%\webui"
+ %cecho% error "Webui is enabled, but no webui data found at %RsWebuiBuildPath%"
goto error
)
)
diff --git a/build_scripts/Windows-msys2/env/tools/prepare-msys2.bat b/build_scripts/Windows-msys2/env/tools/prepare-msys2.bat
index b1e0c4187..eaeba1482 100644
--- a/build_scripts/Windows-msys2/env/tools/prepare-msys2.bat
+++ b/build_scripts/Windows-msys2/env/tools/prepare-msys2.bat
@@ -16,7 +16,13 @@ if "%~1"=="clean" (
goto exit
)
-if exist "%EnvMSYS2Path%\msys%MSYS2Base%\usr\bin\pacman.exe" (
+set MSYS2Version=20231026
+
+set MSYS2Install=msys2-base-x86_64-%MSYS2Version%.sfx.exe
+set MSYS2Url=https://github.com/msys2/msys2-installer/releases/download/%MSYS2Version:~0,4%-%MSYS2Version:~4,2%-%MSYS2Version:~6,2%/%MSYS2Install%
+set MSYS2UnpackPath=%EnvMSYS2Path%\msys64
+
+if exist "%MSYS2UnpackPath%\usr\bin\pacman.exe" (
if "%~1"=="reinstall" (
choice /M "Found existing MSYS2 version. Do you want to proceed?"
if !ERRORLEVEL!==2 goto exit
@@ -25,29 +31,26 @@ if exist "%EnvMSYS2Path%\msys%MSYS2Base%\usr\bin\pacman.exe" (
)
)
-if "%MSYS2Architecture%"=="i686" set MSYS2Version=20210705
-if "%MSYS2Architecture%"=="x86_64" set MSYS2Version=20210725
+if exist "%MSYS2UnpackPath%" (
+ %cecho% info "Remove previous MSYS2 version"
+ call "%ToolsPath%\remove-dir.bat" "%MSYS2UnpackPath%"
+)
-set MSYS2Install=msys2-base-%MSYS2Architecture%-%MSYS2Version%.tar.xz
-set MSYS2Url=https://repo.msys2.org/distrib/%MSYS2Architecture%/%MSYS2Install%
-
-%cecho% info "Remove previous MSYS2 version"
-call "%ToolsPath%\remove-dir.bat" "%EnvMSYS2Path%"
-
-%cecho% info "Download installation files"
+%cecho% info "Download MSYS2 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
%cecho% info "Unpack MSYS2"
-"%EnvSevenZipExe%" x -so "%EnvDownloadPath%\%MSYS2Install%" | "%EnvSevenZipExe%" x -y -si -ttar -o"%EnvMSYS2Path%"
+"%EnvDownloadPath%\%MSYS2Install%" -y -o"%EnvMSYS2Path%"
-set MSYS2SH=%EnvMSYS2Path%\msys%MSYS2Base%\usr\bin\sh
+set MSYS2SH=%MSYS2UnpackPath%\usr\bin\sh
%cecho% info "Initialize MSYS2"
"%MSYS2SH%" -lc "yes | pacman --noconfirm -Syuu msys2-keyring"
+"%MSYS2SH%" -lc "pacman --noconfirm -Sy"
"%MSYS2SH%" -lc "pacman --noconfirm -Su"
-call "%EnvMSYS2Path%\msys%MSYS2Base%\autorebase.bat"
+call "%MSYS2UnpackPath%\autorebase.bat"
:exit
endlocal
diff --git a/build_scripts/Windows-msys2/env/tools/prepare-tools.bat b/build_scripts/Windows-msys2/env/tools/prepare-tools.bat
index c6b14a787..66f5f9120 100644
--- a/build_scripts/Windows-msys2/env/tools/prepare-tools.bat
+++ b/build_scripts/Windows-msys2/env/tools/prepare-tools.bat
@@ -34,7 +34,7 @@ if not exist "%EnvToolsPath%\cecho.exe" (
if not exist "%EnvDownloadPath%\%cCEhoInstall%" echo Cannot download cecho installation& goto error
echo Unpack cecho
- "%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%CEchoInstall%"
+ "%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%CEchoInstall%" -y -bso0
copy "%EnvTempPath%\cecho.exe" "%EnvToolsPath%"
call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%"
diff --git a/build_scripts/Windows-msys2/env/tools/root/update-msys2.bat b/build_scripts/Windows-msys2/env/tools/root/update-msys2.bat
index a3d2398cd..ddd5bdd0f 100644
--- a/build_scripts/Windows-msys2/env/tools/root/update-msys2.bat
+++ b/build_scripts/Windows-msys2/env/tools/root/update-msys2.bat
@@ -2,18 +2,13 @@
setlocal
-if exist "%~dp0msys2\msys32" call :update 32
-if exist "%~dp0msys2\msys64" call :update 64
+if not exist "%~dp0msys2\msys64" goto :EOF
-goto :EOF
+set MSYS2SH=%~dp0msys2\msys64\usr\bin\sh
-:update
-set MSYS2SH=%~dp0msys2\msys%~1\usr\bin\sh
-
-echo Update MSYS2 %~1
+echo Update MSYS2
"%MSYS2SH%" -lc "yes | pacman --noconfirm -Syuu msys2-keyring"
"%MSYS2SH%" -lc "pacman --noconfirm -Su"
-:exit
endlocal
goto :EOF
diff --git a/build_scripts/Windows-msys2/tools/webui.bat b/build_scripts/Windows-msys2/tools/webui.bat
deleted file mode 100644
index b7af01e53..000000000
--- a/build_scripts/Windows-msys2/tools/webui.bat
+++ /dev/null
@@ -1,22 +0,0 @@
-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/build-libs/Makefile b/build_scripts/Windows/build-libs/Makefile
index 8bab46b92..0075770cd 100644
--- a/build_scripts/Windows/build-libs/Makefile
+++ b/build_scripts/Windows/build-libs/Makefile
@@ -1,18 +1,18 @@
-ZLIB_VERSION=1.2.3
-BZIP2_VERSION=1.0.6
-MINIUPNPC_VERSION=2.0
-OPENSSL_VERSION=1.1.1h
+ZLIB_VERSION=1.2.11
+BZIP2_VERSION=1.0.8
+MINIUPNPC_VERSION=2.2.3
+OPENSSL_VERSION=1.1.1w
SPEEX_VERSION=1.2.0
-SPEEXDSP_VERSION=1.2rc3
-LIBXML2_VERSION=2.9.7
-LIBXSLT_VERSION=1.1.32
-CURL_VERSION=7.58.0
+SPEEXDSP_VERSION=1.2.0
+LIBXML2_VERSION=2.9.12
+LIBXSLT_VERSION=1.1.34
+CURL_VERSION=7.81.0
TCL_VERSION=8.6.10
-SQLCIPHER_VERSION=4.4.0
-LIBMICROHTTPD_VERSION=0.9.59
-FFMPEG_VERSION=3.4
+SQLCIPHER_VERSION=4.5.0
+LIBMICROHTTPD_VERSION=0.9.75
+FFMPEG_VERSION=4.4
RAPIDJSON_VERSION=1.1.0
-XAPIAN_VERSION=1.4.7
+XAPIAN_VERSION=1.4.19
DOWNLOAD_PATH?=download
BUILD_PATH=build
@@ -56,8 +56,7 @@ $(BUILD_PATH)/zlib-$(ZLIB_VERSION): $(DOWNLOAD_PATH)/zlib-$(ZLIB_VERSION).tar.gz
rm -r -f $(BUILD_PATH)/zlib-*
tar xvf $(DOWNLOAD_PATH)/zlib-$(ZLIB_VERSION).tar.gz
# build
- cd zlib-$(ZLIB_VERSION) && ./configure
- #cd zlib-$(ZLIB_VERSION) && make install prefix="`pwd`/../$(BUILD_PATH)"
+ cd zlib-$(ZLIB_VERSION) && make -f win32/Makefile.gcc libz.a
cd zlib-$(ZLIB_VERSION) && make
# copy files
mkdir -p $(BUILD_PATH)/zlib-$(ZLIB_VERSION).tmp/include
@@ -72,7 +71,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 https://sourceforge.net/projects/bzip2/files/bzip2-$(BZIP2_VERSION).tar.gz/download -O $(DOWNLOAD_PATH)/bzip2-$(BZIP2_VERSION).tar.gz
+ wget https://www.sourceware.org/pub/bzip2/bzip2-$(BZIP2_VERSION).tar.gz -O $(DOWNLOAD_PATH)/bzip2-$(BZIP2_VERSION).tar.gz
$(BUILD_PATH)/bzip2-$(BZIP2_VERSION): $(DOWNLOAD_PATH)/bzip2-$(BZIP2_VERSION).tar.gz
# prepare
@@ -100,10 +99,10 @@ $(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 && export PATH=.:$$PATH && make -f Makefile.mingw init libminiupnpc.a miniupnpc.dll
+ cd miniupnpc-$(MINIUPNPC_VERSION) && export CC=gcc && export PATH=.:$$PATH && make -f Makefile.mingw 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/
+ cp miniupnpc-$(MINIUPNPC_VERSION)/include/*.h $(BUILD_PATH)/miniupnpc-$(MINIUPNPC_VERSION).tmp/include/miniupnpc/
mkdir -p $(BUILD_PATH)/miniupnpc-$(MINIUPNPC_VERSION).tmp/lib
cp miniupnpc-$(MINIUPNPC_VERSION)/miniupnpc.lib $(BUILD_PATH)/miniupnpc-$(MINIUPNPC_VERSION).tmp/lib/
mkdir -p $(BUILD_PATH)/miniupnpc-$(MINIUPNPC_VERSION).tmp/bin
@@ -314,7 +313,7 @@ $(BUILD_PATH)/ffmpeg-$(FFMPEG_VERSION): $(DOWNLOAD_PATH)/ffmpeg-$(FFMPEG_VERSION
rm -r -f $(BUILD_PATH)/ffmpeg-*
tar xvf $(DOWNLOAD_PATH)/ffmpeg-$(FFMPEG_VERSION).tar.gz
# build
- cd ffmpeg-$(FFMPEG_VERSION) && ./configure --disable-shared --enable-static --disable-programs --disable-ffmpeg --disable-ffplay --disable-ffprobe --disable-ffserver --disable-doc --disable-htmlpages --disable-manpages --disable-podpages --disable-txtpages --disable-yasm --disable-everything --enable-encoder=mpeg4 --enable-decoder=mpeg4 --prefix="`pwd`/../$(BUILD_PATH)/ffmpeg-$(FFMPEG_VERSION).tmp"
+ cd ffmpeg-$(FFMPEG_VERSION) && ./configure --disable-shared --enable-static --disable-programs --disable-ffmpeg --disable-ffplay --disable-ffprobe --disable-doc --disable-htmlpages --disable-manpages --disable-podpages --disable-txtpages --disable-yasm --disable-everything --enable-encoder=mpeg4 --enable-decoder=mpeg4 --prefix="`pwd`/../$(BUILD_PATH)/ffmpeg-$(FFMPEG_VERSION).tmp"
cd ffmpeg-$(FFMPEG_VERSION) && make install
# cleanup
rm -r -f ffmpeg-$(FFMPEG_VERSION)
diff --git a/build_scripts/Windows/build.bat b/build_scripts/Windows/build.bat
index 9dd51cc16..90bf2ae1c 100644
--- a/build_scripts/Windows/build.bat
+++ b/build_scripts/Windows/build.bat
@@ -13,11 +13,11 @@ 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 jsonapi plugins nativedialogs
+call "%~dp0build\build.bat" release autologin webui plugins nativedialogs service
if errorlevel 1 %cecho% error "Failed to build %SourceName%." & exit /B %ERRORLEVEL%
%cecho% info "Pack %SourceName%"
-call "%~dp0build\pack.bat" release plugins
+call "%~dp0build\pack.bat" release webui plugins service
if errorlevel 1 %cecho% error "Failed to pack %SourceName%." & exit /B %ERRORLEVEL%
%cecho% info "Build installer"
diff --git a/build_scripts/Windows/build/build-installer.bat b/build_scripts/Windows/build/build-installer.bat
index b34e76157..7209afb6e 100644
--- a/build_scripts/Windows/build/build-installer.bat
+++ b/build_scripts/Windows/build/build-installer.bat
@@ -9,7 +9,7 @@ call "%EnvPath%\env.bat"
if errorlevel 1 goto error_env
:: Initialize environment
-call "%~dp0env.bat" release
+call "%~dp0env.bat" installer release
if errorlevel 2 exit /B 2
if errorlevel 1 goto error_env
@@ -40,6 +40,7 @@ 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"
+if exist "%RsWebuiBuildPath%" set NSIS_PARAM=%NSIS_PARAM% /DWEBUIDIR="%RsWebuiBuildPath%"
:: 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 095fc6081..91ff71382 100644
--- a/build_scripts/Windows/build/build.bat
+++ b/build_scripts/Windows/build/build.bat
@@ -9,7 +9,7 @@ call "%EnvPath%\env.bat"
if errorlevel 1 goto error_env
:: Initialize environment
-call "%~dp0env.bat" %*
+call "%~dp0env.bat" build %*
if errorlevel 2 exit /B 2
if errorlevel 1 goto error_env
@@ -49,11 +49,15 @@ echo.
title Build - %SourceName%-%RsBuildConfig% [qmake]
-set RS_QMAKE_CONFIG=%RsBuildConfig% no_rs_cppwarning
+set RS_QMAKE_CONFIG=%RsBuildConfig%
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 "%ParamWebui%"=="1" set RS_QMAKE_CONFIG=%RS_QMAKE_CONFIG% rs_webui
if "%ParamPlugins%"=="1" set RS_QMAKE_CONFIG=%RS_QMAKE_CONFIG% retroshare_plugins
if "%ParamUseNativeDialogs%"=="1" set RS_QMAKE_CONFIG=%RS_QMAKE_CONFIG% rs_use_native_dialogs
+if "%ParamService%" NEQ "1" set RS_QMAKE_CONFIG=%RS_QMAKE_CONFIG% no_retroshare_service
+if "%ParamFriendServer%" NEQ "1" set RS_QMAKE_CONFIG=%RS_QMAKE_CONFIG% no_retroshare_friendserver
+if "%ParamEmbeddedFriendServer%"=="1" set RS_QMAKE_CONFIG=%RS_QMAKE_CONFIG% rs_efs
qmake "%SourcePath%\RetroShare.pro" -r -spec win32-g++ "CONFIG+=%RS_QMAKE_CONFIG%" "EXTERNAL_LIB_DIR=%BuildLibsPath%\libs"
if errorlevel 1 goto error
diff --git a/build_scripts/Windows/build/clean.bat b/build_scripts/Windows/build/clean.bat
index e8d5aa7b8..4ab752d4d 100644
--- a/build_scripts/Windows/build/clean.bat
+++ b/build_scripts/Windows/build/clean.bat
@@ -8,7 +8,7 @@ if errorlevel 1 goto error_env
call "%EnvPath%\env.bat"
if errorlevel 1 goto error_env
-call "%~dp0env.bat" %*
+call "%~dp0env.bat" clean %*
if errorlevel 2 exit /B 2
if errorlevel 1 goto error_env
diff --git a/build_scripts/Windows/build/env.bat b/build_scripts/Windows/build/env.bat
index 883077173..98f70c87f 100644
--- a/build_scripts/Windows/build/env.bat
+++ b/build_scripts/Windows/build/env.bat
@@ -4,11 +4,18 @@ set ParamDebug=0
set ParamAutologin=0
set ParamPlugins=0
set ParamJsonApi=0
+set ParamWebui=0
+set ParamService=0
+set ParamFriendServer=0
+set ParamEmbeddedFriendServer=0
set ParamUseNativeDialogs=0
set ParamTor=0
set NonInteractive=0
set CoreCount=%NUMBER_OF_PROCESSORS%
+set Module=%~1
+shift /1
+
:parameter_loop
if "%~1" NEQ "" (
for /f "tokens=1,2 delims==" %%a in ("%~1") do (
@@ -20,6 +27,15 @@ if "%~1" NEQ "" (
set ParamAutologin=1
) else if "%%~a"=="jsonapi" (
set ParamJsonApi=1
+ ) else if "%%~a"=="webui" (
+ set ParamJsonApi=1
+ set ParamWebui=1
+ ) else if "%%~a"=="service" (
+ set ParamService=1
+ ) else if "%%~a"=="friendserver" (
+ set ParamFriendServer=1
+ ) else if "%%~a"=="embedded-friendserver" (
+ set ParamEmbeddedFriendServer=1
) else if "%%~a"=="plugins" (
set ParamPlugins=1
) else if "%%~a"=="tor" (
@@ -32,7 +48,7 @@ if "%~1" NEQ "" (
set ParamUseNativeDialogs=1
) else (
echo.
- echo Unknown parameter %1
+ echo Unknown parameter %1 for %Module%
goto :usage
)
)
@@ -89,6 +105,7 @@ set RsBuildPath=%BuildPath%\Qt-%QtVersion%-%GCCArchitecture%-%RsBuildConfig%
set RsDeployPath=%DeployPath%\Qt-%QtVersion%-%GCCArchitecture%%RsType%-%RsBuildConfig%
set RsPackPath=%DeployPath%
set RsArchiveAdd=
+set RsWebuiBuildPath=%RsBuildPath%\retroshare-webui\webui
if not exist "%~dp0env-mod.bat" goto no_mod
call "%~dp0env-mod.bat"
@@ -102,21 +119,33 @@ echo.
echo Usage: release^|debug [^]
echo.
echo Mandatory parameter
-echo release^|debug Build release or debug version
+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 nativedialogs Build with native dialogs
-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
+if "%Module%"=="build" (
+ echo Optional parameter ^(need clean when changed^)
+ echo autologin Build with autologin
+ echo jsonapi Build with jsonapi
+ echo webui Build with jsonapi and webui
+ echo service Build service
+ echo friendserver Build Friend Server
+ echo embedded-friendserver Build with embedded Friend Server
+ echo plugins Build plugins
+ echo nativedialogs Build with native dialogs
+ echo.
+ echo Optional parameter
+ echo singlethread Use only 1 thread for building
+)
+if "%Module%"=="pack" (
+ echo Optional parameter
+ echo webui Pack webui
+ echo service Pack service
+ echo friendserver Pack Friend Server ^(needs Tor^)
+ echo tor Pack Tor version
+ echo plugins Pack plugins
+)
+if "%Module%"=="git-log" (
+ echo Optional parameter
+ 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 4d479fc3f..b2328d853 100644
--- a/build_scripts/Windows/build/git-log.bat
+++ b/build_scripts/Windows/build/git-log.bat
@@ -8,7 +8,7 @@ if errorlevel 1 goto error_env
call "%EnvPath%\env.bat"
if errorlevel 1 goto error_env
-call "%~dp0env.bat" %*
+call "%~dp0env.bat" git-log %*
if errorlevel 2 exit /B 2
if errorlevel 1 goto error_env
@@ -88,6 +88,10 @@ echo %RsRef%>"%RsLastRefFile%"
exit /B %ERRORLEVEL%
+:error
+%cecho% error "\n%~n0 failed\n"
+exit /B 1
+
:error_env
echo Failed to initialize environment.
endlocal
diff --git a/build_scripts/Windows/build/pack.bat b/build_scripts/Windows/build/pack.bat
index 2a86b9a16..1c844efa0 100644
--- a/build_scripts/Windows/build/pack.bat
+++ b/build_scripts/Windows/build/pack.bat
@@ -11,7 +11,7 @@ call "%EnvPath%\env.bat"
if errorlevel 1 goto error_env
:: Initialize environment
-call "%~dp0env.bat" %*
+call "%~dp0env.bat" pack %*
if errorlevel 2 exit /B 2
if errorlevel 1 goto error_env
@@ -95,9 +95,23 @@ copy nul "%RsDeployPath%\portable" %Quite%
echo copy binaries
copy "%RsBuildPath%\retroshare-gui\src\%RsBuildConfig%\retroshare*.exe" "%RsDeployPath%" %Quite%
-copy "%RsBuildPath%\retroshare-service\src\%RsBuildConfig%\retroshare*-service.exe" "%RsDeployPath%" %Quite%
if exist "%RsBuildPath%\libretroshare\src\lib\retroshare.dll" copy "%RsBuildPath%\libretroshare\src\lib\retroshare.dll" "%RsDeployPath%" %Quite%
+if "%ParamService%"=="1" (
+ copy "%RsBuildPath%\retroshare-service\src\%RsBuildConfig%\retroshare*-service.exe" "%RsDeployPath%" %Quite%
+ if errorlevel 1 %cecho% error "Service not found"& goto error
+)
+
+if "%ParamFriendServer%"=="1" (
+ if "%ParamTor%"=="1" (
+ copy "%RsBuildPath%\retroshare-friendserver\src\%RsBuildConfig%\retroshare-friendserver.exe" "%RsDeployPath%" %Quite%
+ if errorlevel 1 %cecho% error "Friend Server not found"& goto error
+ ) else (
+ %cecho% error "Friend Server needs Tor"
+ goto error
+ )
+)
+
echo copy extensions
if "%ParamPlugins%"=="1" (
for /D %%D in ("%RsBuildPath%\plugins\*") do (
@@ -167,10 +181,11 @@ copy "%SourcePath%\libbitdht\src\bitdht\bdboot.txt" "%RsDeployPath%" %Quite%
echo copy changelog.txt
copy "%RsBuildPath%\changelog.txt" "%RsDeployPath%" %Quite%
-if exist "%SourcePath%\libresapi\src\webui" (
+if defined ParamWebui (
echo copy webui
mkdir "%RsDeployPath%\webui"
- xcopy /S "%SourcePath%\libresapi\src\webui" "%RsDeployPath%\webui" %Quite%
+ xcopy /S "%RsWebuiBuildPath%" "%RsDeployPath%\webui" %Quite%
+ if errorlevel 1 %cecho% error "WebUi not found"& goto error
)
if "%ParamTor%"=="1" (
diff --git a/build_scripts/Windows/env/env-msys2.bat b/build_scripts/Windows/env/env-msys2.bat
index b99ede679..47dd8d96a 100644
--- a/build_scripts/Windows/env/env-msys2.bat
+++ b/build_scripts/Windows/env/env-msys2.bat
@@ -5,7 +5,6 @@
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
@@ -23,11 +22,12 @@ if "%GCCArchitecture%"=="x64" (
)
set EnvMSYS2Path=%EnvRootPath%\msys2
+set EnvMSYS2BasePath=%EnvMSYS2Path%\msys64
call "%~dp0tools\prepare-msys2.bat" %1
if errorlevel 1 exit /B %ERRORLEVEL%
-set EnvMSYS2SH=%EnvMSYS2Path%\msys%MSYS2Base%\usr\bin\sh.exe
+set EnvMSYS2SH=%EnvMSYS2BasePath%\usr\bin\sh.exe
if not exist "%EnvMSYS2SH%" if errorlevel 1 goto error_env
set EnvMSYS2Cmd="%EnvMSYS2SH%" -lc
diff --git a/build_scripts/Windows/env/env.bat b/build_scripts/Windows/env/env.bat
index bc95045b0..185a421a9 100644
--- a/build_scripts/Windows/env/env.bat
+++ b/build_scripts/Windows/env/env.bat
@@ -28,6 +28,9 @@ if errorlevel 1 exit /B %ERRORLEVEL%
set PATH=%EnvToolsPath%\MinGit\cmd;%EnvToolsPath%\cmake\bin;%PATH%
set HOME=%EnvToolsPath%\MinGit\home
+:: Add Doxygen to PATH
+set PATH=%EnvToolsPath%\doxygen;%PATH%
+
exit /B 0
:error_env
diff --git a/build_scripts/Windows/env/tools/prepare-msys2.bat b/build_scripts/Windows/env/tools/prepare-msys2.bat
index a0483289a..25f00ac30 100644
--- a/build_scripts/Windows/env/tools/prepare-msys2.bat
+++ b/build_scripts/Windows/env/tools/prepare-msys2.bat
@@ -16,7 +16,15 @@ if "%~1"=="clean" (
goto exit
)
-if exist "%EnvMSYS2Path%\msys%MSYS2Base%\usr\bin\pacman.exe" (
+set MSYS2Version=20231026
+
+set MSYS2Install=msys2-base-x86_64-%MSYS2Version%.sfx.exe
+set MSYS2Url=https://github.com/msys2/msys2-installer/releases/download/%MSYS2Version:~0,4%-%MSYS2Version:~4,2%-%MSYS2Version:~6,2%/%MSYS2Install%
+set MSYS2UnpackPath=%EnvMSYS2Path%\msys64
+set CMakeInstall=cmake-3.19.0-win32-x86.zip
+set CMakeUrl=https://github.com/Kitware/CMake/releases/download/v3.19.0/%CMakeInstall%
+
+if exist "%MSYS2UnpackPath%\usr\bin\pacman.exe" (
if "%~1"=="reinstall" (
choice /M "Found existing MSYS2 version. Do you want to proceed?"
if !ERRORLEVEL!==2 goto exit
@@ -25,21 +33,12 @@ if exist "%EnvMSYS2Path%\msys%MSYS2Base%\usr\bin\pacman.exe" (
)
)
-if "%MSYS2Architecture%"=="i686" set MSYS2Version=20210705
-if "%MSYS2Architecture%"=="x86_64" set MSYS2Version=20210725
-
-set MSYS2Install=msys2-base-%MSYS2Architecture%-%MSYS2Version%.tar.xz
-set MSYS2Url=https://repo.msys2.org/distrib/%MSYS2Architecture%/%MSYS2Install%
-set CMakeInstall=cmake-3.19.0-win32-x86.zip
-set CMakeUrl=https://github.com/Kitware/CMake/releases/download/v3.19.0/%CMakeInstall%
-set CMakeUnpackPath=%EnvMSYS2Path%\msys%MSYS2Base%
-
-if exist "%EnvMSYS2Path%\msys%MSYS2Base%" (
+if exist "%MSYS2UnpackPath%" (
%cecho% info "Remove previous MSYS2 version"
- call "%ToolsPath%\remove-dir.bat" "%EnvMSYS2Path%\msys%MSYS2Base%"
+ call "%ToolsPath%\remove-dir.bat" "%MSYS2UnpackPath%"
)
-%cecho% info "Download installation files"
+%cecho% info "Download MSYS2 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
@@ -47,31 +46,32 @@ if not exist "%EnvDownloadPath%\%CMakeInstall%" call "%ToolsPath%\download-file.
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%"
+"%EnvDownloadPath%\%MSYS2Install%" -y -o"%EnvMSYS2Path%"
%cecho% info "Unpack CMake"
-"%EnvSevenZipExe%" x -o"%CMakeUnpackPath%" "%EnvDownloadPath%\%CMakeInstall%"
+"%EnvSevenZipExe%" x -o"%MSYS2UnpackPath%" "%EnvDownloadPath%\%CMakeInstall%" -y -bso0
%cecho% info "Install CMake"
set CMakeVersion=
-for /D %%F in (%CMakeUnpackPath%\cmake*) do set CMakeVersion=%%~nxF
+for /D %%F in (%MSYS2UnpackPath%\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
+for /f "tokens=3" %%F in ('find /c /i "%CMakeVersion%" "%MSYS2UnpackPath%\etc\profile"') do set FoundProfile=%%F
if "%FoundProfile%"=="0" (
- echo export PATH="${PATH}:/%CMakeVersion%/bin">>"%EnvMSYS2Path%\msys%MSYS2Base%\etc\profile"
+ echo export PATH="${PATH}:/%CMakeVersion%/bin">>"%MSYS2UnpackPath%\etc\profile"
)
-set MSYS2SH=%EnvMSYS2Path%\msys%MSYS2Base%\usr\bin\sh
+set MSYS2SH=%MSYS2UnpackPath%\usr\bin\sh
%cecho% info "Initialize MSYS2"
"%MSYS2SH%" -lc "yes | pacman --noconfirm -Syuu msys2-keyring"
+"%MSYS2SH%" -lc "pacman --noconfirm -Sy"
"%MSYS2SH%" -lc "pacman --noconfirm -Su"
-call "%EnvMSYS2Path%\msys%MSYS2Base%\autorebase.bat"
+call "%MSYS2UnpackPath%\autorebase.bat"
:exit
endlocal
diff --git a/build_scripts/Windows/env/tools/prepare-tools.bat b/build_scripts/Windows/env/tools/prepare-tools.bat
index d53a70cf6..88549faed 100644
--- a/build_scripts/Windows/env/tools/prepare-tools.bat
+++ b/build_scripts/Windows/env/tools/prepare-tools.bat
@@ -16,6 +16,9 @@ set NSISInstallPath=%EnvToolsPath%\NSIS
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 DoxygenInstall=doxygen-1.9.6.windows.x64.bin.zip
+set DoxygenUrl=https://github.com/doxygen/doxygen/releases/download/Release_1_9_6/%DoxygenInstall%
+set DoxygenInstallPath=%EnvToolsPath%\doxygen
set CMakeVersion=cmake-3.19.0-win32-x86
set CMakeInstall=%CMakeVersion%.zip
set CMakeUrl=https://github.com/Kitware/CMake/releases/download/v3.19.0/%CMakeInstall%
@@ -50,7 +53,7 @@ if not exist "%EnvToolsPath%\cecho.exe" (
if not exist "%EnvDownloadPath%\%cCEhoInstall%" echo Cannot download cecho installation& goto error
echo Unpack cecho
- "%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%CEchoInstall%"
+ "%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%CEchoInstall%" -y -bso0
copy "%EnvTempPath%\cecho.exe" "%EnvToolsPath%"
call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%"
@@ -66,7 +69,7 @@ if not exist "%EnvToolsPath%\depends.exe" (
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%"
+ "%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%DependsInstall%" -y -bso0
copy "%EnvTempPath%\*" "%EnvToolsPath%"
call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%"
@@ -82,7 +85,7 @@ if not exist "%EnvToolsPath%\cut.exe" (
if not exist "%EnvDownloadPath%\%UnixToolsInstall%" %cecho% error "Cannot download Unix Tools installation" & goto error
%cecho% info "Unpack Unix Tools"
- "%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%UnixToolsInstall%"
+ "%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%UnixToolsInstall%" -y -bso0
copy "%EnvTempPath%\cut.exe" "%EnvToolsPath%"
call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%"
@@ -98,7 +101,7 @@ if not exist "%EnvToolsPath%\sed.exe" (
if not exist "%EnvDownloadPath%\%UnixToolsInstall%" %cecho% error "Cannot download Unix Tools installation" & goto error
%cecho% info "Unpack Unix Tools"
- "%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%UnixToolsInstall%"
+ "%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%UnixToolsInstall%" -y -bso0
copy "%EnvTempPath%\sed.exe" "%EnvToolsPath%"
call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%"
@@ -118,7 +121,7 @@ if not exist "%NSISInstallPath%\nsis.exe" (
if not exist "%EnvDownloadPath%\%NSISInstall%" %cecho% error "Cannot download NSIS installation" & goto error
%cecho% info "Unpack NSIS"
- "%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%NSISInstall%"
+ "%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%NSISInstall%" -y -bso0
if not exist "%NSISInstallPath%" mkdir "%NSISInstallPath%"
xcopy /s "%EnvTempPath%" "%NSISInstallPath%"
@@ -132,7 +135,20 @@ if not exist "%MinGitInstallPath%\cmd\git.exe" (
if not exist "%EnvDownloadPath%\%MinGitInstall%" %cecho% error "Cannot download MinGit installation" & goto error
%cecho% info "Unpack MinGit"
- "%EnvSevenZipExe%" x -o"%MinGitInstallPath%" "%EnvDownloadPath%\%MinGitInstall%"
+ "%EnvSevenZipExe%" x -o"%MinGitInstallPath%" "%EnvDownloadPath%\%MinGitInstall%" -y -bso0
+)
+
+if not exist "%EnvDownloadPath%\%DoxygenInstall%" call "%ToolsPath%\remove-dir.bat" "%DoxygenInstallPath%"
+if not exist "%DoxygenInstallPath%\doxygen.exe" (
+ if exist "%DoxygenInstallPath%" call "%ToolsPath%\remove-dir.bat" "%DoxygenInstallPath%"
+
+ %cecho% info "Download Doxygen installation"
+
+ if not exist "%EnvDownloadPath%\%DoxygenInstall%" call "%ToolsPath%\download-file.bat" "%DoxygenUrl%" "%EnvDownloadPath%\%DoxygenInstall%"
+ if not exist "%EnvDownloadPath%\%DoxygenInstall%" %cecho% error "Cannot download doxygen installation" & goto error
+
+ %cecho% info "Unpack Doxygen"
+ "%EnvSevenZipExe%" x -o"%DoxygenInstallPath%" "%EnvDownloadPath%\%DoxygenInstall%" -y -bso0
)
if not exist "%EnvDownloadPath%\%CMakeInstall%" call "%ToolsPath%\remove-dir.bat" "%CMakeInstallPath%"
@@ -147,7 +163,7 @@ if not exist "%CMakeInstallPath%\bin\cmake.exe" (
if not exist "%EnvDownloadPath%\%CMakeInstall%" %cecho% error "Cannot download CMake installation" & goto error
%cecho% info "Unpack CMake"
- "%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%CMakeInstall%"
+ "%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%CMakeInstall%" -y -bso0
move "%EnvTempPath%\%CMakeVersion%" "%CMakeInstallPath%"
@@ -160,7 +176,7 @@ 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
+for /F "tokens=1,2 delims= " %%A in ('%EnvSedExe% -r -n -e"s/.*href=\"^(.*^)^(tor-.*windows-i686.*\.tar\.gz^)\".*/\2 \1\2/p" "%EnvTempPath%\index.html"') do set TorInstall=%%A& set TorDownloadUrl=%%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
@@ -173,7 +189,7 @@ if not exist "%EnvTorPath%\Tor\tor.exe" (
if not exist "%EnvDownloadPath%\%TorInstall%" %cecho% error "Cannot download Tor installation" & goto error
%cecho% info "Unpack Tor"
- "%EnvSevenZipExe%" x -o"%EnvTorPath%" "%EnvDownloadPath%\%TorInstall%"
+ "%EnvSevenZipExe%" x -so "%EnvDownloadPath%\%TorInstall%" | "%EnvSevenZipExe%" x -si -ttar -o"%EnvTorPath%" -y -bso0
)
:exit
diff --git a/build_scripts/Windows/env/tools/root/update-msys2.bat b/build_scripts/Windows/env/tools/root/update-msys2.bat
index a3d2398cd..ddd5bdd0f 100644
--- a/build_scripts/Windows/env/tools/root/update-msys2.bat
+++ b/build_scripts/Windows/env/tools/root/update-msys2.bat
@@ -2,18 +2,13 @@
setlocal
-if exist "%~dp0msys2\msys32" call :update 32
-if exist "%~dp0msys2\msys64" call :update 64
+if not exist "%~dp0msys2\msys64" goto :EOF
-goto :EOF
+set MSYS2SH=%~dp0msys2\msys64\usr\bin\sh
-:update
-set MSYS2SH=%~dp0msys2\msys%~1\usr\bin\sh
-
-echo Update MSYS2 %~1
+echo Update MSYS2
"%MSYS2SH%" -lc "yes | pacman --noconfirm -Syuu msys2-keyring"
"%MSYS2SH%" -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 74f92ffe5..4279f8174 100644
--- a/build_scripts/Windows/installer/lang/ca_ES.nsh
+++ b/build_scripts/Windows/installer/lang/ca_ES.nsh
@@ -2,6 +2,12 @@
!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_WebUI "WebUI"
+!insertmacro LANG_STRING Section_WebUI_Desc "Installs WebUI."
+!insertmacro LANG_STRING Section_Service "Service"
+!insertmacro LANG_STRING Section_Service_Desc "Installs Service."
+!insertmacro LANG_STRING Section_FriendServer "Friend Server"
+!insertmacro LANG_STRING Section_FriendServer_Desc "Installs Friend Server."
!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 663e9e786..d67eb3e6f 100644
--- a/build_scripts/Windows/installer/lang/de.nsh
+++ b/build_scripts/Windows/installer/lang/de.nsh
@@ -2,6 +2,12 @@
!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_WebUI "WebUI"
+!insertmacro LANG_STRING Section_WebUI_Desc "Installiert WebUI."
+!insertmacro LANG_STRING Section_Service "Service"
+!insertmacro LANG_STRING Section_Service_Desc "Installiert Service."
+!insertmacro LANG_STRING Section_FriendServer "Friend Server"
+!insertmacro LANG_STRING Section_FriendServer_Desc "Installiert Friend Server."
!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 a709eec24..7ecb92979 100644
--- a/build_scripts/Windows/installer/lang/en.nsh
+++ b/build_scripts/Windows/installer/lang/en.nsh
@@ -2,6 +2,12 @@
!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_WebUI "WebUI"
+!insertmacro LANG_STRING Section_WebUI_Desc "Installs WebUI."
+!insertmacro LANG_STRING Section_Service "Service"
+!insertmacro LANG_STRING Section_Service_Desc "Installs Service."
+!insertmacro LANG_STRING Section_FriendServer "Friend Server"
+!insertmacro LANG_STRING Section_FriendServer_Desc "Installs Friend Server."
!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 66021c14e..5f0a8aeee 100644
--- a/build_scripts/Windows/installer/lang/es.nsh
+++ b/build_scripts/Windows/installer/lang/es.nsh
@@ -2,6 +2,12 @@
!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_WebUI "WebUI"
+!insertmacro LANG_STRING Section_WebUI_Desc "Installs WebUI."
+!insertmacro LANG_STRING Section_Service "Service"
+!insertmacro LANG_STRING Section_Service_Desc "Installs Service."
+!insertmacro LANG_STRING Section_FriendServer "Friend Server"
+!insertmacro LANG_STRING Section_FriendServer_Desc "Installs Friend Server."
!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 680746da0..e55ca2466 100644
--- a/build_scripts/Windows/installer/lang/fr.nsh
+++ b/build_scripts/Windows/installer/lang/fr.nsh
@@ -2,6 +2,12 @@
!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_WebUI "WebUI"
+!insertmacro LANG_STRING Section_WebUI_Desc "Installs WebUI."
+!insertmacro LANG_STRING Section_Service "Service"
+!insertmacro LANG_STRING Section_Service_Desc "Installs Service."
+!insertmacro LANG_STRING Section_FriendServer "Friend Server"
+!insertmacro LANG_STRING Section_FriendServer_Desc "Installs Friend Server."
!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 53fc42934..44b3b70a1 100644
--- a/build_scripts/Windows/installer/lang/pl.nsh
+++ b/build_scripts/Windows/installer/lang/pl.nsh
@@ -2,6 +2,12 @@
!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_WebUI "WebUI"
+!insertmacro LANG_STRING Section_WebUI_Desc "Installs WebUI."
+!insertmacro LANG_STRING Section_Service "Service"
+!insertmacro LANG_STRING Section_Service_Desc "Installs Service."
+!insertmacro LANG_STRING Section_FriendServer "Friend Server"
+!insertmacro LANG_STRING Section_FriendServer_Desc "Installs Friend Server."
!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 b8ba2df89..969d58629 100644
--- a/build_scripts/Windows/installer/lang/ru.nsh
+++ b/build_scripts/Windows/installer/lang/ru.nsh
@@ -2,6 +2,12 @@
!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_WebUI "WebUI"
+!insertmacro LANG_STRING Section_WebUI_Desc "Installs WebUI."
+!insertmacro LANG_STRING Section_Service "Service"
+!insertmacro LANG_STRING Section_Service_Desc "Installs Service."
+!insertmacro LANG_STRING Section_FriendServer "Friend Server"
+!insertmacro LANG_STRING Section_FriendServer_Desc "Installs Friend Server."
!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 85abeaac2..70a475a86 100644
--- a/build_scripts/Windows/installer/lang/tr.nsh
+++ b/build_scripts/Windows/installer/lang/tr.nsh
@@ -2,6 +2,12 @@
!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_WebUI "WebUI"
+!insertmacro LANG_STRING Section_WebUI_Desc "Installs WebUI."
+!insertmacro LANG_STRING Section_Service "Service"
+!insertmacro LANG_STRING Section_Service_Desc "Installs Service."
+!insertmacro LANG_STRING Section_FriendServer "Friend Server"
+!insertmacro LANG_STRING Section_FriendServer_Desc "Installs Friend Server."
!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 e95f6939f..cb38b6d94 100644
--- a/build_scripts/Windows/installer/lang/ts/en.ts
+++ b/build_scripts/Windows/installer/lang/ts/en.ts
@@ -22,6 +22,48 @@
+
+ Section_WebUI
+
+ WebUI
+
+
+
+
+ Section_WebUI_Desc
+
+ Installs WebUI.
+
+
+
+
+ Section_Service
+
+ Service
+
+
+
+
+ Section_Service_Desc
+
+ Installs Service.
+
+
+
+
+ Section_FriendServer
+
+ Friend Server
+
+
+
+
+ Section_FriendServer_Desc
+
+ Installs Friend Server.
+
+
+Section_Data
diff --git a/build_scripts/Windows/installer/lang/zh_CN.nsh b/build_scripts/Windows/installer/lang/zh_CN.nsh
index 330de33ab..7d9a3cc02 100644
--- a/build_scripts/Windows/installer/lang/zh_CN.nsh
+++ b/build_scripts/Windows/installer/lang/zh_CN.nsh
@@ -2,6 +2,12 @@
!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_WebUI "WebUI"
+!insertmacro LANG_STRING Section_WebUI_Desc "Installs WebUI."
+!insertmacro LANG_STRING Section_Service "Service"
+!insertmacro LANG_STRING Section_Service_Desc "Installs Service."
+!insertmacro LANG_STRING Section_FriendServer "Friend Server"
+!insertmacro LANG_STRING Section_FriendServer_Desc "Installs Friend Server."
!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 9fd364764..63c81bbcc 100644
--- a/build_scripts/Windows/installer/retroshare-Qt5.nsi
+++ b/build_scripts/Windows/installer/retroshare-Qt5.nsi
@@ -60,6 +60,9 @@
!define /date DATE "%Y%m%d"
!endif
+# Service
+${!defineifexist} SERVICE_EXISTS "${RELEASEDIR}\retroshare-service\src\release\retroshare-service.exe"
+
# Tor
!ifdef TORDIR
${!defineifexist} TOR_EXISTS "${TORDIR}\tor.exe"
@@ -68,6 +71,20 @@ ${!defineifexist} TOR_EXISTS "${TORDIR}\tor.exe"
!endif
!endif
+# WebUI
+!ifdef WEBUIDIR
+${!defineifexist} WEBUI_EXISTS "${WEBUIDIR}\index.html"
+!ifndef WEBUI_EXISTS
+!error "WebUI files not found"
+!endif
+!endif
+
+# Friend Server
+!ifdef TOR_EXISTS
+# Add Friend Server with Tor only
+#${!defineifexist} FRIENDSERVER_EXISTS "${RELEASEDIR}\retroshare-friendserver\src\release\retroshare-friendserver.exe"
+!endif
+
# Application name and version
!define APPNAME "RetroShare"
!define APPNAMEANDVERSION "${APPNAME} ${VERSION}"
@@ -193,7 +210,6 @@ Section $(Section_Main) Section_Main
; Main binaries
SetOutPath "$INSTDIR"
File "${RELEASEDIR}\retroshare-gui\src\release\retroshare.exe"
- File "${RELEASEDIR}\retroshare-service\src\release\retroshare-service.exe"
File /nonfatal "${RELEASEDIR}\libretroshare\src\lib\retroshare.dll"
; Qt binaries
@@ -282,11 +298,35 @@ Section $(Section_Main) Section_Main
File /r "${SOURCEDIR}\retroshare-gui\src\license\*.*"
SectionEnd
+# Service
+!ifdef SERVICE_EXISTS
+ Section /o $(Section_Service) Section_Service
+ SetOutPath "$INSTDIR"
+ File "${RELEASEDIR}\retroshare-service\src\release\retroshare-service.exe"
+ SectionEnd
+!endif
+
+# Friend Server
+!ifdef FRIENDSERVER_EXISTS
+ Section /o $(Section_FriendServer) Section_FriendServer
+ SetOutPath "$INSTDIR"
+ File "${RELEASEDIR}\retroshare-friendserver\src\release\retroshare-friendserver.exe"
+ SectionEnd
+!endif
+
# Tor
!ifdef TOR_EXISTS
Section /o $(Section_Tor) Section_Tor
SetOutPath "$INSTDIR\tor"
- File /r "${TORDIR}\*"
+ File "${TORDIR}\*"
+ SectionEnd
+!endif
+
+# WebUI
+!ifdef WEBUI_EXISTS
+ Section /o $(Section_WebUI) Section_WebUI
+ SetOutPath "$INSTDIR\webui"
+ File /r "${WEBUIDIR}\*"
SectionEnd
!endif
@@ -355,6 +395,22 @@ Section $(Section_StartMenu) Section_StartMenu
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
+
+!ifdef SERVICE_EXISTS
+ SectionGetFlags ${Section_Service} $0
+ IntOp $0 $0 & ${SF_SELECTED}
+ ${If} $0 == ${SF_SELECTED}
+ CreateShortCut "$SMPROGRAMS\${APPNAME}\${APPNAME} Service.lnk" "$INSTDIR\retroshare-service.exe" "" "$INSTDIR\retroshare-service.exe" 0
+ ${EndIf}
+!endif
+
+!ifdef FRIENDSERVER_EXISTS
+ SectionGetFlags ${Section_FriendServer} $0
+ IntOp $0 $0 & ${SF_SELECTED}
+ ${If} $0 == ${SF_SELECTED}
+ CreateShortCut "$SMPROGRAMS\${APPNAME}\${APPNAME} Friend Server.lnk" "$INSTDIR\retroshare-friendserver.exe" "" "$INSTDIR\retroshare-friendserver.exe" 0
+ ${EndIf}
+!endif
SectionEnd
Section $(Section_Desktop) Section_Desktop
@@ -407,6 +463,9 @@ SectionEnd
; !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_DESCRIPTION_TEXT ${Section_WebUI} $(Section_WebUI_Desc)
+ !insertmacro MUI_DESCRIPTION_TEXT ${Section_Service} $(Section_Service_Desc)
+ !insertmacro MUI_DESCRIPTION_TEXT ${Section_FriendServer} $(Section_FriendServer_Desc)
!insertmacro MUI_FUNCTION_DESCRIPTION_END
# Uninstall
@@ -461,6 +520,26 @@ Function .onInit
!insertmacro MUI_LANGDLL_DISPLAY
FunctionEnd
+!ifdef FRIENDSERVER_EXISTS
+Function .onSelChange
+ SectionGetFlags ${Section_FriendServer} $0
+ IntOp $0 $0 & ${SF_SELECTED}
+ ${If} $0 == ${SF_SELECTED}
+ # Activate Tor and set readonly
+ SectionGetFlags ${Section_Tor} $1
+ IntOp $1 $1 | ${SF_SELECTED}
+ IntOp $1 $1 | ${SF_RO}
+ SectionSetFlags ${Section_Tor} $1
+ ${Else}
+ # Remove readonly from Tor
+ SectionGetFlags ${Section_Tor} $1
+ IntOp $2 ${SF_RO} ~
+ IntOp $1 $1 & $2
+ SectionSetFlags ${Section_Tor} $1
+ ${EndIf}
+FunctionEnd
+!endif
+
# Installation mode
Function RequireAdmin
diff --git a/build_scripts/Windows/tools/generate-changelog.bat b/build_scripts/Windows/tools/generate-changelog.bat
index c38225045..2eb87ced6 100644
--- a/build_scripts/Windows/tools/generate-changelog.bat
+++ b/build_scripts/Windows/tools/generate-changelog.bat
@@ -18,6 +18,8 @@ copy nul %logfile% > nul
pushd %~1
+set Percent=%%
+
set last=HEAD
for /f %%t in ('git tag --sort=-taggerdate --merged ^| findstr v') do (
echo generating changelog for !last!..%%t
@@ -30,7 +32,7 @@ for /f %%t in ('git tag --sort=-taggerdate --merged ^| findstr v') do (
rem echo !last! ---^> %%t >> %logfile%
echo ----------------------------------------------- >> %logfile%
echo. >> %logfile%
- git log %%t..!last! --no-merges "--pretty=format:%%h %%ai %%<(10,trunc)%%an %%s" >> %logfile%
+ git log %%t..!last! --no-merges "--pretty=format:!Percent!h !Percent!ai !Percent!<(10,trunc)!Percent!an !Percent!s" >> %logfile%
echo. >> %logfile%
echo. >> %logfile%
set last=%%t
diff --git a/build_scripts/git_tag_cleaner.sh b/build_scripts/git_tag_cleaner.sh
new file mode 100755
index 000000000..cf7a3ef0c
--- /dev/null
+++ b/build_scripts/git_tag_cleaner.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+function git_del_tag()
+{
+ mTag=$1
+
+ for mRemote in $(git remote); do
+ echo "Attempting tag $mTag removal from remote $mRemote"
+ GIT_TERMINAL_PROMPT=0 git push $mRemote :$mTag || true
+ done
+ git tag --delete $mTag
+}
+
+for mModule in . build_scripts/OBS/ libbitdht/ libretroshare/ openpgpsdk/ retroshare-webui/ ; do
+ pushd $mModule
+ git_del_tag v0.6.7a
+ git tag --list | grep untagged | while read mTag; do git_del_tag $mTag ; done
+ popd
+done
+
diff --git a/libbitdht b/libbitdht
index 659423769..2ddc86fb5 160000
--- a/libbitdht
+++ b/libbitdht
@@ -1 +1 @@
-Subproject commit 659423769541169457c41f71c8a038e2d64ba079
+Subproject commit 2ddc86fb575a61170f4c06a00152e3e7dc74c8f4
diff --git a/libretroshare b/libretroshare
index a7a430008..402f32eda 160000
--- a/libretroshare
+++ b/libretroshare
@@ -1 +1 @@
-Subproject commit a7a430008b76e53727598c4d13106e7ce95221d7
+Subproject commit 402f32eda026c3ec3e429b5fb842e87ebd985d73
diff --git a/plugins/FeedReader/FeedReader.pro b/plugins/FeedReader/FeedReader.pro
index b78dda911..5c696b5c3 100644
--- a/plugins/FeedReader/FeedReader.pro
+++ b/plugins/FeedReader/FeedReader.pro
@@ -44,6 +44,7 @@ SOURCES = FeedReaderPlugin.cpp \
gui/FeedReaderFeedNotify.cpp \
gui/FeedReaderUserNotify.cpp \
gui/FeedReaderFeedItem.cpp \
+ gui/FeedTreeWidget.cpp \
util/CURLWrapper.cpp \
util/XMLWrapper.cpp \
util/HTMLWrapper.cpp \
@@ -64,6 +65,7 @@ HEADERS = FeedReaderPlugin.h \
gui/FeedReaderFeedNotify.h \
gui/FeedReaderUserNotify.h \
gui/FeedReaderFeedItem.h \
+ gui/FeedTreeWidget.h \
util/CURLWrapper.h \
util/XMLWrapper.h \
util/HTMLWrapper.h \
@@ -121,6 +123,11 @@ win32 {
#Have to reorder libs, else got /libs/lib/libcrypto.a(bio_lib.o):bio_lib.c:(.text+0x0): multiple definition of `BIO_new'
LIBS = -lcurl -lxml2 -lz -lxslt -lws2_32 -lwldap32 -lssl -lcrypto -lgdi32 $${LIBS}
+ isEmpty(QMAKE_SH) {
+ # MinGW
+ LIBS += -lcrypt32
+ }
+
# Check for msys2
!isEmpty(PREFIX_MSYS2) {
message(Use msys2 xml2.)
diff --git a/plugins/FeedReader/FeedReaderPlugin.cpp b/plugins/FeedReader/FeedReaderPlugin.cpp
index 24b602f9c..bc44a3036 100644
--- a/plugins/FeedReader/FeedReaderPlugin.cpp
+++ b/plugins/FeedReader/FeedReaderPlugin.cpp
@@ -92,7 +92,7 @@ void FeedReaderPlugin::setInterfaces(RsPlugInInterfaces &interfaces)
{
mInterfaces = interfaces;
- mFeedReader = new p3FeedReader(mPlugInHandler, mInterfaces.mGxsForums);
+ mFeedReader = new p3FeedReader(mPlugInHandler, mInterfaces.mGxsForums, mInterfaces.mPosted);
rsFeedReader = mFeedReader;
mNotify = new FeedReaderNotify();
diff --git a/plugins/FeedReader/gui/AddFeedDialog.cpp b/plugins/FeedReader/gui/AddFeedDialog.cpp
index 19d02d968..59baf908a 100644
--- a/plugins/FeedReader/gui/AddFeedDialog.cpp
+++ b/plugins/FeedReader/gui/AddFeedDialog.cpp
@@ -30,9 +30,11 @@
#include "gui/common/UIStateHelper.h"
#include
+#include
#include
-#define TOKEN_TYPE_FORUM_GROUPS 1
+#define TOKEN_TYPE_FORUM_GROUPS 1
+#define TOKEN_TYPE_POSTED_GROUPS 2
AddFeedDialog::AddFeedDialog(RsFeedReader *feedReader, FeedReaderNotify *notify, QWidget *parent)
: QDialog(parent, Qt::Window), mFeedReader(feedReader), mNotify(notify), ui(new Ui::AddFeedDialog)
@@ -47,9 +49,12 @@ AddFeedDialog::AddFeedDialog(RsFeedReader *feedReader, FeedReaderNotify *notify,
mStateHelper->addWidget(TOKEN_TYPE_FORUM_GROUPS, ui->forumComboBox, UISTATE_LOADING_DISABLED);
mStateHelper->addWidget(TOKEN_TYPE_FORUM_GROUPS, ui->buttonBox->button(QDialogButtonBox::Ok), UISTATE_LOADING_DISABLED);
+ mStateHelper->addWidget(TOKEN_TYPE_POSTED_GROUPS, ui->postedComboBox, UISTATE_LOADING_DISABLED);
+ mStateHelper->addWidget(TOKEN_TYPE_POSTED_GROUPS, ui->buttonBox->button(QDialogButtonBox::Ok), UISTATE_LOADING_DISABLED);
/* Setup TokenQueue */
- mTokenQueue = new TokenQueue(rsGxsForums->getTokenService(), this);
+ mForumTokenQueue = new TokenQueue(rsGxsForums->getTokenService(), this);
+ mPostedTokenQueue = new TokenQueue(rsPosted->getTokenService(), this);
/* Connect signals */
connect(ui->buttonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(createFeed()));
@@ -59,18 +64,23 @@ AddFeedDialog::AddFeedDialog(RsFeedReader *feedReader, FeedReaderNotify *notify,
connect(ui->useStandardStorageTimeCheckBox, SIGNAL(toggled(bool)), this, SLOT(useStandardStorageTimeToggled()));
connect(ui->useStandardUpdateInterval, SIGNAL(toggled(bool)), this, SLOT(useStandardUpdateIntervalToggled()));
connect(ui->useStandardProxyCheckBox, SIGNAL(toggled(bool)), this, SLOT(useStandardProxyToggled()));
- connect(ui->typeForumRadio, SIGNAL(toggled(bool)), this, SLOT(typeForumToggled()));
+ connect(ui->typeForumCheckBox, SIGNAL(toggled(bool)), this, SLOT(typeForumToggled()));
+ connect(ui->typePostedCheckBox, SIGNAL(toggled(bool)), this, SLOT(typePostedToggled()));
+ connect(ui->typeLocalCheckBox, SIGNAL(toggled(bool)), this, SLOT(typeLocalToggled()));
+ connect(ui->postedFirstImageCheckBox, SIGNAL(toggled(bool)), this, SLOT(postedFirstImageToggled()));
connect(ui->previewButton, SIGNAL(clicked()), this, SLOT(preview()));
/* currently only for local feeds */
- connect(ui->saveCompletePageCheckBox, SIGNAL(toggled(bool)), this, SLOT(denyForumToggled()));
+ connect(ui->saveCompletePageCheckBox, SIGNAL(toggled(bool)), this, SLOT(denyForumAndPostedToggled()));
connect(ui->urlLineEdit, SIGNAL(textChanged(QString)), this, SLOT(validate()));
connect(ui->nameLineEdit, SIGNAL(textChanged(QString)), this, SLOT(validate()));
connect(ui->useInfoFromFeedCheckBox, SIGNAL(toggled(bool)), this, SLOT(validate()));
- connect(ui->typeLocalRadio, SIGNAL(toggled(bool)), this, SLOT(validate()));
- connect(ui->typeForumRadio, SIGNAL(toggled(bool)), this, SLOT(validate()));
+ connect(ui->typeLocalCheckBox, SIGNAL(toggled(bool)), this, SLOT(validate()));
+ connect(ui->typeForumCheckBox, SIGNAL(toggled(bool)), this, SLOT(validate()));
connect(ui->forumComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(validate()));
+ connect(ui->typePostedCheckBox, SIGNAL(toggled(bool)), this, SLOT(validate()));
+ connect(ui->postedComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(validate()));
connect(ui->clearCachePushButton, SIGNAL(clicked()), this, SLOT(clearMessageCache()));
@@ -79,13 +89,24 @@ AddFeedDialog::AddFeedDialog(RsFeedReader *feedReader, FeedReaderNotify *notify,
ui->activatedCheckBox->setChecked(true);
mStateHelper->setWidgetEnabled(ui->forumComboBox, false);
+ mStateHelper->setWidgetEnabled(ui->postedComboBox, false);
ui->useInfoFromFeedCheckBox->setChecked(true);
ui->updateForumInfoCheckBox->setEnabled(false);
ui->updateForumInfoCheckBox->setChecked(true);
+ ui->updatePostedInfoCheckBox->setEnabled(false);
+ ui->updatePostedInfoCheckBox->setChecked(true);
+ ui->postedFirstImageCheckBox->setEnabled(false);
+ ui->postedFirstImageCheckBox->setChecked(false);
+ ui->postedOnlyImageCheckBox->setEnabled(false);
+ ui->postedOnlyImageCheckBox->setChecked(false);
+ ui->postedShinkImageCheckBox->setEnabled(false);
+ ui->postedShinkImageCheckBox->setChecked(true);
ui->useAuthenticationCheckBox->setChecked(false);
ui->useStandardStorageTimeCheckBox->setChecked(true);
ui->useStandardUpdateInterval->setChecked(true);
ui->useStandardProxyCheckBox->setChecked(true);
+ ui->embedImagesCheckBox->setChecked(true);
+ ui->saveCompletePageCheckBox->setEnabled(false);
/* not yet supported */
ui->authenticationGroupBox->setEnabled(false);
@@ -98,6 +119,9 @@ AddFeedDialog::AddFeedDialog(RsFeedReader *feedReader, FeedReaderNotify *notify,
/* fill own forums */
requestForumGroups();
+ /* fill own posted */
+ requestPostedGroups();
+
validate();
ui->urlLineEdit->setFocus();
@@ -112,7 +136,8 @@ AddFeedDialog::~AddFeedDialog()
processSettings(false);
delete(ui);
- delete(mTokenQueue);
+ delete(mForumTokenQueue);
+ delete(mPostedTokenQueue);
}
void AddFeedDialog::processSettings(bool load)
@@ -161,18 +186,66 @@ void AddFeedDialog::useStandardProxyToggled()
void AddFeedDialog::typeForumToggled()
{
- bool checked = ui->typeForumRadio->isChecked();
+ bool checked = ui->typeForumCheckBox->isChecked();
mStateHelper->setWidgetEnabled(ui->forumComboBox, checked);
ui->updateForumInfoCheckBox->setEnabled(checked);
+
+ if (checked) {
+ ui->typeLocalCheckBox->setChecked(false);
+ }
}
-void AddFeedDialog::denyForumToggled()
+void AddFeedDialog::postedFirstImageToggled()
+{
+ bool checked = ui->postedFirstImageCheckBox->isChecked();
+ ui->postedOnlyImageCheckBox->setEnabled(checked);
+
+ if (!checked) {
+ ui->postedOnlyImageCheckBox->setChecked(false);
+ }
+}
+
+void AddFeedDialog::typePostedToggled()
+{
+ bool checked = ui->typePostedCheckBox->isChecked();
+ mStateHelper->setWidgetEnabled(ui->postedComboBox, checked);
+ ui->updatePostedInfoCheckBox->setEnabled(checked);
+ ui->postedFirstImageCheckBox->setEnabled(checked);
+ ui->postedShinkImageCheckBox->setEnabled(checked);
+
+ if (checked) {
+ ui->typeLocalCheckBox->setChecked(false);
+ }else {
+ ui->postedFirstImageCheckBox->setChecked(false);
+ }
+}
+
+void AddFeedDialog::typeLocalToggled()
+{
+ bool checked = ui->typeLocalCheckBox->isChecked();
+ if (checked) {
+ mStateHelper->setWidgetEnabled(ui->forumComboBox, false);
+ mStateHelper->setWidgetEnabled(ui->postedComboBox, false);
+ ui->typeForumCheckBox->setChecked(false);
+ ui->typePostedCheckBox->setChecked(false);
+ ui->saveCompletePageCheckBox->setEnabled(true);
+ } else {
+ ui->saveCompletePageCheckBox->setEnabled(false);
+ ui->saveCompletePageCheckBox->setChecked(false);
+ }
+}
+
+void AddFeedDialog::denyForumAndPostedToggled()
{
if (ui->saveCompletePageCheckBox->isChecked()) {
- ui->typeForumRadio->setEnabled(false);
- ui->typeLocalRadio->setChecked(true);
+ ui->typeForumCheckBox->setEnabled(false);
+ ui->typeForumCheckBox->setChecked(false);
+ ui->typePostedCheckBox->setEnabled(false);
+ ui->typePostedCheckBox->setChecked(false);
+ ui->typeLocalCheckBox->setChecked(true);
} else {
- ui->typeForumRadio->setEnabled(true);
+ ui->typeForumCheckBox->setEnabled(true);
+ ui->typePostedCheckBox->setEnabled(true);
}
}
@@ -189,11 +262,15 @@ void AddFeedDialog::validate()
ui->previewButton->setEnabled(ok);
- if (!ui->typeLocalRadio->isChecked() && !ui->typeForumRadio->isChecked()) {
+ if (!ui->typeLocalCheckBox->isChecked() && !ui->typeForumCheckBox->isChecked() && !ui->typePostedCheckBox->isChecked()) {
ok = false;
}
- if (ui->typeForumRadio->isChecked() && ui->forumComboBox->itemData(ui->forumComboBox->currentIndex()).toString().isEmpty()) {
+ if (ui->typeForumCheckBox->isChecked() && ui->forumComboBox->itemData(ui->forumComboBox->currentIndex()).toString().isEmpty()) {
+ ok = false;
+ }
+
+ if (ui->typePostedCheckBox->isChecked() && ui->postedComboBox->itemData(ui->postedComboBox->currentIndex()).toString().isEmpty()) {
ok = false;
}
@@ -224,20 +301,32 @@ bool AddFeedDialog::fillFeed(uint32_t feedId)
ui->urlLineEdit->setText(QString::fromUtf8(feedInfo.url.c_str()));
ui->useInfoFromFeedCheckBox->setChecked(feedInfo.flag.infoFromFeed);
ui->updateForumInfoCheckBox->setChecked(feedInfo.flag.updateForumInfo);
+ ui->updatePostedInfoCheckBox->setChecked(feedInfo.flag.updatePostedInfo);
+ ui->postedFirstImageCheckBox->setChecked(feedInfo.flag.postedFirstImage);
+ ui->postedOnlyImageCheckBox->setChecked(feedInfo.flag.postedOnlyImage);
+ ui->postedShinkImageCheckBox->setChecked(feedInfo.flag.postedShrinkImage);
ui->activatedCheckBox->setChecked(!feedInfo.flag.deactivated);
ui->embedImagesCheckBox->setChecked(feedInfo.flag.embedImages);
ui->saveCompletePageCheckBox->setChecked(feedInfo.flag.saveCompletePage);
ui->descriptionPlainTextEdit->setPlainText(QString::fromUtf8(feedInfo.description.c_str()));
- if (feedInfo.flag.forum) {
- mStateHelper->setWidgetEnabled(ui->forumComboBox, true);
- ui->typeForumRadio->setChecked(true);
- ui->saveCompletePageCheckBox->setEnabled(false);
+ if (feedInfo.flag.forum || feedInfo.flag.posted) {
+ if (feedInfo.flag.forum) {
+ mStateHelper->setWidgetEnabled(ui->forumComboBox, true);
+ ui->typeForumCheckBox->setChecked(true);
- setActiveForumId(feedInfo.forumId);
+ setActiveForumId(feedInfo.forumId);
+ }
+ if (feedInfo.flag.posted) {
+ mStateHelper->setWidgetEnabled(ui->postedComboBox, true);
+ ui->typePostedCheckBox->setChecked(true);
+
+ setActivePostedId(feedInfo.postedId);
+ }
+ ui->saveCompletePageCheckBox->setEnabled(false);
} else {
- ui->typeLocalRadio->setChecked(true);
+ ui->typeLocalCheckBox->setChecked(true);
mStateHelper->setWidgetEnabled(ui->forumComboBox, false);
}
@@ -286,6 +375,21 @@ void AddFeedDialog::setActiveForumId(const std::string &forumId)
}
}
+void AddFeedDialog::setActivePostedId(const std::string &postedId)
+{
+ if (mStateHelper->isLoading(TOKEN_TYPE_POSTED_GROUPS)) {
+ mFillPostedId = postedId;
+ return;
+ }
+
+ int index = ui->postedComboBox->findData(QString::fromStdString(postedId));
+ if (index >= 0) {
+ ui->postedComboBox->setCurrentIndex(index);
+ } else {
+ ui->postedComboBox->setCurrentIndex(0);
+ }
+}
+
void AddFeedDialog::getFeedInfo(FeedInfo &feedInfo)
{
feedInfo.parentId = mParentId;
@@ -294,18 +398,28 @@ void AddFeedDialog::getFeedInfo(FeedInfo &feedInfo)
feedInfo.url = ui->urlLineEdit->text().toUtf8().constData();
feedInfo.flag.infoFromFeed = ui->useInfoFromFeedCheckBox->isChecked();
feedInfo.flag.updateForumInfo = ui->updateForumInfoCheckBox->isChecked() && ui->updateForumInfoCheckBox->isEnabled();
+ feedInfo.flag.updatePostedInfo = ui->updatePostedInfoCheckBox->isChecked() && ui->updatePostedInfoCheckBox->isEnabled();
+ feedInfo.flag.postedFirstImage = ui->postedFirstImageCheckBox->isChecked() && ui->postedFirstImageCheckBox->isEnabled();
+ feedInfo.flag.postedOnlyImage = ui->postedOnlyImageCheckBox->isChecked() && ui->postedOnlyImageCheckBox->isEnabled();
+ feedInfo.flag.postedShrinkImage = ui->postedShinkImageCheckBox->isChecked() && ui->postedShinkImageCheckBox->isEnabled();
feedInfo.flag.deactivated = !ui->activatedCheckBox->isChecked();
feedInfo.flag.embedImages = ui->embedImagesCheckBox->isChecked();
feedInfo.flag.saveCompletePage = ui->saveCompletePageCheckBox->isChecked();
feedInfo.description = ui->descriptionPlainTextEdit->toPlainText().toUtf8().constData();
- feedInfo.flag.forum = ui->typeForumRadio->isChecked();
+ feedInfo.flag.forum = ui->typeForumCheckBox->isChecked();
if (feedInfo.flag.forum) {
feedInfo.forumId = ui->forumComboBox->itemData(ui->forumComboBox->currentIndex()).toString().toStdString();
}
+ feedInfo.flag.posted = ui->typePostedCheckBox->isChecked();
+
+ if (feedInfo.flag.posted) {
+ feedInfo.postedId = ui->postedComboBox->itemData(ui->postedComboBox->currentIndex()).toString().toStdString();
+ }
+
feedInfo.flag.authentication = ui->useAuthenticationCheckBox->isChecked();
feedInfo.user = ui->userLineEdit->text().toUtf8().constData();
feedInfo.password = ui->passwordLineEdit->text().toUtf8().constData();
@@ -340,12 +454,12 @@ void AddFeedDialog::createFeed()
if (mFeedId == 0) {
/* add new feed */
- RsFeedAddResult result = mFeedReader->addFeed(feedInfo, mFeedId);
+ RsFeedResult result = mFeedReader->addFeed(feedInfo, mFeedId);
if (FeedReaderStringDefs::showError(this, result, tr("Create feed"), tr("Cannot create feed."))) {
return;
}
} else {
- RsFeedAddResult result = mFeedReader->setFeed(mFeedId, feedInfo);
+ RsFeedResult result = mFeedReader->setFeed(mFeedId, feedInfo);
if (FeedReaderStringDefs::showError(this, result, tr("Edit feed"), tr("Cannot change feed."))) {
return;
}
@@ -382,10 +496,10 @@ void AddFeedDialog::requestForumGroups()
RsTokReqOptions opts;
opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
- mTokenQueue->cancelActiveRequestTokens(TOKEN_TYPE_FORUM_GROUPS);
+ mForumTokenQueue->cancelActiveRequestTokens(TOKEN_TYPE_FORUM_GROUPS);
uint32_t token;
- mTokenQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, TOKEN_TYPE_FORUM_GROUPS);
+ mForumTokenQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, TOKEN_TYPE_FORUM_GROUPS);
}
void AddFeedDialog::loadForumGroups(const uint32_t &token)
@@ -416,9 +530,50 @@ void AddFeedDialog::loadForumGroups(const uint32_t &token)
}
}
+void AddFeedDialog::requestPostedGroups()
+{
+ mStateHelper->setLoading(TOKEN_TYPE_POSTED_GROUPS, true);
+
+ RsTokReqOptions opts;
+ opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
+
+ mPostedTokenQueue->cancelActiveRequestTokens(TOKEN_TYPE_POSTED_GROUPS);
+
+ uint32_t token;
+ mPostedTokenQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, TOKEN_TYPE_POSTED_GROUPS);
+}
+
+void AddFeedDialog::loadPostedGroups(const uint32_t &token)
+{
+ std::vector groups;
+ rsPosted->getGroupData(token, groups);
+
+ ui->postedComboBox->clear();
+
+ for (std::vector::iterator it = groups.begin(); it != groups.end(); ++it) {
+ const RsPostedGroup &group = *it;
+
+ /* show only own posted */
+ if (IS_GROUP_PUBLISHER(group.mMeta.mSubscribeFlags) && IS_GROUP_ADMIN(group.mMeta.mSubscribeFlags) && !group.mMeta.mAuthorId.isNull()) {
+ ui->postedComboBox->addItem(QString::fromUtf8(group.mMeta.mGroupName.c_str()), QString::fromStdString(group.mMeta.mGroupId.toStdString()));
+ }
+ }
+
+ /* insert empty item */
+ ui->postedComboBox->insertItem(0, "", "");
+ ui->postedComboBox->setCurrentIndex(0);
+
+ mStateHelper->setLoading(TOKEN_TYPE_POSTED_GROUPS, false);
+
+ if (!mFillPostedId.empty()) {
+ setActivePostedId(mFillPostedId);
+ mFillPostedId.clear();
+ }
+}
+
void AddFeedDialog::loadRequest(const TokenQueue *queue, const TokenRequest &req)
{
- if (queue == mTokenQueue)
+ if (queue == mForumTokenQueue)
{
/* now switch on req */
switch(req.mUserType)
@@ -433,4 +588,20 @@ void AddFeedDialog::loadRequest(const TokenQueue *queue, const TokenRequest &req
}
}
+
+ if (queue == mPostedTokenQueue)
+ {
+ /* now switch on req */
+ switch(req.mUserType)
+ {
+ case TOKEN_TYPE_POSTED_GROUPS:
+ loadPostedGroups(req.mToken);
+ break;
+
+ default:
+ std::cerr << "AddFeedDialog::loadRequest() ERROR: INVALID TYPE";
+ std::cerr << std::endl;
+
+ }
+ }
}
diff --git a/plugins/FeedReader/gui/AddFeedDialog.h b/plugins/FeedReader/gui/AddFeedDialog.h
index 111238d33..e557580e1 100644
--- a/plugins/FeedReader/gui/AddFeedDialog.h
+++ b/plugins/FeedReader/gui/AddFeedDialog.h
@@ -53,7 +53,10 @@ private slots:
void useStandardUpdateIntervalToggled();
void useStandardProxyToggled();
void typeForumToggled();
- void denyForumToggled();
+ void postedFirstImageToggled();
+ void typePostedToggled();
+ void typeLocalToggled();
+ void denyForumAndPostedToggled();
void validate();
void createFeed();
void preview();
@@ -63,9 +66,12 @@ private:
void processSettings(bool load);
void getFeedInfo(FeedInfo &feedInfo);
void setActiveForumId(const std::string &forumId);
+ void setActivePostedId(const std::string &postedId);
void requestForumGroups();
void loadForumGroups(const uint32_t &token);
+ void requestPostedGroups();
+ void loadPostedGroups(const uint32_t &token);
private:
RsFeedReader *mFeedReader;
@@ -73,13 +79,15 @@ private:
uint32_t mFeedId;
uint32_t mParentId;
std::string mFillForumId;
+ std::string mFillPostedId;
RsFeedTransformationType mTransformationType;
std::list mXPathsToUse;
std::list mXPathsToRemove;
std::string mXslt;
- TokenQueue *mTokenQueue;
+ TokenQueue *mForumTokenQueue;
+ TokenQueue *mPostedTokenQueue;
UIStateHelper *mStateHelper;
Ui::AddFeedDialog *ui;
diff --git a/plugins/FeedReader/gui/AddFeedDialog.ui b/plugins/FeedReader/gui/AddFeedDialog.ui
index a10cec8a4..ebb478162 100644
--- a/plugins/FeedReader/gui/AddFeedDialog.ui
+++ b/plugins/FeedReader/gui/AddFeedDialog.ui
@@ -6,8 +6,8 @@
00
- 1068
- 880
+ 739
+ 653
@@ -45,48 +45,6 @@
QFrame::Raised
-
-
-
- Type
-
-
-
- 6
-
-
- 6
-
-
- 6
-
-
- 6
-
-
-
-
-
-
- Forum
-
-
-
-
-
-
-
-
-
-
-
- Local Feed
-
-
-
-
-
-
@@ -309,7 +267,6 @@
11
- 75truetrue
@@ -324,7 +281,44 @@
-
+
+
+
+ Misc
+
+
+
+
+
+ Activated
+
+
+
+
+
+
+ Use name and description from feed
+
+
+
+
+
+
+ Embed images
+
+
+
+
+
+
+ Save complete web page (experimental for local feeds)
+
+
+
+
+
+
+ Transformation
@@ -357,46 +351,99 @@
-
-
+
+
- Misc
+ Type
-
-
-
+
+
+ 6
+
+
+ 6
+
+
+ 6
+
+
+ 6
+
+
+
+
+
+ 0
+ 0
+
+
- Activated
+ Forum
-
-
+
+
+
+
+
- Use name and description from feed
+ Local Feed
-
-
+
+
+
+
+
+ Update forum information
+
+
+
+
+
+
+
+
+
+
- Update forum information
+ Board
-
-
-
- Embed images
-
-
-
-
-
-
- Save complete web page (experimental for local feeds)
-
-
+
+
+
+
+
+ Update board information
+
+
+
+
+
+
+ Only image
+
+
+
+
+
+
+ Use first image as board image
+
+
+
+
+
+
+ Shrink image
+
+
+
+
@@ -423,22 +470,32 @@
urlLineEditnameLineEditdescriptionPlainTextEdit
- typeForumRadio
+ typeForumCheckBoxforumComboBox
+ updateForumInfoCheckBox
+ typePostedCheckBox
+ postedComboBox
+ updatePostedInfoCheckBox
+ postedShinkImageCheckBox
+ postedFirstImageCheckBox
+ postedOnlyImageCheckBox
+ typeLocalCheckBoxactivatedCheckBoxuseInfoFromFeedCheckBox
- updateForumInfoCheckBox
+ embedImagesCheckBox
+ saveCompletePageCheckBox
+ previewButtonuseAuthenticationCheckBoxuserLineEditpasswordLineEdituseStandardStorageTimeCheckBox
+ clearCachePushButtonstorageTimeSpinBoxuseStandardUpdateIntervalupdateIntervalSpinBoxuseStandardProxyCheckBoxproxyAddressLineEditproxyPortSpinBox
- buttonBox
diff --git a/plugins/FeedReader/gui/FeedReaderDialog.cpp b/plugins/FeedReader/gui/FeedReaderDialog.cpp
index abc2f1aa5..08f390a09 100644
--- a/plugins/FeedReader/gui/FeedReaderDialog.cpp
+++ b/plugins/FeedReader/gui/FeedReaderDialog.cpp
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
#include "FeedReaderDialog.h"
#include "FeedReaderMessageWidget.h"
@@ -35,6 +36,8 @@
#include "gui/settings/rsharesettings.h"
#include "gui/notifyqt.h"
#include "FeedReaderUserNotify.h"
+#include "gui/Posted/PostedCreatePostDialog.h"
+#include "util/imageutil.h"
#include "interface/rsFeedReader.h"
#include "retroshare/rsiface.h"
@@ -55,6 +58,8 @@
#define ROLE_FEED_ERROR Qt::UserRole + 9
#define ROLE_FEED_DEACTIVATED Qt::UserRole + 10
+const int MAXMESSAGESIZE = RsSerialiser::MAX_SERIAL_SIZE - 70000;
+
FeedReaderDialog::FeedReaderDialog(RsFeedReader *feedReader, FeedReaderNotify *notify, QWidget *parent)
: MainPage(parent), mFeedReader(feedReader), mNotify(notify), ui(new Ui::FeedReaderDialog)
{
@@ -66,6 +71,7 @@ FeedReaderDialog::FeedReaderDialog(RsFeedReader *feedReader, FeedReaderNotify *n
mMessageWidget = NULL;
connect(mNotify, &FeedReaderNotify::feedChanged, this, &FeedReaderDialog::feedChanged, Qt::QueuedConnection);
+ connect(mNotify, &FeedReaderNotify::optimizeImage, this, &FeedReaderDialog::optimizeImage, Qt::QueuedConnection);
connect(NotifyQt::getInstance(), SIGNAL(settingsChanged()), this, SLOT(settingsChanged()));
@@ -84,9 +90,16 @@ FeedReaderDialog::FeedReaderDialog(RsFeedReader *feedReader, FeedReaderNotify *n
connect(ui->feedAddButton, SIGNAL(clicked()), this, SLOT(newFeed()));
connect(ui->feedProcessButton, SIGNAL(clicked()), this, SLOT(processFeed()));
+ connect(ui->feedTreeWidget, SIGNAL(feedReparent(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(feedTreeReparent(QTreeWidgetItem*,QTreeWidgetItem*)));
+
mFeedCompareRole = new RSTreeWidgetItemCompareRole;
mFeedCompareRole->setRole(COLUMN_FEED_NAME, ROLE_FEED_SORT);
+ /* enable drag and drop */
+ ui->feedTreeWidget->setAcceptDrops(true);
+ ui->feedTreeWidget->setDragEnabled(true);
+ ui->feedTreeWidget->setDragDropMode(QAbstractItemView::InternalMove);
+
/* initialize root item */
mRootItem = new QTreeWidgetItem(ui->feedTreeWidget);
QString name = tr("Message Folders");
@@ -395,6 +408,9 @@ void FeedReaderDialog::updateFeeds(uint32_t parentId, QTreeWidgetItem *parentIte
mOpenFeedIds->removeAt(index);
}
}
+ } else {
+ /* disable drop */
+ item->setFlags(item->flags() & ~Qt::ItemIsDropEnabled);
}
}
@@ -594,6 +610,55 @@ void FeedReaderDialog::feedChanged(uint32_t feedId, int type)
calculateFeedItems();
}
+void FeedReaderDialog::optimizeImage()
+{
+ while (true) {
+ FeedReaderOptimizeImageTask *optimizeImageTask = mFeedReader->getOptimizeImageTask();
+
+ if (!optimizeImageTask) {
+ return;
+ }
+
+ optimizeImageTask->mResult = false;
+
+ QImage image;
+ if (image.loadFromData(optimizeImageTask->mImage.data(), optimizeImageTask->mImage.size())) {
+ QByteArray optimizedImageData;
+ std::string optimizedImageMimeType;
+ QImage imageOptDummy;
+
+ switch (optimizeImageTask->mType) {
+ case FeedReaderOptimizeImageTask::POSTED:
+ if (PostedCreatePostDialog::optimizeImage(image, optimizedImageData, imageOptDummy)) {
+ optimizedImageMimeType = "image/jpeg";
+ optimizeImageTask->mResult = true;
+ }
+ break;
+ case FeedReaderOptimizeImageTask::SIZE:
+ if (ImageUtil::optimizeSizeBytes(optimizedImageData, image, imageOptDummy, "JPG", 0, MAXMESSAGESIZE)) {
+ optimizedImageMimeType = "image/jpg";
+ optimizeImageTask->mResult = true;
+ }
+ break;
+ }
+
+ if (optimizeImageTask->mResult) {
+ if (optimizedImageData.size() < (ssize_t) optimizeImageTask->mImage.size()) {
+ /* use optimized image */
+ optimizeImageTask->mImageResult.assign(optimizedImageData.begin(), optimizedImageData.end());
+ optimizeImageTask->mMimeTypeResult = optimizedImageMimeType;
+ } else {
+ /* use original image */
+ optimizeImageTask->mImageResult = optimizeImageTask->mImage;
+ optimizeImageTask->mMimeTypeResult = optimizeImageTask->mMimeType;
+ }
+ }
+ }
+
+ mFeedReader->setOptimizeImageTaskResult(optimizeImageTask);
+ }
+}
+
FeedReaderMessageWidget *FeedReaderDialog::feedMessageWidget(uint32_t id)
{
int tabCount = ui->messageTabWidget->count();
@@ -738,7 +803,7 @@ void FeedReaderDialog::newFolder()
if (dialog.exec() == QDialog::Accepted && !dialog.textValue().isEmpty()) {
uint32_t feedId;
- RsFeedAddResult result = mFeedReader->addFolder(currentFeedId(), dialog.textValue().toUtf8().constData(), feedId);
+ RsFeedResult result = mFeedReader->addFolder(currentFeedId(), dialog.textValue().toUtf8().constData(), feedId);
FeedReaderStringDefs::showError(this, result, tr("Create folder"), tr("Cannot create folder."));
}
}
@@ -791,7 +856,7 @@ void FeedReaderDialog::editFeed()
dialog.setTextValue(item->data(COLUMN_FEED_DATA, ROLE_FEED_NAME).toString());
if (dialog.exec() == QDialog::Accepted && !dialog.textValue().isEmpty()) {
- RsFeedAddResult result = mFeedReader->setFolder(feedId, dialog.textValue().toUtf8().constData());
+ RsFeedResult result = mFeedReader->setFolder(feedId, dialog.textValue().toUtf8().constData());
FeedReaderStringDefs::showError(this, result, tr("Create folder"), tr("Cannot create folder."));
}
} else {
@@ -832,3 +897,30 @@ void FeedReaderDialog::processFeed()
mFeedReader->processFeed(feedId);
}
+
+void FeedReaderDialog::feedTreeReparent(QTreeWidgetItem *item, QTreeWidgetItem *newParent)
+{
+ if (!item || ! newParent) {
+ return;
+ }
+
+ uint32_t feedId = item->data(COLUMN_FEED_DATA, ROLE_FEED_ID).toUInt();
+ uint32_t parentId = newParent->data(COLUMN_FEED_DATA, ROLE_FEED_ID).toUInt();
+
+ if (feedId == 0) {
+ return;
+ }
+
+ RsFeedResult result = mFeedReader->setParent(feedId, parentId);
+ if (FeedReaderStringDefs::showError(this, result, tr("Move feed"), tr("Cannot move feed."))) {
+ return;
+ }
+
+ bool expanded = item->isExpanded();
+ item->parent()->removeChild(item);
+ newParent->addChild(item);
+ item->setExpanded(expanded);
+ newParent->setExpanded(true);
+
+ calculateFeedItems();
+}
diff --git a/plugins/FeedReader/gui/FeedReaderDialog.h b/plugins/FeedReader/gui/FeedReaderDialog.h
index a221cfe64..12e11ee7f 100644
--- a/plugins/FeedReader/gui/FeedReaderDialog.h
+++ b/plugins/FeedReader/gui/FeedReaderDialog.h
@@ -61,6 +61,7 @@ private slots:
void editFeed();
void activateFeed();
void processFeed();
+ void feedTreeReparent(QTreeWidgetItem *item, QTreeWidgetItem *newParent);
void messageTabCloseRequested(int index);
void messageTabChanged(int index);
@@ -68,6 +69,7 @@ private slots:
/* FeedReaderNotify */
void feedChanged(uint32_t feedId, int type);
+ void optimizeImage();
private:
uint32_t currentFeedId();
diff --git a/plugins/FeedReader/gui/FeedReaderDialog.ui b/plugins/FeedReader/gui/FeedReaderDialog.ui
index 5eed95bc7..b6b004d69 100644
--- a/plugins/FeedReader/gui/FeedReaderDialog.ui
+++ b/plugins/FeedReader/gui/FeedReaderDialog.ui
@@ -150,7 +150,7 @@
-
+ Qt::CustomContextMenu
@@ -186,9 +186,9 @@
- RSTreeWidget
+ FeedTreeWidgetQTreeWidget
- gui/common/RSTreeWidget.h
+ gui/FeedTreeWidget.hRSTabWidget
diff --git a/plugins/FeedReader/gui/FeedReaderMessageWidget.cpp b/plugins/FeedReader/gui/FeedReaderMessageWidget.cpp
index 1550bc636..59f72103c 100644
--- a/plugins/FeedReader/gui/FeedReaderMessageWidget.cpp
+++ b/plugins/FeedReader/gui/FeedReaderMessageWidget.cpp
@@ -25,6 +25,7 @@
#include
#include
#include
+#include
#include "FeedReaderMessageWidget.h"
#include "ui_FeedReaderMessageWidget.h"
@@ -36,8 +37,14 @@
#include "gui/settings/rsharesettings.h"
#include "util/HandleRichText.h"
#include "util/QtVersion.h"
+#include "gui/Posted/PostedCreatePostDialog.h"
+#include "gui/gxsforums/CreateGxsForumMsg.h"
+#include "gui/common/FilesDefs.h"
+#include "util/imageutil.h"
#include "retroshare/rsiface.h"
+#include "retroshare/rsgxsforums.h"
+#include "retroshare/rsposted.h"
#define COLUMN_MSG_COUNT 4
#define COLUMN_MSG_TITLE 0
@@ -83,6 +90,12 @@ FeedReaderMessageWidget::FeedReaderMessageWidget(uint32_t feedId, RsFeedReader *
connect(ui->msgRemoveButton, SIGNAL(clicked()), this, SLOT(removeMsg()));
connect(ui->feedProcessButton, SIGNAL(clicked()), this, SLOT(processFeed()));
+ /* Set initial size the splitter */
+ QList sizes;
+ sizes << 250 << width(); // Qt calculates the right sizes
+ ui->pictureSplitter->setSizes(sizes);
+ ui->pictureSplitter->hide();
+
// create timer for navigation
mTimer = new QTimer(this);
mTimer->setInterval(300);
@@ -115,6 +128,10 @@ FeedReaderMessageWidget::FeedReaderMessageWidget(uint32_t feedId, RsFeedReader *
ui->filterLineEdit->addFilter(QIcon(), tr("Author"), COLUMN_MSG_AUTHOR, tr("Search Author"));
ui->filterLineEdit->setCurrentFilter(COLUMN_MSG_TITLE);
+ /* add image actions */
+ ui->attachmentLabel->addContextMenuAction(ui->actionAttachmentCopyLinkLocation);
+ connect(ui->actionAttachmentCopyLinkLocation, &QAction::triggered, this, &FeedReaderMessageWidget::attachmentCopyLinkLocation);
+
/* load settings */
processSettings(true);
@@ -175,6 +192,7 @@ void FeedReaderMessageWidget::processSettings(bool load)
// state of splitter
ui->msgSplitter->restoreState(Settings->value("msgSplitter").toByteArray());
+ ui->pictureSplitter->restoreState(Settings->value("pictureSplitter").toByteArray());
} else {
// save settings
@@ -183,6 +201,7 @@ void FeedReaderMessageWidget::processSettings(bool load)
// state of splitter
Settings->setValue("msgSplitter", ui->msgSplitter->saveState());
+ Settings->setValue("pictureSplitter", ui->pictureSplitter->saveState());
}
Settings->endGroup();
@@ -245,9 +264,19 @@ void FeedReaderMessageWidget::setFeedId(uint32_t feedId)
ui->msgReadAllButton->setEnabled(false);
ui->msgTreeWidget->setPlaceholderText("");
} else {
- if (mFeedInfo.flag.forum) {
+ if (mFeedInfo.flag.forum || mFeedInfo.flag.posted) {
ui->msgReadAllButton->setEnabled(false);
- ui->msgTreeWidget->setPlaceholderText(tr("The messages will be added to the forum"));
+
+ if (mFeedInfo.flag.forum && mFeedInfo.flag.posted) {
+ ui->msgTreeWidget->setPlaceholderText(tr("The messages will be added to the forum and the board"));
+ } else {
+ if (mFeedInfo.flag.forum) {
+ ui->msgTreeWidget->setPlaceholderText(tr("The messages will be added to the forum"));
+ }
+ if (mFeedInfo.flag.posted) {
+ ui->msgTreeWidget->setPlaceholderText(tr("The messages will be added to the board"));
+ }
+ }
} else {
ui->msgReadAllButton->setEnabled(true);
ui->msgTreeWidget->setPlaceholderText("");
@@ -340,6 +369,20 @@ void FeedReaderMessageWidget::msgTreeCustomPopupMenu(QPoint /*point*/)
action = contextMnu.addAction(QIcon(""), tr("Remove"), this, SLOT(removeMsg()));
action->setEnabled(!selectedItems.empty());
+ if (selectedItems.size() == 1 && (mFeedReader->forums() || mFeedReader->posted())) {
+ contextMnu.addSeparator();
+
+ if (mFeedReader->forums()) {
+ QMenu *menu = contextMnu.addMenu(tr("Add to forum"));
+ connect(menu, SIGNAL(aboutToShow()), this, SLOT(fillForumMenu()));
+ }
+
+ if (mFeedReader->posted()) {
+ QMenu *menu = contextMnu.addMenu(tr("Add to board"));
+ connect(menu, SIGNAL(aboutToShow()), this, SLOT(fillPostedMenu()));
+ }
+ }
+
contextMnu.addSeparator();
action = contextMnu.addAction(QIcon(""), tr("Retransform"), this, SLOT(retransformMsg()));
@@ -571,6 +614,21 @@ void FeedReaderMessageWidget::msgItemChanged()
mTimer->start();
}
+void FeedReaderMessageWidget::clearMessage()
+{
+ ui->msgTitle->clear();
+// ui->msgLink->clear();
+ ui->msgText->clear();
+ ui->msgTextSplitter->clear();
+ ui->attachmentLabel->clear();
+ ui->linkButton->setEnabled(false);
+ ui->msgText->show();
+ ui->pictureSplitter->hide();
+
+ ui->actionAttachmentCopyLinkLocation->setData(QVariant());
+ ui->actionAttachmentCopyLinkLocation->setEnabled(false);
+}
+
void FeedReaderMessageWidget::updateCurrentMessage()
{
mTimer->stop();
@@ -580,10 +638,7 @@ void FeedReaderMessageWidget::updateCurrentMessage()
std::string msgId = currentMsgId();
if (mFeedId == 0 || msgId.empty()) {
- ui->msgTitle->clear();
-// ui->msgLink->clear();
- ui->msgText->clear();
- ui->linkButton->setEnabled(false);
+ clearMessage();
ui->msgReadButton->setEnabled(false);
ui->msgUnreadButton->setEnabled(false);
@@ -594,10 +649,7 @@ void FeedReaderMessageWidget::updateCurrentMessage()
QTreeWidgetItem *item = ui->msgTreeWidget->currentItem();
if (!item) {
/* there is something wrong */
- ui->msgTitle->clear();
-// ui->msgLink->clear();
- ui->msgText->clear();
- ui->linkButton->setEnabled(false);
+ clearMessage();
ui->msgReadButton->setEnabled(false);
ui->msgUnreadButton->setEnabled(false);
@@ -612,10 +664,7 @@ void FeedReaderMessageWidget::updateCurrentMessage()
/* get msg */
FeedMsgInfo msgInfo;
if (!mFeedReader->getMsgInfo(mFeedId, msgId, msgInfo)) {
- ui->msgTitle->clear();
-// ui->msgLink->clear();
- ui->msgText->clear();
- ui->linkButton->setEnabled(false);
+ clearMessage();
return;
}
@@ -635,9 +684,41 @@ void FeedReaderMessageWidget::updateCurrentMessage()
setMsgAsReadUnread(row, setToReadOnActive);
}
+ ui->actionAttachmentCopyLinkLocation->setData(QVariant());
+ ui->actionAttachmentCopyLinkLocation->setEnabled(false);
+
+ if (!msgInfo.attachment.empty()) {
+ QByteArray imageData((char*) msgInfo.attachment.data(), msgInfo.attachment.size());
+ QPixmap pixmap;
+ if (pixmap.loadFromData(imageData)) {
+ ui->attachmentLabel->setPixmap(pixmap);
+ ui->pictureSplitter->show();
+ ui->msgText->hide();
+ } else {
+ ui->pictureSplitter->hide();
+ ui->msgText->show();
+ }
+
+ if (!msgInfo.attachmentLink.empty()) {
+ ui->actionAttachmentCopyLinkLocation->setData(QString::fromUtf8(msgInfo.attachmentLink.c_str()));
+ ui->actionAttachmentCopyLinkLocation->setEnabled(true);
+ }
+ } else {
+ ui->pictureSplitter->hide();
+ ui->msgText->show();
+ }
+
QString msgTxt = RsHtml().formatText(ui->msgText->document(), QString::fromUtf8((msgInfo.descriptionTransformed.empty() ? msgInfo.description : msgInfo.descriptionTransformed).c_str()), RSHTML_FORMATTEXT_EMBED_LINKS);
- ui->msgText->setHtml(msgTxt);
+ if (ui->pictureSplitter->isVisible()) {
+ ui->msgTextSplitter->setHtml(msgTxt);
+ ui->msgText->clear();
+ } else {
+ ui->msgText->setHtml(msgTxt);
+ ui->msgTextSplitter->clear();
+ ui->attachmentLabel->clear();
+ }
+
ui->msgTitle->setText(QString::fromUtf8(msgInfo.title.c_str()));
ui->linkButton->setEnabled(!msgInfo.link.empty());
@@ -714,11 +795,11 @@ void FeedReaderMessageWidget::toggleMsgText()
void FeedReaderMessageWidget::toggleMsgText_internal()
{
if (ui->expandButton->isChecked()) {
- ui->msgText->setVisible(true);
+ ui->horizontalLayoutWidget->setVisible(true);
ui->expandButton->setIcon(QIcon(QString(":/images/edit_remove24.png")));
ui->expandButton->setToolTip(tr("Hide"));
} else {
- ui->msgText->setVisible(false);
+ ui->horizontalLayoutWidget->setVisible(false);
ui->expandButton->setIcon(QIcon(QString(":/images/edit_add24.png")));
ui->expandButton->setToolTip(tr("Expand"));
}
@@ -840,3 +921,123 @@ void FeedReaderMessageWidget::openLinkMsg()
QDesktopServices::openUrl(QUrl(link));
}
+
+void FeedReaderMessageWidget::fillForumMenu()
+{
+ QMenu *menu = dynamic_cast(sender()) ;
+ if (!menu) {
+ return;
+ }
+
+ disconnect(menu, SIGNAL(aboutToShow()), this, SLOT(fillForumMenu()));
+
+ std::vector groups;
+ if (mFeedReader->getForumGroups(groups, true)) {
+ for (std::vector::iterator it = groups.begin(); it != groups.end(); ++it) {
+ const RsGxsForumGroup &group = *it;
+ QAction *action = menu->addAction(QString::fromUtf8(group.mMeta.mGroupName.c_str()), this, SLOT(addToForum()));
+ action->setData(QString::fromUtf8(group.mMeta.mGroupId.toStdString().c_str()));
+ }
+ }
+}
+
+void FeedReaderMessageWidget::fillPostedMenu()
+{
+ QMenu *menu = dynamic_cast(sender()) ;
+ if (!menu) {
+ return;
+ }
+
+ disconnect(menu, SIGNAL(aboutToShow()), this, SLOT(fillPostedMenu()));
+
+ std::vector groups;
+ if (mFeedReader->getPostedGroups(groups, true)) {
+ for (std::vector::iterator it = groups.begin(); it != groups.end(); ++it) {
+ const RsPostedGroup &group = *it;
+ QAction *action = menu->addAction(QString::fromUtf8(group.mMeta.mGroupName.c_str()), this, SLOT(addToPosted()));
+ action->setData(QString::fromUtf8(group.mMeta.mGroupId.toStdString().c_str()));
+ }
+ }
+}
+
+void FeedReaderMessageWidget::addToForum()
+{
+ QAction *action = dynamic_cast(sender()) ;
+ if (!action) {
+ return;
+ }
+
+ QString id = action->data().toString();
+ if (id.isEmpty()) {
+ return;
+ }
+
+ QList selectedItems = ui->msgTreeWidget->selectedItems();
+ if (selectedItems.size() != 1) {
+ return;
+ }
+
+ std::string msgId = selectedItems[0]->data(COLUMN_MSG_DATA, ROLE_MSG_ID).toString().toStdString();
+ FeedMsgInfo msgInfo;
+ if (!mFeedReader->getMsgInfo(mFeedId, msgId, msgInfo)) {
+ return;
+ }
+
+ RsGxsGroupId forumId(id.toStdString());
+
+ CreateGxsForumMsg *msgDialog = new CreateGxsForumMsg(forumId, RsGxsMessageId(), RsGxsMessageId(), RsGxsId()) ;
+ msgDialog->setSubject(QString::fromUtf8(msgInfo.title.c_str()));
+ msgDialog->insertPastedText(QString::fromUtf8(msgInfo.description.c_str()));
+ msgDialog->show();
+}
+
+void FeedReaderMessageWidget::addToPosted()
+{
+ QAction *action = dynamic_cast(sender()) ;
+ if (!action) {
+ return;
+ }
+
+ QString id = action->data().toString();
+ if (id.isEmpty()) {
+ return;
+ }
+
+ QList selectedItems = ui->msgTreeWidget->selectedItems();
+ if (selectedItems.size() != 1) {
+ return;
+ }
+
+ std::string msgId = selectedItems[0]->data(COLUMN_MSG_DATA, ROLE_MSG_ID).toString().toStdString();
+ FeedMsgInfo msgInfo;
+ if (!mFeedReader->getMsgInfo(mFeedId, msgId, msgInfo)) {
+ return;
+ }
+
+ RsGxsGroupId postedId(id.toStdString());
+
+ PostedCreatePostDialog *msgDialog = new PostedCreatePostDialog(mFeedReader->posted(), postedId);
+ msgDialog->setTitle(QString::fromUtf8(msgInfo.title.c_str()));
+ msgDialog->setNotes(QString::fromUtf8(msgInfo.description.c_str()));
+ msgDialog->setLink(QString::fromUtf8(msgInfo.link.c_str()));
+ msgDialog->show();
+}
+
+void FeedReaderMessageWidget::attachmentCopyLinkLocation()
+{
+ QAction *action = dynamic_cast(sender()) ;
+ if (!action) {
+ return;
+ }
+
+ QVariant data = action->data();
+ if (!data.isValid()) {
+ return;
+ }
+
+ if (data.type() != QVariant::String) {
+ return;
+ }
+
+ QApplication::clipboard()->setText(data.toString());
+}
diff --git a/plugins/FeedReader/gui/FeedReaderMessageWidget.h b/plugins/FeedReader/gui/FeedReaderMessageWidget.h
index 452c407ad..1223b3b4e 100644
--- a/plugins/FeedReader/gui/FeedReaderMessageWidget.h
+++ b/plugins/FeedReader/gui/FeedReaderMessageWidget.h
@@ -73,6 +73,11 @@ private slots:
void openLinkMsg();
void copyLinkMsg();
void retransformMsg();
+ void fillForumMenu();
+ void fillPostedMenu();
+ void addToForum();
+ void addToPosted();
+ void attachmentCopyLinkLocation();
/* FeedReaderNotify */
void feedChanged(uint32_t feedId, int type);
@@ -88,6 +93,7 @@ private:
void filterItem(QTreeWidgetItem *item, const QString &text, int filterColumn);
void filterItem(QTreeWidgetItem *item);
void toggleMsgText_internal();
+ void clearMessage();
bool mProcessSettings;
RSTreeWidgetItemCompareRole *mMsgCompareRole;
diff --git a/plugins/FeedReader/gui/FeedReaderMessageWidget.ui b/plugins/FeedReader/gui/FeedReaderMessageWidget.ui
index 3542c5375..fada5279e 100644
--- a/plugins/FeedReader/gui/FeedReaderMessageWidget.ui
+++ b/plugins/FeedReader/gui/FeedReaderMessageWidget.ui
@@ -182,7 +182,7 @@
-
+ :/images/message-state-header.png:/images/message-state-header.png
@@ -245,7 +245,7 @@
-
+ :/images/edit_remove24.png:/images/edit_remove24.png
@@ -260,20 +260,63 @@
-
-
-
- 0
- 10
-
-
-
- Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse
-
+
+
+
+
+
+
+ 0
+ 10
+
+
+
+ Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse
+
+
+
+
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+ pictureLabel
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+
+
+ 0
+ 10
+
+
+
+ Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse
+
+
+
+
+
+
+
+ Copy Link Location
+
+
@@ -297,10 +340,14 @@
gui/common/ElidedLabel.h1
+
+ AspectRatioPixmapLabel
+ QLabel
+ util/AspectRatioPixmapLabel.h
+
-
diff --git a/plugins/FeedReader/gui/FeedReaderNotify.cpp b/plugins/FeedReader/gui/FeedReaderNotify.cpp
index f2aa7ad1d..95728b225 100644
--- a/plugins/FeedReader/gui/FeedReaderNotify.cpp
+++ b/plugins/FeedReader/gui/FeedReaderNotify.cpp
@@ -33,3 +33,8 @@ void FeedReaderNotify::notifyMsgChanged(uint32_t feedId, const std::string &msgI
{
emit msgChanged(feedId, QString::fromStdString(msgId), type);
}
+
+void FeedReaderNotify::notifyOptimizeImage()
+{
+ emit optimizeImage();
+}
diff --git a/plugins/FeedReader/gui/FeedReaderNotify.h b/plugins/FeedReader/gui/FeedReaderNotify.h
index 5a8ad712a..5913f1985 100644
--- a/plugins/FeedReader/gui/FeedReaderNotify.h
+++ b/plugins/FeedReader/gui/FeedReaderNotify.h
@@ -34,10 +34,12 @@ public:
/* RsFeedReaderNotify */
virtual void notifyFeedChanged(uint32_t feedId, int type);
virtual void notifyMsgChanged(uint32_t feedId, const std::string &msgId, int type);
+ virtual void notifyOptimizeImage();
signals:
void feedChanged(uint32_t feedId, int type);
void msgChanged(uint32_t feedId, const QString &msgId, int type);
+ void optimizeImage();
};
#endif
diff --git a/plugins/FeedReader/gui/FeedReaderStringDefs.cpp b/plugins/FeedReader/gui/FeedReaderStringDefs.cpp
index 99c537498..53a62e669 100644
--- a/plugins/FeedReader/gui/FeedReaderStringDefs.cpp
+++ b/plugins/FeedReader/gui/FeedReaderStringDefs.cpp
@@ -23,27 +23,27 @@
#include "FeedReaderStringDefs.h"
-bool FeedReaderStringDefs::showError(QWidget *parent, RsFeedAddResult result, const QString &title, const QString &text)
+bool FeedReaderStringDefs::showError(QWidget *parent, RsFeedResult result, const QString &title, const QString &text)
{
QString error;
switch (result) {
- case RS_FEED_ADD_RESULT_SUCCESS:
+ case RS_FEED_RESULT_SUCCESS:
/* no error */
return false;
- case RS_FEED_ADD_RESULT_FEED_NOT_FOUND:
+ case RS_FEED_RESULT_FEED_NOT_FOUND:
error = QApplication::translate("FeedReaderStringDefs", "Feed not found.");
break;
- case RS_FEED_ADD_RESULT_PARENT_NOT_FOUND:
+ case RS_FEED_RESULT_PARENT_NOT_FOUND:
error = QApplication::translate("FeedReaderStringDefs", "Parent not found.");
break;
- case RS_FEED_ADD_RESULT_PARENT_IS_NO_FOLDER:
+ case RS_FEED_RESULT_PARENT_IS_NO_FOLDER:
error = QApplication::translate("FeedReaderStringDefs", "Parent is no folder.");
break;
- case RS_FEED_ADD_RESULT_FEED_IS_FOLDER:
+ case RS_FEED_RESULT_FEED_IS_FOLDER:
error = QApplication::translate("FeedReaderStringDefs", "Feed is a folder.");
break;
- case RS_FEED_ADD_RESULT_FEED_IS_NO_FOLDER:
+ case RS_FEED_RESULT_FEED_IS_NO_FOLDER:
error = QApplication::translate("FeedReaderStringDefs", "Feed is no folder.");
break;
default:
@@ -101,6 +101,9 @@ QString FeedReaderStringDefs::errorString(RsFeedReaderErrorState errorState, con
case RS_FEED_ERRORSTATE_DOWNLOAD_UNKOWN_RESPONSE_CODE:
errorText = QApplication::translate("FeedReaderStringDefs", "Unknown response code");
break;
+ case RS_FEED_ERRORSTATE_DOWNLOAD_BLOCKED:
+ errorText = QApplication::translate("FeedReaderStringDefs", "Download blocked");
+ break;
/* process */
case RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR:
@@ -109,9 +112,9 @@ QString FeedReaderStringDefs::errorString(RsFeedReaderErrorState errorState, con
case RS_FEED_ERRORSTATE_PROCESS_UNKNOWN_FORMAT:
errorText = QApplication::translate("FeedReaderStringDefs", "Unknown XML format");
break;
- case RS_FEED_ERRORSTATE_PROCESS_FORUM_CREATE:
- errorText = QApplication::translate("FeedReaderStringDefs", "Can't create forum");
- break;
+// case RS_FEED_ERRORSTATE_PROCESS_FORUM_CREATE:
+// errorText = QApplication::translate("FeedReaderStringDefs", "Can't create forum");
+// break;
case RS_FEED_ERRORSTATE_PROCESS_FORUM_NOT_FOUND:
errorText = QApplication::translate("FeedReaderStringDefs", "Forum not found");
break;
@@ -121,6 +124,21 @@ QString FeedReaderStringDefs::errorString(RsFeedReaderErrorState errorState, con
case RS_FEED_ERRORSTATE_PROCESS_FORUM_NO_AUTHOR:
errorText = QApplication::translate("FeedReaderStringDefs", "Forum has no author");
break;
+// case RS_FEED_ERRORSTATE_PROCESS_POSTED_CREATE:
+// errorText = QApplication::translate("FeedReaderStringDefs", "Can't create board");
+// break;
+ case RS_FEED_ERRORSTATE_PROCESS_POSTED_NOT_FOUND:
+ errorText = QApplication::translate("FeedReaderStringDefs", "Board not found");
+ break;
+ case RS_FEED_ERRORSTATE_PROCESS_POSTED_NO_ADMIN:
+ errorText = QApplication::translate("FeedReaderStringDefs", "You are not admin of the board");
+ break;
+ case RS_FEED_ERRORSTATE_PROCESS_POSTED_NO_AUTHOR:
+ errorText = QApplication::translate("FeedReaderStringDefs", "Board has no author");
+ break;
+ case RS_FEED_ERRORSTATE_PROCESS_POST:
+ errorText = QApplication::translate("FeedReaderStringDefs", "Some posts could not be created");
+ break;
case RS_FEED_ERRORSTATE_PROCESS_HTML_ERROR:
errorText = QApplication::translate("FeedReaderStringDefs", "Can't read html");
diff --git a/plugins/FeedReader/gui/FeedReaderStringDefs.h b/plugins/FeedReader/gui/FeedReaderStringDefs.h
index f4b384c8f..587621e7d 100644
--- a/plugins/FeedReader/gui/FeedReaderStringDefs.h
+++ b/plugins/FeedReader/gui/FeedReaderStringDefs.h
@@ -30,7 +30,7 @@ class QWidget;
class FeedReaderStringDefs
{
public:
- static bool showError(QWidget *parent, RsFeedAddResult result, const QString &title, const QString &text);
+ static bool showError(QWidget *parent, RsFeedResult result, const QString &title, const QString &text);
static QString workState(FeedInfo::WorkState state);
static QString errorString(const FeedInfo &feedInfo);
static QString errorString(RsFeedReaderErrorState errorState, const std::string &errorString);
diff --git a/plugins/FeedReader/gui/FeedTreeWidget.cpp b/plugins/FeedReader/gui/FeedTreeWidget.cpp
new file mode 100644
index 000000000..37baaf235
--- /dev/null
+++ b/plugins/FeedReader/gui/FeedTreeWidget.cpp
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * plugins/FeedReader/gui/FeedTreeWidget.cpp *
+ * *
+ * Copyright (C) 2012 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 "FeedTreeWidget.h"
+
+FeedTreeWidget::FeedTreeWidget(QWidget *parent) : RSTreeWidget(parent)
+{
+ mDraggedItem = NULL;
+}
+
+void FeedTreeWidget::dragEnterEvent(QDragEnterEvent *event)
+{
+ mDraggedItem = currentItem();
+ RSTreeWidget::dragEnterEvent(event);
+}
+
+void FeedTreeWidget::dragLeaveEvent(QDragLeaveEvent *event)
+{
+ RSTreeWidget::dragLeaveEvent(event);
+ mDraggedItem = NULL;
+}
+
+bool FeedTreeWidget::canDrop(QDropEvent *event, QTreeWidgetItem **dropItem)
+{
+ if (dropItem) {
+ *dropItem = NULL;
+ }
+
+ if (!mDraggedItem) {
+ /* no drag item */
+ return false;
+ }
+
+ QModelIndex droppedIndex = indexAt(event->pos());
+ if (!droppedIndex.isValid()) {
+ /* no drop target */
+ return false;
+ }
+
+ QTreeWidgetItem *dropItemIntern = itemFromIndex(droppedIndex);
+ if (!dropItemIntern) {
+ /* no drop item */
+ return false;
+ }
+
+ if ((dropItemIntern->flags() & Qt::ItemIsDropEnabled) == 0) {
+ /* drop is disabled */
+ return false;
+ }
+
+ if (dropItemIntern == mDraggedItem->parent()) {
+ /* drag item parent */
+ return false;
+ }
+
+ if (dropItem) {
+ *dropItem = dropItemIntern;
+ }
+
+ return true;
+}
+
+void FeedTreeWidget::dragMoveEvent(QDragMoveEvent *event)
+{
+ if (!canDrop(event)) {
+ event->ignore();
+ return;
+ }
+
+ RSTreeWidget::dragMoveEvent(event);
+}
+
+void FeedTreeWidget::dropEvent(QDropEvent *event)
+{
+ QTreeWidgetItem *dropItem;
+ if (!canDrop(event, &dropItem)) {
+ event->ignore();
+ return;
+ }
+
+ if (!mDraggedItem) {
+ /* no drag item */
+ event->ignore();
+ return;
+ }
+
+ QTreeWidgetItem *draggedParent = mDraggedItem->parent();
+ if (!draggedParent) {
+ /* no drag item parent */
+ event->ignore();
+ return;
+ }
+
+ if (!dropItem) {
+ /* no drop item */
+ event->ignore();
+ return;
+ }
+
+ emit feedReparent(mDraggedItem, dropItem);
+}
diff --git a/plugins/FeedReader/gui/FeedTreeWidget.h b/plugins/FeedReader/gui/FeedTreeWidget.h
new file mode 100644
index 000000000..e9eb197a9
--- /dev/null
+++ b/plugins/FeedReader/gui/FeedTreeWidget.h
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * plugins/FeedReader/gui/FeedTreeWidget.h *
+ * *
+ * Copyright (C) 2012 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 _FEEDTREEWIDGET_H
+#define _FEEDTREEWIDGET_H
+
+#include "gui/common/RSTreeWidget.h"
+
+/* Subclassing RSTreeWidget */
+class FeedTreeWidget : public RSTreeWidget
+{
+ Q_OBJECT
+
+public:
+ FeedTreeWidget(QWidget *parent = 0);
+
+Q_SIGNALS:
+ void feedReparent(QTreeWidgetItem *item, QTreeWidgetItem *newParent);
+
+protected:
+ void dragEnterEvent(QDragEnterEvent *event);
+ void dragLeaveEvent(QDragLeaveEvent *event);
+ void dragMoveEvent(QDragMoveEvent *event);
+ void dropEvent(QDropEvent *event);
+
+private:
+ bool canDrop(QDropEvent *event, QTreeWidgetItem **dropItem = NULL);
+
+private:
+ QTreeWidgetItem *mDraggedItem;
+};
+
+#endif
diff --git a/plugins/FeedReader/interface/rsFeedReader.h b/plugins/FeedReader/interface/rsFeedReader.h
index bf524ad0c..326d3e022 100644
--- a/plugins/FeedReader/interface/rsFeedReader.h
+++ b/plugins/FeedReader/interface/rsFeedReader.h
@@ -25,8 +25,13 @@
#include
#include
#include
+#include
class RsFeedReader;
+class RsGxsForums;
+class RsPosted;
+class RsGxsForumGroup;
+class RsPostedGroup;
extern RsFeedReader *rsFeedReader;
enum RsFeedReaderErrorState {
@@ -38,14 +43,20 @@ enum RsFeedReaderErrorState {
RS_FEED_ERRORSTATE_DOWNLOAD_UNKNOWN_CONTENT_TYPE = 3,
RS_FEED_ERRORSTATE_DOWNLOAD_NOT_FOUND = 4,
RS_FEED_ERRORSTATE_DOWNLOAD_UNKOWN_RESPONSE_CODE = 5,
+ RS_FEED_ERRORSTATE_DOWNLOAD_BLOCKED = 6,
/* process */
RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR = 50,
RS_FEED_ERRORSTATE_PROCESS_UNKNOWN_FORMAT = 51,
- RS_FEED_ERRORSTATE_PROCESS_FORUM_CREATE = 100,
+// RS_FEED_ERRORSTATE_PROCESS_FORUM_CREATE = 100,
RS_FEED_ERRORSTATE_PROCESS_FORUM_NOT_FOUND = 101,
RS_FEED_ERRORSTATE_PROCESS_FORUM_NO_ADMIN = 102,
RS_FEED_ERRORSTATE_PROCESS_FORUM_NO_AUTHOR = 103,
+// RS_FEED_ERRORSTATE_PROCESS_POSTED_CREATE = 104,
+ RS_FEED_ERRORSTATE_PROCESS_POSTED_NOT_FOUND = 105,
+ RS_FEED_ERRORSTATE_PROCESS_POSTED_NO_ADMIN = 106,
+ RS_FEED_ERRORSTATE_PROCESS_POSTED_NO_AUTHOR = 107,
+ RS_FEED_ERRORSTATE_PROCESS_POST = 108,
RS_FEED_ERRORSTATE_PROCESS_HTML_ERROR = 150,
RS_FEED_ERRORSTATE_PROCESS_XPATH_INTERNAL_ERROR = 151,
@@ -56,14 +67,14 @@ enum RsFeedReaderErrorState {
RS_FEED_ERRORSTATE_PROCESS_XSLT_NO_RESULT = 156
};
-enum RsFeedAddResult
+enum RsFeedResult
{
- RS_FEED_ADD_RESULT_SUCCESS,
- RS_FEED_ADD_RESULT_FEED_NOT_FOUND,
- RS_FEED_ADD_RESULT_PARENT_NOT_FOUND,
- RS_FEED_ADD_RESULT_PARENT_IS_NO_FOLDER,
- RS_FEED_ADD_RESULT_FEED_IS_FOLDER,
- RS_FEED_ADD_RESULT_FEED_IS_NO_FOLDER
+ RS_FEED_RESULT_SUCCESS,
+ RS_FEED_RESULT_FEED_NOT_FOUND,
+ RS_FEED_RESULT_PARENT_NOT_FOUND,
+ RS_FEED_RESULT_PARENT_IS_NO_FOLDER,
+ RS_FEED_RESULT_FEED_IS_FOLDER,
+ RS_FEED_RESULT_FEED_IS_NO_FOLDER
};
enum RsFeedTransformationType
@@ -102,6 +113,8 @@ public:
flag.deactivated = false;
flag.forum = false;
flag.updateForumInfo = false;
+ flag.posted = false;
+ flag.updatePostedInfo = false;
flag.embedImages = false;
flag.saveCompletePage = false;
flag.preview = false;
@@ -122,6 +135,7 @@ public:
time_t lastUpdate;
uint32_t storageTime;
std::string forumId;
+ std::string postedId;
WorkState workstate;
RsFeedReaderErrorState errorState;
std::string errorString;
@@ -141,6 +155,11 @@ public:
bool deactivated : 1;
bool forum : 1;
bool updateForumInfo : 1;
+ bool posted : 1;
+ bool updatePostedInfo : 1;
+ bool postedFirstImage : 1;
+ bool postedOnlyImage : 1;
+ bool postedShrinkImage : 1;
bool embedImages : 1;
bool saveCompletePage : 1;
bool preview : 1;
@@ -166,6 +185,9 @@ public:
std::string description;
std::string descriptionTransformed;
time_t pubDate;
+ std::string attachmentLink;
+ std::vector attachment;
+ std::string attachmentMimeType;
struct {
bool isnew : 1;
@@ -174,6 +196,32 @@ public:
} flag;
};
+class FeedReaderOptimizeImageTask
+{
+public:
+ enum Type {
+ POSTED,
+ SIZE
+ };
+
+public:
+ Type mType;
+ std::vector mImage;
+ std::string mMimeType;
+ std::vector mImageResult;
+ std::string mMimeTypeResult;
+ bool mResult;
+
+public:
+ FeedReaderOptimizeImageTask(Type type, const std::vector &image, const std::string &mimeType)
+ {
+ mType = type;
+ mImage = image;
+ mMimeType = mimeType;
+ mResult = false;
+ }
+};
+
class RsFeedReaderNotify
{
public:
@@ -181,6 +229,7 @@ public:
virtual void notifyFeedChanged(uint32_t /*feedId*/, int /*type*/) {}
virtual void notifyMsgChanged(uint32_t /*feedId*/, const std::string &/*msgId*/, int /*type*/) {}
+ virtual void notifyOptimizeImage() {}
};
class RsFeedReader
@@ -201,24 +250,33 @@ public:
virtual bool getSaveInBackground() = 0;
virtual void setSaveInBackground(bool saveInBackground) = 0;
- virtual RsFeedAddResult addFolder(uint32_t parentId, const std::string &name, uint32_t &feedId) = 0;
- virtual RsFeedAddResult setFolder(uint32_t feedId, const std::string &name) = 0;
- virtual RsFeedAddResult addFeed(const FeedInfo &feedInfo, uint32_t &feedId) = 0;
- virtual RsFeedAddResult setFeed(uint32_t feedId, const FeedInfo &feedInfo) = 0;
- virtual bool removeFeed(uint32_t feedId) = 0;
- virtual bool addPreviewFeed(const FeedInfo &feedInfo, uint32_t &feedId) = 0;
- virtual void getFeedList(uint32_t parentId, std::list &feedInfos) = 0;
- virtual bool getFeedInfo(uint32_t feedId, FeedInfo &feedInfo) = 0;
- virtual bool getMsgInfo(uint32_t feedId, const std::string &msgId, FeedMsgInfo &msgInfo) = 0;
- virtual bool removeMsg(uint32_t feedId, const std::string &msgId) = 0;
- virtual bool removeMsgs(uint32_t feedId, const std::list &msgIds) = 0;
- virtual bool getMessageCount(uint32_t feedId, uint32_t *msgCount, uint32_t *newCount, uint32_t *unreadCount) = 0;
- virtual bool getFeedMsgList(uint32_t feedId, std::list &msgInfos) = 0;
- virtual bool getFeedMsgIdList(uint32_t feedId, std::list &msgIds) = 0;
- virtual bool processFeed(uint32_t feedId) = 0;
- virtual bool setMessageRead(uint32_t feedId, const std::string &msgId, bool read) = 0;
- virtual bool retransformMsg(uint32_t feedId, const std::string &msgId) = 0;
- virtual bool clearMessageCache(uint32_t feedId) = 0;
+ virtual RsFeedResult addFolder(uint32_t parentId, const std::string &name, uint32_t &feedId) = 0;
+ virtual RsFeedResult setFolder(uint32_t feedId, const std::string &name) = 0;
+ virtual RsFeedResult addFeed(const FeedInfo &feedInfo, uint32_t &feedId) = 0;
+ virtual RsFeedResult setFeed(uint32_t feedId, const FeedInfo &feedInfo) = 0;
+ virtual RsFeedResult setParent(uint32_t feedId, uint32_t parentId) = 0;
+ virtual bool removeFeed(uint32_t feedId) = 0;
+ virtual bool addPreviewFeed(const FeedInfo &feedInfo, uint32_t &feedId) = 0;
+ virtual void getFeedList(uint32_t parentId, std::list &feedInfos) = 0;
+ virtual bool getFeedInfo(uint32_t feedId, FeedInfo &feedInfo) = 0;
+ virtual bool getMsgInfo(uint32_t feedId, const std::string &msgId, FeedMsgInfo &msgInfo) = 0;
+ virtual bool removeMsg(uint32_t feedId, const std::string &msgId) = 0;
+ virtual bool removeMsgs(uint32_t feedId, const std::list &msgIds) = 0;
+ virtual bool getMessageCount(uint32_t feedId, uint32_t *msgCount, uint32_t *newCount, uint32_t *unreadCount) = 0;
+ virtual bool getFeedMsgList(uint32_t feedId, std::list &msgInfos) = 0;
+ virtual bool getFeedMsgIdList(uint32_t feedId, std::list &msgIds) = 0;
+ virtual bool processFeed(uint32_t feedId) = 0;
+ virtual bool setMessageRead(uint32_t feedId, const std::string &msgId, bool read) = 0;
+ virtual bool retransformMsg(uint32_t feedId, const std::string &msgId) = 0;
+ virtual bool clearMessageCache(uint32_t feedId) = 0;
+
+ virtual RsGxsForums* forums() = 0;
+ virtual RsPosted* posted() = 0;
+ virtual bool getForumGroups(std::vector &groups, bool onlyOwn) = 0;
+ virtual bool getPostedGroups(std::vector &groups, bool onlyOwn) = 0;
+
+ virtual FeedReaderOptimizeImageTask *getOptimizeImageTask() = 0;
+ virtual void setOptimizeImageTaskResult(FeedReaderOptimizeImageTask *optimizeImageTask) = 0;
virtual RsFeedReaderErrorState processXPath(const std::list &xpathsToUse, const std::list &xpathsToRemove, std::string &description, std::string &errorString) = 0;
virtual RsFeedReaderErrorState processXslt(const std::string &xslt, std::string &description, std::string &errorString) = 0;
diff --git a/plugins/FeedReader/lang/FeedReader_en.ts b/plugins/FeedReader/lang/FeedReader_en.ts
index 89cde2fe0..707c8cec9 100644
--- a/plugins/FeedReader/lang/FeedReader_en.ts
+++ b/plugins/FeedReader/lang/FeedReader_en.ts
@@ -9,7 +9,32 @@
-
+
+ Board
+
+
+
+
+ Update board information
+
+
+
+
+ Only image
+
+
+
+
+ Use first image as board image
+
+
+
+
+ Shrink image
+
+
+
+ Authentication (not yet supported)
@@ -59,12 +84,12 @@
-
+ Embed images
-
+ Storage time
@@ -99,22 +124,22 @@
-
+ Type
-
+ Forum
-
+ Local Feed
-
+ Transformation
@@ -129,7 +154,7 @@
-
+ Misc
@@ -144,17 +169,17 @@
-
+ Update forum information
-
+ Save complete web page (experimental for local feeds)
-
+ Description:
@@ -164,18 +189,18 @@
-
+ Name:
-
+ Feed Details
-
-
+
+ Edit feed
@@ -277,7 +302,7 @@
FeedReaderDialog
-
+ Feeds
@@ -292,7 +317,7 @@
-
+ Message Folders
@@ -342,12 +367,12 @@
-
+ No name
-
+ Add new folder
@@ -398,11 +423,21 @@
Please enter a new name for the folder
+
+
+ Move feed
+
+
+
+
+ Cannot move feed.
+
+ FeedReaderFeedItem
-
+ Expand
@@ -500,7 +535,7 @@
-
+ Title
@@ -542,12 +577,22 @@
-
+ The messages will be added to the forum
-
+
+ The messages will be added to the forum and the board
+
+
+
+
+ The messages will be added to the board
+
+
+
+ No name
@@ -576,8 +621,18 @@
Remove
+
+
+ Add to forum
+
+
+ Add to board
+
+
+
+ Retransform
@@ -659,12 +714,12 @@
-
+ Unknown
-
+ Internal download error
@@ -688,6 +743,11 @@
Unknown response code
+
+
+ Download blocked
+
+ Internal process error
@@ -699,12 +759,7 @@
-
- Can't create forum
-
-
-
-
+ Forum not found
@@ -718,6 +773,26 @@
Forum has no author
+
+
+ Board not found
+
+
+
+
+ You are not admin of the board
+
+
+
+
+ Board has no author
+
+
+
+
+ Some posts could not be created
+
+ Can't read html
@@ -790,7 +865,7 @@
-
+ Name:
@@ -825,7 +900,7 @@
-
+ Title:
diff --git a/plugins/FeedReader/services/p3FeedReader.cc b/plugins/FeedReader/services/p3FeedReader.cc
index d70591c36..3e46a027c 100644
--- a/plugins/FeedReader/services/p3FeedReader.cc
+++ b/plugins/FeedReader/services/p3FeedReader.cc
@@ -24,6 +24,7 @@
#include "rsitems/rsconfigitems.h"
#include "retroshare/rsiface.h"
#include "retroshare/rsgxsforums.h"
+#include "retroshare/rsposted.h"
#include "util/rsstring.h"
#include "util/rstime.h"
#include "gxs/rsgenexchange.h"
@@ -34,6 +35,7 @@ RsFeedReader *rsFeedReader = NULL;
#define FEEDREADER_CLEAN_INTERVAL 1 * 60 * 60 // check every hour
#define FEEDREADER_FORUM_PREFIX "RSS: "
+#define FEEDREADER_POSTED_PREFIX "RSS: "
#define MAX_REQUEST_AGE 30 // 30 seconds
@@ -41,9 +43,9 @@ RsFeedReader *rsFeedReader = NULL;
* #define FEEDREADER_DEBUG
*********/
-p3FeedReader::p3FeedReader(RsPluginHandler* pgHandler, RsGxsForums *forums)
+p3FeedReader::p3FeedReader(RsPluginHandler* pgHandler, RsGxsForums *forums, RsPosted *posted)
: RsPQIService(RS_SERVICE_TYPE_PLUGIN_FEEDREADER, 5, pgHandler),
- mFeedReaderMtx("p3FeedReader"), mDownloadMutex("p3FeedReaderDownload"), mProcessMutex("p3FeedReaderProcess"), mPreviewMutex("p3FeedReaderPreview")
+ mFeedReaderMtx("p3FeedReader"), mDownloadMutex("p3FeedReaderDownload"), mProcessMutex("p3FeedReaderProcess"), mImageMutex("p3FeedReaderImage"), mPreviewMutex("p3FeedReaderPreview")
{
mNextFeedId = 1;
mNextMsgId = 1;
@@ -55,6 +57,7 @@ p3FeedReader::p3FeedReader(RsPluginHandler* pgHandler, RsGxsForums *forums)
mStandardProxyPort = 0;
mLastClean = 0;
mForums = forums;
+ mPosted = posted;
mNotify = NULL;
mSaveInBackground = false;
mStopped = false;
@@ -92,6 +95,7 @@ static void feedToInfo(const RsFeedReaderFeed *feed, FeedInfo &info)
info.updateInterval = feed->updateInterval;
info.lastUpdate = feed->lastUpdate;
info.forumId = feed->forumId;
+ info.postedId = feed->postedId;
info.storageTime = feed->storageTime;
info.errorState = feed->errorState;
info.errorString = feed->errorString;
@@ -110,6 +114,11 @@ static void feedToInfo(const RsFeedReaderFeed *feed, FeedInfo &info)
info.flag.deactivated = (feed->flag & RS_FEED_FLAG_DEACTIVATED);
info.flag.forum = (feed->flag & RS_FEED_FLAG_FORUM);
info.flag.updateForumInfo = (feed->flag & RS_FEED_FLAG_UPDATE_FORUM_INFO);
+ info.flag.posted = (feed->flag & RS_FEED_FLAG_POSTED);
+ info.flag.updatePostedInfo = (feed->flag & RS_FEED_FLAG_UPDATE_POSTED_INFO);
+ info.flag.postedFirstImage = (feed->flag & RS_FEED_FLAG_POSTED_FIRST_IMAGE);
+ info.flag.postedOnlyImage = (feed->flag & RS_FEED_FLAG_POSTED_ONLY_IMAGE);
+ info.flag.postedShrinkImage = (feed->flag & RS_FEED_FLAG_POSTED_SHRINK_IMAGE);
info.flag.embedImages = (feed->flag & RS_FEED_FLAG_EMBED_IMAGES);
info.flag.saveCompletePage = (feed->flag & RS_FEED_FLAG_SAVE_COMPLETE_PAGE);
@@ -151,6 +160,7 @@ static void infoToFeed(const FeedInfo &info, RsFeedReaderFeed *feed)
feed->storageTime = info.storageTime;
feed->forumId = info.forumId;
+ feed->postedId = info.postedId;
feed->transformationType = info.transformationType;
feed->xpathsToUse.ids = info.xpathsToUse;
@@ -193,6 +203,24 @@ static void infoToFeed(const FeedInfo &info, RsFeedReaderFeed *feed)
if (info.flag.updateForumInfo) {
feed->flag |= RS_FEED_FLAG_UPDATE_FORUM_INFO;
}
+ if (info.flag.posted) {
+ feed->flag |= RS_FEED_FLAG_POSTED;
+ }
+ if (info.flag.updatePostedInfo) {
+ feed->flag |= RS_FEED_FLAG_UPDATE_POSTED_INFO;
+ }
+ if (info.flag.updatePostedInfo) {
+ feed->flag |= RS_FEED_FLAG_UPDATE_POSTED_INFO;
+ }
+ if (info.flag.postedFirstImage) {
+ feed->flag |= RS_FEED_FLAG_POSTED_FIRST_IMAGE;
+ }
+ if (info.flag.postedOnlyImage) {
+ feed->flag |= RS_FEED_FLAG_POSTED_ONLY_IMAGE;
+ }
+ if (info.flag.postedShrinkImage) {
+ feed->flag |= RS_FEED_FLAG_POSTED_SHRINK_IMAGE;
+ }
}
static void feedMsgToInfo(const RsFeedReaderMsg *msg, FeedMsgInfo &info)
@@ -205,6 +233,13 @@ static void feedMsgToInfo(const RsFeedReaderMsg *msg, FeedMsgInfo &info)
info.description = msg->description;
info.descriptionTransformed = msg->descriptionTransformed;
info.pubDate = msg->pubDate;
+ info.attachmentLink = msg->attachmentLink;
+ if (!msg->attachment.empty()) {
+ p3FeedReaderThread::fromBase64(msg->attachment, info.attachment);
+ } else {
+ info.attachment.clear();
+ }
+ info.attachmentMimeType = msg->attachmentMimeType;
info.flag.isnew = (msg->flag & RS_FEEDMSG_FLAG_NEW);
info.flag.read = (msg->flag & RS_FEEDMSG_FLAG_READ);
@@ -245,7 +280,7 @@ void p3FeedReader::setStandardStorageTime(uint32_t storageTime)
if (mStandardStorageTime != storageTime) {
mStandardStorageTime = storageTime;
- IndicateConfigChanged();
+ IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_NOW);
}
}
@@ -262,7 +297,7 @@ void p3FeedReader::setStandardUpdateInterval(uint32_t updateInterval)
if (mStandardUpdateInterval != updateInterval) {
mStandardUpdateInterval = updateInterval;
- IndicateConfigChanged();
+ IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_NOW);
}
}
@@ -284,7 +319,7 @@ void p3FeedReader::setStandardProxy(bool useProxy, const std::string &proxyAddre
mStandardProxyAddress = proxyAddress;
mStandardProxyPort = proxyPort;
mStandardUseProxy = useProxy;
- IndicateConfigChanged();
+ IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_NOW);
}
}
@@ -301,7 +336,7 @@ void p3FeedReader::setSaveInBackground(bool saveInBackground)
if (saveInBackground != mSaveInBackground) {
mSaveInBackground = saveInBackground;
- IndicateConfigChanged();
+ IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_NOW);
}
}
@@ -342,7 +377,7 @@ void p3FeedReader::stopPreviewThreads_locked()
}
}
-RsFeedAddResult p3FeedReader::addFolder(uint32_t parentId, const std::string &name, uint32_t &feedId)
+RsFeedResult p3FeedReader::addFolder(uint32_t parentId, const std::string &name, uint32_t &feedId)
{
feedId = 0;
@@ -356,14 +391,14 @@ RsFeedAddResult p3FeedReader::addFolder(uint32_t parentId, const std::string &na
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReader::addFolder - parent id " << parentId << " not found" << std::endl;
#endif
- return RS_FEED_ADD_RESULT_PARENT_NOT_FOUND;
+ return RS_FEED_RESULT_PARENT_NOT_FOUND;
}
if ((parentIt->second->flag & RS_FEED_FLAG_FOLDER) == 0) {
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReader::addFolder - parent " << parentIt->second->name << " is no folder" << std::endl;
#endif
- return RS_FEED_ADD_RESULT_PARENT_IS_NO_FOLDER;
+ return RS_FEED_RESULT_PARENT_IS_NO_FOLDER;
}
}
@@ -377,16 +412,16 @@ RsFeedAddResult p3FeedReader::addFolder(uint32_t parentId, const std::string &na
feedId = fi->feedId;
}
- IndicateConfigChanged();
+ IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_NOW);
if (mNotify) {
mNotify->notifyFeedChanged(feedId, NOTIFY_TYPE_ADD);
}
- return RS_FEED_ADD_RESULT_SUCCESS;
+ return RS_FEED_RESULT_SUCCESS;
}
-RsFeedAddResult p3FeedReader::setFolder(uint32_t feedId, const std::string &name)
+RsFeedResult p3FeedReader::setFolder(uint32_t feedId, const std::string &name)
{
{
RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/
@@ -400,33 +435,33 @@ RsFeedAddResult p3FeedReader::setFolder(uint32_t feedId, const std::string &name
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReader::setFolder - feed id " << feedId << " not found" << std::endl;
#endif
- return RS_FEED_ADD_RESULT_FEED_NOT_FOUND;
+ return RS_FEED_RESULT_FEED_NOT_FOUND;
}
if ((feedIt->second->flag & RS_FEED_FLAG_FOLDER) == 0) {
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReader::setFolder - feed " << feedIt->second->name << " is no folder" << std::endl;
#endif
- return RS_FEED_ADD_RESULT_FEED_IS_NO_FOLDER;
+ return RS_FEED_RESULT_FEED_IS_NO_FOLDER;
}
RsFeedReaderFeed *fi = feedIt->second;
if (fi->name == name) {
- return RS_FEED_ADD_RESULT_SUCCESS;
+ return RS_FEED_RESULT_SUCCESS;
}
fi->name = name;
}
- IndicateConfigChanged();
+ IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_NOW);
if (mNotify) {
mNotify->notifyFeedChanged(feedId, NOTIFY_TYPE_MOD);
}
- return RS_FEED_ADD_RESULT_SUCCESS;
+ return RS_FEED_RESULT_SUCCESS;
}
-RsFeedAddResult p3FeedReader::addFeed(const FeedInfo &feedInfo, uint32_t &feedId)
+RsFeedResult p3FeedReader::addFeed(const FeedInfo &feedInfo, uint32_t &feedId)
{
feedId = 0;
@@ -444,14 +479,14 @@ RsFeedAddResult p3FeedReader::addFeed(const FeedInfo &feedInfo, uint32_t &feedId
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReader::addFeed - parent id " << feedInfo.parentId << " not found" << std::endl;
#endif
- return RS_FEED_ADD_RESULT_PARENT_NOT_FOUND;
+ return RS_FEED_RESULT_PARENT_NOT_FOUND;
}
if ((parentIt->second->flag & RS_FEED_FLAG_FOLDER) == 0) {
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReader::addFeed - parent " << parentIt->second->name << " is no folder" << std::endl;
#endif
- return RS_FEED_ADD_RESULT_PARENT_IS_NO_FOLDER;
+ return RS_FEED_RESULT_PARENT_IS_NO_FOLDER;
}
}
@@ -464,20 +499,23 @@ RsFeedAddResult p3FeedReader::addFeed(const FeedInfo &feedInfo, uint32_t &feedId
feedId = fi->feedId;
}
- IndicateConfigChanged();
+ IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_NOW);
if (mNotify) {
mNotify->notifyFeedChanged(feedId, NOTIFY_TYPE_ADD);
}
- return RS_FEED_ADD_RESULT_SUCCESS;
+ return RS_FEED_RESULT_SUCCESS;
}
-RsFeedAddResult p3FeedReader::setFeed(uint32_t feedId, const FeedInfo &feedInfo)
+RsFeedResult p3FeedReader::setFeed(uint32_t feedId, const FeedInfo &feedInfo)
{
std::string forumId;
std::string forumName;
std::string forumDescription;
+ std::string postedId;
+ std::string postedName;
+ std::string postedDescription;
{
RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/
@@ -491,14 +529,14 @@ RsFeedAddResult p3FeedReader::setFeed(uint32_t feedId, const FeedInfo &feedInfo)
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReader::setFeed - feed id " << feedId << " not found" << std::endl;
#endif
- return RS_FEED_ADD_RESULT_FEED_NOT_FOUND;
+ return RS_FEED_RESULT_FEED_NOT_FOUND;
}
if (feedIt->second->flag & RS_FEED_FLAG_FOLDER) {
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReader::setFeed - feed " << feedIt->second->name << " is a folder" << std::endl;
#endif
- return RS_FEED_ADD_RESULT_FEED_IS_FOLDER;
+ return RS_FEED_RESULT_FEED_IS_FOLDER;
}
if (feedInfo.parentId) {
@@ -508,19 +546,20 @@ RsFeedAddResult p3FeedReader::setFeed(uint32_t feedId, const FeedInfo &feedInfo)
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReader::setFeed - parent id " << feedInfo.parentId << " not found" << std::endl;
#endif
- return RS_FEED_ADD_RESULT_PARENT_NOT_FOUND;
+ return RS_FEED_RESULT_PARENT_NOT_FOUND;
}
if ((parentIt->second->flag & RS_FEED_FLAG_FOLDER) == 0) {
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReader::setFeed - parent " << parentIt->second->name << " is no folder" << std::endl;
#endif
- return RS_FEED_ADD_RESULT_PARENT_IS_NO_FOLDER;
+ return RS_FEED_RESULT_PARENT_IS_NO_FOLDER;
}
}
RsFeedReaderFeed *fi = feedIt->second;
std::string oldForumId = fi->forumId;
+ std::string oldPostedId = fi->postedId;
std::string oldName = fi->name;
std::string oldDescription = fi->description;
@@ -534,9 +573,18 @@ RsFeedAddResult p3FeedReader::setFeed(uint32_t feedId, const FeedInfo &feedInfo)
forumDescription = fi->description;
forumName.insert(0, FEEDREADER_FORUM_PREFIX);
}
+
+ if ((fi->flag & RS_FEED_FLAG_POSTED) && (fi->flag & RS_FEED_FLAG_UPDATE_POSTED_INFO) && !fi->postedId.empty() &&
+ (fi->postedId != oldPostedId || fi->name != oldName || fi->description != oldDescription)) {
+ /* name or description changed, update posted */
+ postedId = fi->postedId;
+ postedName = fi->name;
+ postedDescription = fi->description;
+ postedName.insert(0, FEEDREADER_POSTED_PREFIX);
+ }
}
- IndicateConfigChanged();
+ IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_NOW);
if (mNotify) {
mNotify->notifyFeedChanged(feedId, NOTIFY_TYPE_MOD);
@@ -550,7 +598,71 @@ RsFeedAddResult p3FeedReader::setFeed(uint32_t feedId, const FeedInfo &feedInfo)
//TODO: error
}
- return RS_FEED_ADD_RESULT_SUCCESS;
+ if (!postedId.empty()) {
+ RsPostedGroup postedGroup;
+ if (getPostedGroup(RsGxsGroupId(postedId), postedGroup)) {
+ updatePostedGroup(postedGroup, postedName, postedDescription);
+ }
+ //TODO: error
+ }
+
+ return RS_FEED_RESULT_SUCCESS;
+}
+
+RsFeedResult p3FeedReader::setParent(uint32_t feedId, uint32_t parentId)
+{
+ bool changed = false;
+
+ {
+ RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/
+
+#ifdef FEEDREADER_DEBUG
+ std::cerr << "p3FeedReader::setParent - set parent " << parentId << std::endl;
+#endif
+
+ std::map::iterator feedIt = mFeeds.find(feedId);
+ if (feedIt == mFeeds.end()) {
+#ifdef FEEDREADER_DEBUG
+ std::cerr << "p3FeedReader::setParent - feed id " << feedId << " not found" << std::endl;
+#endif
+ return RS_FEED_RESULT_FEED_NOT_FOUND;
+ }
+
+ if (parentId) {
+ /* check parent id */
+ std::map::iterator parentIt = mFeeds.find(parentId);
+ if (parentIt == mFeeds.end()) {
+#ifdef FEEDREADER_DEBUG
+ std::cerr << "p3FeedReader::setParent - parent id " << parentId << " not found" << std::endl;
+#endif
+ return RS_FEED_RESULT_PARENT_NOT_FOUND;
+ }
+
+ if ((parentIt->second->flag & RS_FEED_FLAG_FOLDER) == 0) {
+#ifdef FEEDREADER_DEBUG
+ std::cerr << "p3FeedReader::setParent - parent " << parentIt->second->name << " is no folder" << std::endl;
+#endif
+ return RS_FEED_RESULT_PARENT_IS_NO_FOLDER;
+ }
+ }
+
+ RsFeedReaderFeed *fi = feedIt->second;
+
+ if (fi->parentId != parentId) {
+ fi->parentId = parentId;
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_NOW);
+
+ if (mNotify) {
+ mNotify->notifyFeedChanged(feedId, NOTIFY_TYPE_MOD);
+ }
+ }
+
+ return RS_FEED_RESULT_SUCCESS;
}
void p3FeedReader::deleteAllMsgs_locked(RsFeedReaderFeed *fi)
@@ -628,7 +740,7 @@ bool p3FeedReader::removeFeed(uint32_t feedId)
}
if (changed) {
- IndicateConfigChanged();
+ IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_NOW);
}
if (preview) {
@@ -682,6 +794,7 @@ bool p3FeedReader::addPreviewFeed(const FeedInfo &feedInfo, uint32_t &feedId)
fi->updateInterval = 0;
fi->lastUpdate = 0;
fi->forumId.clear();
+ fi->postedId.clear();
fi->storageTime = 0;
mFeeds[fi->feedId] = fi;
@@ -808,7 +921,7 @@ bool p3FeedReader::removeMsg(uint32_t feedId, const std::string &msgId)
}
if (changed) {
- IndicateConfigChanged();
+ IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_NOW);
}
if (mNotify) {
@@ -860,7 +973,7 @@ bool p3FeedReader::removeMsgs(uint32_t feedId, const std::list &msg
}
if (changed) {
- IndicateConfigChanged();
+ IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_NOW);
}
if (mNotify && !removedMsgs.empty()) {
@@ -1158,7 +1271,7 @@ bool p3FeedReader::setMessageRead(uint32_t feedId, const std::string &msgId, boo
}
if (changed) {
- IndicateConfigChanged();
+ IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_OFTEN);
if (mNotify) {
mNotify->notifyFeedChanged(feedId, NOTIFY_TYPE_MOD);
mNotify->notifyMsgChanged(feedId, msgId, NOTIFY_TYPE_MOD);
@@ -1212,7 +1325,7 @@ bool p3FeedReader::retransformMsg(uint32_t feedId, const std::string &msgId)
}
if (feedChanged || msgChanged) {
- IndicateConfigChanged();
+ IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_NOW);
if (mNotify) {
if (feedChanged) {
mNotify->notifyFeedChanged(feedId, NOTIFY_TYPE_MOD);
@@ -1266,7 +1379,7 @@ bool p3FeedReader::clearMessageCache(uint32_t feedId)
}
}
- IndicateConfigChanged();
+ IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_NOW);
return true;
}
@@ -1343,10 +1456,21 @@ int p3FeedReader::tick()
}
}
+ // check images
+ bool imageToOptimze = false;
+ {
+ RsStackMutex stack(mImageMutex); /******* LOCK STACK MUTEX *********/
+
+ imageToOptimze = !mImages.empty();
+ }
+
if (mNotify) {
for (it = notifyIds.begin(); it != notifyIds.end(); ++it) {
mNotify->notifyFeedChanged(*it, NOTIFY_TYPE_MOD);
}
+ if (imageToOptimze) {
+ mNotify->notifyOptimizeImage();
+ }
}
return 0;
@@ -1397,7 +1521,7 @@ void p3FeedReader::cleanFeeds()
mLastClean = currentTime;
if (removedMsgIds.size()) {
- IndicateConfigChanged();
+ IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_NOW);
if (mNotify) {
std::list >::iterator it;
@@ -1709,7 +1833,7 @@ void p3FeedReader::onDownloadSuccess(uint32_t feedId, const std::string &content
fi->icon = icon;
if (!preview) {
- IndicateConfigChanged();
+ IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_NOW);
}
}
@@ -1760,7 +1884,7 @@ void p3FeedReader::onDownloadError(uint32_t feedId, RsFeedReaderErrorState resul
#endif
if (!fi->preview) {
- IndicateConfigChanged();
+ IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_NOW);
}
}
@@ -1872,21 +1996,26 @@ void p3FeedReader::onProcessSuccess_filterMsg(uint32_t feedId, std::listerrorString.clear();
if (!fi->preview) {
- IndicateConfigChanged();
+ IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_NOW);
}
}
}
-void p3FeedReader::onProcessSuccess_addMsgs(uint32_t feedId, std::list &msgs, bool single)
+void p3FeedReader::onProcessSuccess_addMsgs(uint32_t feedId, std::list &msgs)
{
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReader::onProcessSuccess_addMsgs - feed " << feedId << " got " << msgs.size() << " messages" << std::endl;
#endif
+ RsFeedReaderErrorState errorState = RS_FEED_ERRORSTATE_OK;
std::list addedMsgs;
std::string forumId;
- RsGxsId authorId;
+ RsGxsId forumAuthorId;
std::list forumMsgs;
+ std::string postedId;
+ RsGxsId postedAuthorId;
+ std::list postedMsgs;
+ uint32_t feedFlag = 0;
{
RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/
@@ -1903,7 +2032,8 @@ void p3FeedReader::onProcessSuccess_addMsgs(uint32_t feedId, std::listsecond;
bool forum = (fi->flag & RS_FEED_FLAG_FORUM) && !fi->preview;
- RsFeedReaderErrorState errorState = RS_FEED_ERRORSTATE_OK;
+ bool posted = (fi->flag & RS_FEED_FLAG_POSTED) && !fi->preview;
+ feedFlag = fi->flag;
if (forum && !msgs.empty()) {
if (mForums) {
@@ -1913,9 +2043,9 @@ void p3FeedReader::onProcessSuccess_addMsgs(uint32_t feedId, std::listforumId), forumGroup)) {
if (IS_GROUP_PUBLISHER(forumGroup.mMeta.mSubscribeFlags) && IS_GROUP_ADMIN(forumGroup.mMeta.mSubscribeFlags)) {
forumId = fi->forumId;
- authorId = forumGroup.mMeta.mAuthorId;
+ forumAuthorId = forumGroup.mMeta.mAuthorId;
- if (authorId.isNull()) {
+ if (forumAuthorId.isNull()) {
errorState = RS_FEED_ERRORSTATE_PROCESS_FORUM_NO_AUTHOR;
}
} else {
@@ -1933,6 +2063,34 @@ void p3FeedReader::onProcessSuccess_addMsgs(uint32_t feedId, std::listpostedId.empty()) {
+ /* check posted */
+ RsPostedGroup postedGroup;
+ if (getPostedGroup(RsGxsGroupId(fi->postedId), postedGroup)) {
+ if (IS_GROUP_PUBLISHER(postedGroup.mMeta.mSubscribeFlags) && IS_GROUP_ADMIN(postedGroup.mMeta.mSubscribeFlags)) {
+ postedId = fi->postedId;
+ postedAuthorId = postedGroup.mMeta.mAuthorId;
+
+ if (postedAuthorId.isNull()) {
+ errorState = RS_FEED_ERRORSTATE_PROCESS_POSTED_NO_AUTHOR;
+ }
+ } else {
+ errorState = RS_FEED_ERRORSTATE_PROCESS_POSTED_NO_ADMIN;
+ }
+ } else {
+ errorState = RS_FEED_ERRORSTATE_PROCESS_POSTED_NOT_FOUND;
+ }
+ } else {
+ std::cerr << "p3FeedReader::onProcessSuccess_addMsgs - posted id is empty (" << fi->name << ")" << std::endl;
+ errorState = RS_FEED_ERRORSTATE_PROCESS_POSTED_NOT_FOUND;
+ }
+ } else {
+ std::cerr << "p3FeedReader::onProcessSuccess_addMsgs - can't process posted, member mPosted is not set" << std::endl;
+ }
+ }
+
/* process msgs */
if (errorState == RS_FEED_ERRORSTATE_OK) {
/* process msgs */
@@ -1949,11 +2107,19 @@ void p3FeedReader::onProcessSuccess_addMsgs(uint32_t feedId, std::listmsgId, "%lu", mNextMsgId++);
}
- if (forum) {
+ if (forum || posted) {
miNew->flag = RS_FEEDMSG_FLAG_DELETED;
- forumMsgs.push_back(*miNew);
+ if (forum) {
+ forumMsgs.push_back(*miNew);
+ }
+ if (posted) {
+ postedMsgs.push_back(*miNew);
+ }
miNew->description.clear();
miNew->descriptionTransformed.clear();
+ miNew->attachmentLink.clear();
+ miNew->attachment.clear();
+ miNew->attachmentMimeType.clear();
} else {
miNew->flag = RS_FEEDMSG_FLAG_NEW;
addedMsgs.push_back(miNew->msgId);
@@ -1969,60 +2135,190 @@ void p3FeedReader::onProcessSuccess_addMsgs(uint32_t feedId, std::listfeedId << " (" << fi->name << ") added " << newMsgs << "/" << msgs.size() << " messages" << std::endl;
#endif
}
+ }
- if (!single) {
- fi->workstate = RsFeedReaderFeed::WAITING;
- fi->content.clear();
- fi->errorState = errorState;
- fi->lastUpdate = time(NULL);
+ bool postError = false;
+
+ if (errorState == RS_FEED_ERRORSTATE_OK) {
+ if (!forumId.empty() && !forumMsgs.empty()) {
+ if (mForums) {
+ /* a bit tricky */
+ RsGenExchange *genExchange = dynamic_cast(mForums);
+ if (genExchange) {
+ /* add messages as forum messages */
+ std::list::iterator msgIt;
+ for (msgIt = forumMsgs.begin(); msgIt != forumMsgs.end(); ++msgIt) {
+ RsFeedReaderMsg &mi = *msgIt;
+
+ /* convert to forum messages */
+ RsGxsForumMsg forumMsg;
+ forumMsg.mMeta.mGroupId = RsGxsGroupId(forumId);
+ forumMsg.mMeta.mMsgName = mi.title;
+ forumMsg.mMeta.mAuthorId = forumAuthorId;
+
+ std::string description = mi.descriptionTransformed.empty() ? mi.description : mi.descriptionTransformed;
+ /* add link */
+ if (!mi.link.empty()) {
+ description += " " + mi.link + "";
+ }
+
+ if (!mi.attachmentBinary.empty()) {
+ if (p3FeedReaderThread::isContentType(mi.attachmentMimeType, "image/")) {
+ /* add attachement to description */
+
+ // optimize image
+ std::vector optimizedImage;
+ std::string optimizedMimeType;
+ if (optimizeImage(FeedReaderOptimizeImageTask::SIZE, mi.attachmentBinary, mi.attachmentBinaryMimeType, optimizedImage, optimizedMimeType)) {
+ std::string base64;
+ if (p3FeedReaderThread::toBase64(optimizedImage, base64)) {
+ std::string imageBase64;
+ rs_sprintf(imageBase64, "data:%s;base64,%s", optimizedMimeType.c_str(), base64.c_str());
+ description += " ";
+ }
+ }
+ }
+ }
+
+ forumMsg.mMsg = description;
+
+ uint32_t token;
+ if (mForums->createMsg(token, forumMsg) && waitForToken(mForums, token)) {
+ RsGxsGrpMsgIdPair msgPair;
+ if (mForums->acknowledgeMsg(token, msgPair)) {
+ /* set to new */
+ genExchange->setMsgStatusFlags(token, msgPair, GXS_SERV::GXS_MSG_STATUS_GUI_NEW | GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD, GXS_SERV::GXS_MSG_STATUS_GUI_NEW | GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD);
+ }
+ } else {
+#ifdef FEEDREADER_DEBUG
+ std::cerr << "p3FeedReader::onProcessSuccess_addMsgs - can't add forum message " << mi.title << " for feed " << forumId << std::endl;
+#endif
+ postError = true;
+ }
+ }
+ } else {
+ std::cerr << "p3FeedReader::onProcessSuccess_addMsgs - can't process forum, member mForums is not derived from RsGenExchange" << std::endl;
+ }
+ } else {
+ std::cerr << "p3FeedReader::onProcessSuccess_addMsgs - can't process forum, member mForums is not set" << std::endl;
+ }
}
- if (!fi->preview) {
- IndicateConfigChanged();
+ if (!postedId.empty() && !postedMsgs.empty()) {
+ if (mPosted) {
+ /* a bit tricky */
+ RsGenExchange *genExchange = dynamic_cast(mPosted);
+ if (genExchange) {
+ /* add messages as posted messages */
+ std::list::iterator msgIt;
+ for (msgIt = postedMsgs.begin(); msgIt != postedMsgs.end(); ++msgIt) {
+ RsFeedReaderMsg &mi = *msgIt;
+
+ /* convert to posted messages */
+ RsPostedPost postedPost;
+ postedPost.mMeta.mGroupId = RsGxsGroupId(postedId);
+ postedPost.mMeta.mMsgName = mi.title;
+ postedPost.mMeta.mAuthorId = postedAuthorId;
+ postedPost.mLink = mi.link;
+
+ std::string description;
+ if (feedFlag & RS_FEED_FLAG_POSTED_FIRST_IMAGE) {
+ if (!mi.postedFirstImage.empty()) {
+ /* use first image as image for posted and description without image as notes */
+ if (feedFlag & RS_FEED_FLAG_POSTED_SHRINK_IMAGE) {
+ // optimize image
+ std::vector optimizedImage;
+ std::string optimizedMimeType;
+ if (optimizeImage(FeedReaderOptimizeImageTask::POSTED, mi.postedFirstImage, mi.postedFirstImageMimeType, optimizedImage, optimizedMimeType)) {
+ postedPost.mImage.copy(optimizedImage.data(), optimizedImage.size());
+ }
+ } else {
+ postedPost.mImage.copy(mi.postedFirstImage.data(), mi.postedFirstImage.size());
+ }
+ if (feedFlag & RS_FEED_FLAG_POSTED_ONLY_IMAGE) {
+ /* ignore description */
+ } else {
+ description = mi.postedDescriptionWithoutFirstImage;
+ }
+ } else {
+ if (feedFlag & RS_FEED_FLAG_POSTED_ONLY_IMAGE) {
+ /* ignore messages without image */
+ continue;
+ }
+ description = mi.descriptionTransformed.empty() ? mi.description : mi.descriptionTransformed;
+ }
+ } else {
+ description = mi.descriptionTransformed.empty() ? mi.description : mi.descriptionTransformed;
+
+ if (!mi.attachmentBinary.empty()) {
+ if (p3FeedReaderThread::isContentType(mi.attachmentMimeType, "image/")) {
+ /* use attachement as image */
+
+ if (feedFlag & RS_FEED_FLAG_POSTED_SHRINK_IMAGE) {
+ // optimize image
+ std::vector optimizedImage;
+ std::string optimizedMimeType;
+ if (optimizeImage(FeedReaderOptimizeImageTask::POSTED, mi.attachmentBinary, mi.attachmentBinaryMimeType, optimizedImage, optimizedMimeType)) {
+ postedPost.mImage.copy(optimizedImage.data(), optimizedImage.size());
+ }
+ } else {
+ postedPost.mImage.copy(mi.attachmentBinary.data(), mi.attachmentBinary.size());
+ }
+ }
+ }
+ }
+
+ postedPost.mNotes = description;
+
+ uint32_t token;
+ if (mPosted->createPost(token, postedPost) && waitForToken(mPosted, token)) {
+ RsGxsGrpMsgIdPair msgPair;
+ if (mPosted->acknowledgeMsg(token, msgPair)) {
+ /* set to new */
+ genExchange->setMsgStatusFlags(token, msgPair, GXS_SERV::GXS_MSG_STATUS_GUI_NEW | GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD, GXS_SERV::GXS_MSG_STATUS_GUI_NEW | GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD);
+ }
+ } else {
+#ifdef FEEDREADER_DEBUG
+ std::cerr << "p3FeedReader::onProcessSuccess_addMsgs - can't add posted message " << mi.title << " for feed " << postedId << std::endl;
+#endif
+ postError = true;
+ }
+ }
+ } else {
+ std::cerr << "p3FeedReader::onProcessSuccess_addMsgs - can't process posted, member mPosted is not derived from RsGenExchange" << std::endl;
+ }
+ } else {
+ std::cerr << "p3FeedReader::onProcessSuccess_addMsgs - can't process posted, member mPosted is not set" << std::endl;
+ }
}
}
- if (!forumId.empty() && !forumMsgs.empty()) {
- if (mForums) {
- /* a bit tricky */
- RsGenExchange *genExchange = dynamic_cast(mForums);
- if (genExchange) {
- /* add messages as forum messages */
- std::list::iterator msgIt;
- for (msgIt = forumMsgs.begin(); msgIt != forumMsgs.end(); ++msgIt) {
- RsFeedReaderMsg &mi = *msgIt;
+ {
+ RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/
- /* convert to forum messages */
- RsGxsForumMsg forumMsg;
- forumMsg.mMeta.mGroupId = RsGxsGroupId(forumId);
- forumMsg.mMeta.mMsgName = mi.title;
- forumMsg.mMeta.mAuthorId = authorId;
-
- std::string description = mi.descriptionTransformed.empty() ? mi.description : mi.descriptionTransformed;
- /* add link */
- if (!mi.link.empty()) {
- description += " " + mi.link + "";
- }
- forumMsg.mMsg = description;
-
- uint32_t token;
- if (mForums->createMsg(token, forumMsg) && waitForToken(token)) {
- RsGxsGrpMsgIdPair msgPair;
- if (mForums->acknowledgeMsg(token, msgPair)) {
- /* set to new */
- genExchange->setMsgStatusFlags(token, msgPair, GXS_SERV::GXS_MSG_STATUS_GUI_NEW | GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD, GXS_SERV::GXS_MSG_STATUS_GUI_NEW | GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD);
- }
- } else {
+ /* find feed */
+ std::map::iterator it = mFeeds.find(feedId);
+ if (it == mFeeds.end()) {
+ /* feed not found */
#ifdef FEEDREADER_DEBUG
- std::cerr << "p3FeedReader::onProcessSuccess_addMsgs - can't add forum message " << mi.title << " for feed " << forumId << std::endl;
+ std::cerr << "p3FeedReader::onProcessSuccess_addMsgs - feed " << feedId << " not found" << std::endl;
#endif
- }
- }
- } else {
- std::cerr << "p3FeedReader::onProcessSuccess_addMsgs - can't process forum, member mForums is not derived from RsGenExchange" << std::endl;
+ return;
+ }
+
+ RsFeedReaderFeed *fi = it->second;
+
+ if (!fi->preview) {
+ fi->workstate = RsFeedReaderFeed::WAITING;
+ fi->content.clear();
+ if (errorState == RS_FEED_ERRORSTATE_OK && postError) {
+ // post error occured
+ errorState = RS_FEED_ERRORSTATE_PROCESS_POST;
}
- } else {
- std::cerr << "p3FeedReader::onProcessSuccess_addMsgs - can't process forum, member mForums is not set" << std::endl;
+ fi->errorState = errorState;
+ fi->lastUpdate = time(NULL);
+
+ IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_NOW);
}
}
@@ -2064,7 +2360,7 @@ void p3FeedReader::onProcessError(uint32_t feedId, RsFeedReaderErrorState result
#endif
if (!fi->preview) {
- IndicateConfigChanged();
+ IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_NOW);
}
}
@@ -2080,6 +2376,9 @@ void p3FeedReader::setFeedInfo(uint32_t feedId, const std::string &name, const s
std::string forumId;
std::string forumName;
std::string forumDescription;
+ std::string postedId;
+ std::string postedName;
+ std::string postedDescription;
{
RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/
@@ -2118,11 +2417,19 @@ void p3FeedReader::setFeedInfo(uint32_t feedId, const std::string &name, const s
forumDescription = fi->description;
forumName.insert(0, FEEDREADER_FORUM_PREFIX);
}
+
+ if ((fi->flag & RS_FEED_FLAG_POSTED) && (fi->flag & RS_FEED_FLAG_UPDATE_POSTED_INFO) && !fi->postedId.empty() && !preview) {
+ /* change posted too */
+ postedId = fi->postedId;
+ postedName = fi->name;
+ postedDescription = fi->description;
+ postedName.insert(0, FEEDREADER_POSTED_PREFIX);
+ }
}
if (changed) {
if (!preview) {
- IndicateConfigChanged();
+ IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_NOW);
}
if (mNotify) {
@@ -2137,6 +2444,14 @@ void p3FeedReader::setFeedInfo(uint32_t feedId, const std::string &name, const s
}
//TODO: error
}
+
+ if (!postedId.empty()) {
+ RsPostedGroup postedGroup;
+ if (getPostedGroup(RsGxsGroupId(postedId), postedGroup)) {
+ updatePostedGroup(postedGroup, postedName, postedDescription);
+ }
+ //TODO: error
+ }
}
bool p3FeedReader::getForumGroup(const RsGxsGroupId &groupId, RsGxsForumGroup &forumGroup)
@@ -2159,7 +2474,7 @@ bool p3FeedReader::getForumGroup(const RsGxsGroupId &groupId, RsGxsForumGroup &f
uint32_t token;
mForums->getTokenService()->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_SUMMARY, opts, grpIds);
- if (!waitForToken(token)) {
+ if (!waitForToken(mForums, token)) {
std::cerr << "p3FeedReader::getForumGroup - waitForToken for request failed" << std::endl;
return false;
}
@@ -2202,7 +2517,7 @@ bool p3FeedReader::updateForumGroup(const RsGxsForumGroup &forumGroup, const std
return false;
}
- if (!waitForToken(token)) {
+ if (!waitForToken(mForums, token)) {
std::cerr << "p3FeedReader::updateForumGroup - waitForToken for update failed" << std::endl;
return false;
}
@@ -2211,13 +2526,85 @@ bool p3FeedReader::updateForumGroup(const RsGxsForumGroup &forumGroup, const std
return true;
}
-bool p3FeedReader::waitForToken(uint32_t token)
+bool p3FeedReader::getPostedGroup(const RsGxsGroupId &groupId, RsPostedGroup &postedGroup)
{
- if (!mForums) {
+ if (!mPosted) {
+ std::cerr << "p3FeedReader::getPostedGroup - can't get posted group " << groupId.toStdString() << ", member mPosted is not set" << std::endl;
return false;
}
- RsTokenService *service = mForums->getTokenService();
+ if (groupId.isNull()) {
+ std::cerr << "p3FeedReader::getPostedGroup - group id is not valid" << std::endl;
+ return false;
+ }
+
+ std::list grpIds;
+ grpIds.push_back(groupId);
+
+ RsTokReqOptions opts;
+ opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
+ uint32_t token;
+ mPosted->getTokenService()->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_SUMMARY, opts, grpIds);
+
+ if (!waitForToken(mPosted, token)) {
+ std::cerr << "p3FeedReader::getPostedGroup - waitForToken for request failed" << std::endl;
+ return false;
+ }
+
+ std::vector groups;
+ if (!mPosted->getGroupData(token, groups)) {
+ std::cerr << "p3FeedReader::getPostedGroup - Error getting data" << std::endl;
+ return false;
+ }
+
+ if (groups.size() != 1) {
+ std::cerr << "p3FeedReader::getPostedGroup - Wrong number of items" << std::endl;
+ return false;
+ }
+
+ postedGroup = groups[0];
+
+ return true;
+}
+
+bool p3FeedReader::updatePostedGroup(const RsPostedGroup &postedGroup, const std::string &groupName, const std::string &groupDescription)
+{
+ if (!mPosted) {
+ std::cerr << "p3FeedReader::updatePostedGroup - can't change posted " << postedGroup.mMeta.mGroupId.toStdString() << ", member mPosted is not set" << std::endl;
+ return false;
+ }
+
+ if (postedGroup.mMeta.mGroupName == groupName && postedGroup.mDescription == groupDescription) {
+ /* No change */
+ return true;
+ }
+
+ RsPostedGroup newPostedGroup = postedGroup;
+ newPostedGroup.mMeta.mGroupName = groupName;
+ newPostedGroup.mDescription = groupDescription;
+
+ uint32_t token;
+ if (!mPosted->updateGroup(token, newPostedGroup)) {
+ std::cerr << "p3FeedReader::updatePostedGroup - can't change posted " << newPostedGroup.mMeta.mGroupId.toStdString() << std::endl;
+ return false;
+ }
+
+ if (!waitForToken(mPosted, token)) {
+ std::cerr << "p3FeedReader::updatePostedGroup - waitForToken for update failed" << std::endl;
+ return false;
+ }
+
+ /* Posted updated */
+ return true;
+}
+
+bool p3FeedReader::waitForToken(RsGxsIfaceHelper *interface, uint32_t token)
+{
+ if (!interface) {
+ return false;
+ }
+
+ RsTokenService *service = interface->getTokenService();
int count = MAX_REQUEST_AGE * 2;
while (!mStopped) {
@@ -2239,3 +2626,169 @@ bool p3FeedReader::waitForToken(uint32_t token)
return false;
}
+
+bool p3FeedReader::getForumGroups(std::vector &groups, bool onlyOwn)
+{
+ if (!mForums) {
+ std::cerr << "p3FeedReader::getForumGroups - can't get groups, member mForums is not set" << std::endl;
+ return false;
+ }
+
+ RsTokReqOptions opts;
+ opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
+
+ uint32_t token;
+ if (!mForums->requestGroupInfo(token, opts)) {
+ std::cerr << "p3FeedReader::getForumGroups - can't get group list" << std::endl;
+ return false;
+ }
+
+ if (!waitForToken(mForums, token)) {
+ std::cerr << "p3FeedReader::getForumGroups - waitForToken for list failed" << std::endl;
+ return false;
+ }
+
+ if (!mForums->getGroupData(token, groups)) {
+ std::cerr << "p3FeedReader::getForumGroups - getGroupData failed" << std::endl;
+ return false;
+ }
+
+ if (onlyOwn) {
+ // filter groups
+ for (std::vector::iterator it = groups.begin(); it != groups.end(); ) {
+ const RsGxsForumGroup &group = *it;
+ if (IS_GROUP_PUBLISHER(group.mMeta.mSubscribeFlags) && IS_GROUP_ADMIN(group.mMeta.mSubscribeFlags)) {
+ ++it;
+ } else {
+ it = groups.erase(it);
+ }
+ }
+ }
+
+ return true;
+}
+
+bool p3FeedReader::getPostedGroups(std::vector &groups, bool onlyOwn)
+{
+ if (!mPosted) {
+ std::cerr << "p3FeedReader::getPostedGroups - can't get groups, member mPosted is not set" << std::endl;
+ return false;
+ }
+
+ RsTokReqOptions opts;
+ opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
+
+ uint32_t token;
+ if (!mPosted->requestGroupInfo(token, opts)) {
+ std::cerr << "p3FeedReader::getPostedGroups - can't get group list" << std::endl;
+ return false;
+ }
+
+ if (!waitForToken(mPosted, token)) {
+ std::cerr << "p3FeedReader::getPostedGroups - waitForToken for list failed" << std::endl;
+ return false;
+ }
+
+ if (!mPosted->getGroupData(token, groups)) {
+ std::cerr << "p3FeedReader::getForumGroups - getGroupData failed" << std::endl;
+ return false;
+ }
+
+ if (onlyOwn) {
+ // filter groups
+ for (std::vector::iterator it = groups.begin(); it != groups.end(); ) {
+ const RsPostedGroup &group = *it;
+ if (IS_GROUP_PUBLISHER(group.mMeta.mSubscribeFlags) && IS_GROUP_ADMIN(group.mMeta.mSubscribeFlags)) {
+ ++it;
+ } else {
+ it = groups.erase(it);
+ }
+ }
+ }
+
+ return true;
+}
+
+bool p3FeedReader::optimizeImage(FeedReaderOptimizeImageTask::Type type, const std::vector &image, const std::string &mimeType, std::vector &resultImage, std::string &resultMimeType)
+{
+ if (!mNotify) {
+ return false;
+ }
+
+ FeedReaderOptimizeImageTask *optimizeImageTask = new FeedReaderOptimizeImageTask(type, image, mimeType);
+ {
+ RsStackMutex stack(mImageMutex); /******* LOCK STACK MUTEX *********/
+
+ mImages.push_back(optimizeImageTask);
+ }
+
+ /* Wait until task is complete */
+ int nSeconds = 0;
+ while (true) {
+ if (mStopped) {
+ return false;
+ }
+
+ rstime::rs_usleep(1000 * 1000); // 1 second
+
+ if (++nSeconds >= 30) {
+ // timeout
+ std::list::iterator it = std::find(mImages.begin(), mImages.end(), optimizeImageTask);
+ if (it != mImages.end()) {
+ mImages.erase(it);
+
+ delete(optimizeImageTask);
+
+ return false;
+ }
+
+ // not found in mImages?
+ }
+
+ {
+ RsStackMutex stack(mImageMutex); /******* LOCK STACK MUTEX *********/
+
+ std::list::iterator it = std::find(mResultImages.begin(), mResultImages.end(), optimizeImageTask);
+ if (it != mResultImages.end()) {
+ mResultImages.erase(it);
+
+ bool result = optimizeImageTask->mResult;
+ if (result) {
+ resultImage = optimizeImageTask->mImageResult;
+ resultMimeType = optimizeImageTask->mMimeTypeResult;
+ }
+
+ delete(optimizeImageTask);
+
+ return result;
+ }
+ }
+ }
+
+ return false;
+}
+
+FeedReaderOptimizeImageTask *p3FeedReader::getOptimizeImageTask()
+{
+ RsStackMutex stack(mImageMutex); /******* LOCK STACK MUTEX *********/
+
+ if (mImages.empty()) {
+ return NULL;
+ }
+
+ FeedReaderOptimizeImageTask *imageResize = mImages.front();
+ mImages.pop_front();
+
+ return imageResize;
+}
+
+void p3FeedReader::setOptimizeImageTaskResult(FeedReaderOptimizeImageTask *optimizeImageTask)
+{
+ if (!optimizeImageTask) {
+ return;
+ }
+
+ RsStackMutex stack(mImageMutex); /******* LOCK STACK MUTEX *********/
+
+ mResultImages.push_back(optimizeImageTask);
+}
diff --git a/plugins/FeedReader/services/p3FeedReader.h b/plugins/FeedReader/services/p3FeedReader.h
index 08fa3619b..0df9bf79e 100644
--- a/plugins/FeedReader/services/p3FeedReader.h
+++ b/plugins/FeedReader/services/p3FeedReader.h
@@ -33,11 +33,14 @@ class p3FeedReaderThread;
class RsGxsForums;
struct RsGxsForumGroup;
+class RsPosted;
+struct RsPostedGroup;
+class RsGxsIfaceHelper;
class p3FeedReader : public RsPQIService, public RsFeedReader
{
public:
- p3FeedReader(RsPluginHandler *pgHandler, RsGxsForums *forums);
+ p3FeedReader(RsPluginHandler *pgHandler, RsGxsForums *forums, RsPosted *posted);
/****************** FeedReader Interface *************/
virtual void stop();
@@ -52,24 +55,33 @@ public:
virtual bool getSaveInBackground();
virtual void setSaveInBackground(bool saveInBackground);
- virtual RsFeedAddResult addFolder(uint32_t parentId, const std::string &name, uint32_t &feedId);
- virtual RsFeedAddResult setFolder(uint32_t feedId, const std::string &name);
- virtual RsFeedAddResult addFeed(const FeedInfo &feedInfo, uint32_t &feedId);
- virtual RsFeedAddResult setFeed(uint32_t feedId, const FeedInfo &feedInfo);
- virtual bool removeFeed(uint32_t feedId);
- virtual bool addPreviewFeed(const FeedInfo &feedInfo, uint32_t &feedId);
- virtual void getFeedList(uint32_t parentId, std::list &feedInfos);
- virtual bool getFeedInfo(uint32_t feedId, FeedInfo &feedInfo);
- virtual bool getMsgInfo(uint32_t feedId, const std::string &msgId, FeedMsgInfo &msgInfo);
- virtual bool removeMsg(uint32_t feedId, const std::string &msgId);
- virtual bool removeMsgs(uint32_t feedId, const std::list &msgIds);
- virtual bool getMessageCount(uint32_t feedId, uint32_t *msgCount, uint32_t *newCount, uint32_t *unreadCount);
- virtual bool getFeedMsgList(uint32_t feedId, std::list &msgInfos);
- virtual bool getFeedMsgIdList(uint32_t feedId, std::list &msgIds);
- virtual bool processFeed(uint32_t feedId);
- virtual bool setMessageRead(uint32_t feedId, const std::string &msgId, bool read);
- virtual bool retransformMsg(uint32_t feedId, const std::string &msgId);
- virtual bool clearMessageCache(uint32_t feedId);
+ virtual RsFeedResult addFolder(uint32_t parentId, const std::string &name, uint32_t &feedId);
+ virtual RsFeedResult setFolder(uint32_t feedId, const std::string &name);
+ virtual RsFeedResult addFeed(const FeedInfo &feedInfo, uint32_t &feedId);
+ virtual RsFeedResult setFeed(uint32_t feedId, const FeedInfo &feedInfo);
+ virtual RsFeedResult setParent(uint32_t feedId, uint32_t parentId);
+ virtual bool removeFeed(uint32_t feedId);
+ virtual bool addPreviewFeed(const FeedInfo &feedInfo, uint32_t &feedId);
+ virtual void getFeedList(uint32_t parentId, std::list &feedInfos);
+ virtual bool getFeedInfo(uint32_t feedId, FeedInfo &feedInfo);
+ virtual bool getMsgInfo(uint32_t feedId, const std::string &msgId, FeedMsgInfo &msgInfo);
+ virtual bool removeMsg(uint32_t feedId, const std::string &msgId);
+ virtual bool removeMsgs(uint32_t feedId, const std::list &msgIds);
+ virtual bool getMessageCount(uint32_t feedId, uint32_t *msgCount, uint32_t *newCount, uint32_t *unreadCount);
+ virtual bool getFeedMsgList(uint32_t feedId, std::list &msgInfos);
+ virtual bool getFeedMsgIdList(uint32_t feedId, std::list &msgIds);
+ virtual bool processFeed(uint32_t feedId);
+ virtual bool setMessageRead(uint32_t feedId, const std::string &msgId, bool read);
+ virtual bool retransformMsg(uint32_t feedId, const std::string &msgId);
+ virtual bool clearMessageCache(uint32_t feedId);
+
+ virtual RsGxsForums* forums() { return mForums; }
+ virtual RsPosted* posted() { return mPosted; }
+ virtual bool getForumGroups(std::vector &groups, bool onlyOwn);
+ virtual bool getPostedGroups(std::vector &groups, bool onlyOwn);
+
+ virtual FeedReaderOptimizeImageTask *getOptimizeImageTask();
+ virtual void setOptimizeImageTaskResult(FeedReaderOptimizeImageTask *optimizeImageTask);
virtual RsFeedReaderErrorState processXPath(const std::list &xpathsToUse, const std::list &xpathsToRemove, std::string &description, std::string &errorString);
virtual RsFeedReaderErrorState processXslt(const std::string &xslt, std::string &description, std::string &errorString);
@@ -83,7 +95,7 @@ public:
void onDownloadSuccess(uint32_t feedId, const std::string &content, std::string &icon);
void onDownloadError(uint32_t feedId, RsFeedReaderErrorState result, const std::string &errorString);
void onProcessSuccess_filterMsg(uint32_t feedId, std::list &msgs);
- void onProcessSuccess_addMsgs(uint32_t feedId, std::list &msgs, bool single);
+ void onProcessSuccess_addMsgs(uint32_t feedId, std::list &msgs);
void onProcessError(uint32_t feedId, RsFeedReaderErrorState result, const std::string &errorString);
bool getFeedToProcess(RsFeedReaderFeed &feed, uint32_t neededFeedId);
@@ -92,7 +104,10 @@ public:
bool getForumGroup(const RsGxsGroupId &groupId, RsGxsForumGroup &forumGroup);
bool updateForumGroup(const RsGxsForumGroup &forumGroup, const std::string &groupName, const std::string &groupDescription);
- bool waitForToken(uint32_t token);
+ bool getPostedGroup(const RsGxsGroupId &groupId, RsPostedGroup &postedGroup);
+ bool updatePostedGroup(const RsPostedGroup &postedGroup, const std::string &groupName, const std::string &groupDescription);
+ bool waitForToken(RsGxsIfaceHelper *interface, uint32_t token);
+ bool optimizeImage(FeedReaderOptimizeImageTask::Type type, const std::vector &image, const std::string &mimeType, std::vector &resultImage, std::string &resultMimeType);
protected:
/****************** p3Config STUFF *******************/
@@ -109,6 +124,7 @@ private:
private:
time_t mLastClean;
RsGxsForums *mForums;
+ RsPosted *mPosted;
RsFeedReaderNotify *mNotify;
volatile bool mStopped;
@@ -133,6 +149,10 @@ private:
RsMutex mProcessMutex;
std::list mProcessFeeds;
+ RsMutex mImageMutex;
+ std::list mImages;
+ std::list mResultImages;
+
RsMutex mPreviewMutex;
p3FeedReaderThread *mPreviewDownloadThread;
p3FeedReaderThread *mPreviewProcessThread;
diff --git a/plugins/FeedReader/services/p3FeedReaderThread.cc b/plugins/FeedReader/services/p3FeedReaderThread.cc
index 38f966663..110d051cf 100644
--- a/plugins/FeedReader/services/p3FeedReaderThread.cc
+++ b/plugins/FeedReader/services/p3FeedReaderThread.cc
@@ -89,7 +89,7 @@ void p3FeedReaderThread::threadTick()
/* first, filter the messages */
mFeedReader->onProcessSuccess_filterMsg(feed.feedId, msgs);
if (isRunning()) {
- /* second, process the descriptions */
+ /* second, process the descriptions and attachment */
for (it = msgs.begin(); it != msgs.end(); ) {
if (!isRunning()) {
break;
@@ -107,7 +107,7 @@ void p3FeedReaderThread::threadTick()
std::list msgSingle;
msgSingle.push_back(mi);
- mFeedReader->onProcessSuccess_addMsgs(feed.feedId, msgSingle, true);
+ mFeedReader->onProcessSuccess_addMsgs(feed.feedId, msgSingle);
/* delete not accepted message */
std::list::iterator it1;
@@ -122,12 +122,15 @@ void p3FeedReaderThread::threadTick()
++it;
}
}
- if (isRunning()) {
- if (result == RS_FEED_ERRORSTATE_OK) {
- /* third, add messages */
- mFeedReader->onProcessSuccess_addMsgs(feed.feedId, msgs, false);
- } else {
- mFeedReader->onProcessError(feed.feedId, result, errorString);
+
+ if (!feed.preview) {
+ if (isRunning()) {
+ if (result == RS_FEED_ERRORSTATE_OK) {
+ /* third, add messages */
+ mFeedReader->onProcessSuccess_addMsgs(feed.feedId, msgs);
+ } else {
+ mFeedReader->onProcessError(feed.feedId, result, errorString);
+ }
}
}
}
@@ -150,12 +153,12 @@ void p3FeedReaderThread::threadTick()
/****************************** Download ***********************************/
/***************************************************************************/
-static bool isContentType(const std::string &contentType, const char *type)
+bool p3FeedReaderThread::isContentType(const std::string &contentType, const char *type)
{
return (strncasecmp(contentType.c_str(), type, strlen(type)) == 0);
}
-static bool toBase64(const std::vector &data, std::string &base64)
+bool p3FeedReaderThread::toBase64(const std::vector &data, std::string &base64)
{
bool result = false;
@@ -184,6 +187,28 @@ static bool toBase64(const std::vector &data, std::string &base64
return result;
}
+bool p3FeedReaderThread::fromBase64(const std::string &base64, std::vector &data)
+{
+ bool result = false;
+
+ BIO *b64 = BIO_new(BIO_f_base64());
+ if (b64) {
+ BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
+ BIO *source = BIO_new_mem_buf(base64.c_str(), -1); // read-only source
+ if (source) {
+ BIO_push(b64, source);
+ const int maxlen = base64.length() / 4 * 3 + 1;
+ data.resize(maxlen);
+ const int len = BIO_read(b64, data.data(), maxlen);
+ data.resize(len);
+ result = true;
+ }
+ BIO_free_all(b64);
+ }
+
+ return result;
+}
+
static std::string getBaseLink(std::string link)
{
size_t found = link.rfind('/');
@@ -196,7 +221,7 @@ static std::string getBaseLink(std::string link)
static std::string calculateLink(const std::string &baseLink, const std::string &link)
{
- if (link.substr(0, 7) == "http://") {
+ if (link.substr(0, 7) == "http://" || link.substr(0, 8) == "https://") {
/* absolute link */
return link;
}
@@ -204,8 +229,14 @@ static std::string calculateLink(const std::string &baseLink, const std::string
/* calculate link of base link */
std::string resultLink = baseLink;
- /* link should begin with "http://" */
- if (resultLink.substr(0, 7) != "http://") {
+ int hostStart = 0;
+ /* link should begin with "http://" or "https://" */
+ if (resultLink.substr(0, 7) == "http://") {
+ hostStart = 7;
+ } else if (resultLink.substr(0, 8) == "https://") {
+ hostStart = 8;
+ } else {
+ hostStart = 7;
resultLink.insert(0, "http://");
}
@@ -216,7 +247,7 @@ static std::string calculateLink(const std::string &baseLink, const std::string
if (*link.begin() == '/') {
/* link begins with "/" */
- size_t found = resultLink.find('/', 7);
+ size_t found = resultLink.find('/', hostStart);
if (found != std::string::npos) {
resultLink.erase(found);
}
@@ -245,12 +276,12 @@ static bool getFavicon(CURLWrapper &CURL, const std::string &url, std::string &i
if (code == CURLE_OK) {
if (CURL.responseCode() == 200) {
std::string contentType = CURL.contentType();
- if (isContentType(contentType, "image/x-icon") ||
- isContentType(contentType, "application/octet-stream") ||
- isContentType(contentType, "text/plain")) {
+ if (p3FeedReaderThread::isContentType(contentType, "image/") ||
+ p3FeedReaderThread::isContentType(contentType, "application/octet-stream") ||
+ p3FeedReaderThread::isContentType(contentType, "text/plain")) {
if (!vicon.empty()) {
#warning p3FeedReaderThread.cc TODO thunder2: check it
- result = toBase64(vicon, icon);
+ result = p3FeedReaderThread::toBase64(vicon, icon);
}
}
}
@@ -296,6 +327,9 @@ RsFeedReaderErrorState p3FeedReaderThread::download(const RsFeedReaderFeed &feed
}
}
break;
+ case 403:
+ result = RS_FEED_ERRORSTATE_DOWNLOAD_BLOCKED;
+ break;
case 404:
result = RS_FEED_ERRORSTATE_DOWNLOAD_NOT_FOUND;
break;
@@ -814,6 +848,22 @@ RsFeedReaderErrorState p3FeedReaderThread::process(const RsFeedReaderFeed &feed,
RsFeedReaderErrorState result = RS_FEED_ERRORSTATE_OK;
+ time_t minimumPubDate = 0;
+ if (feed.lastUpdate == 0) {
+ // Get all items on first scan
+ } else {
+ // Get storage time
+ uint32_t storageTime = 0;
+ if (feed.flag & RS_FEED_FLAG_STANDARD_STORAGE_TIME) {
+ storageTime = mFeedReader->getStandardStorageTime();
+ } else {
+ storageTime = feed.storageTime;
+ }
+ if (storageTime > 0) {
+ minimumPubDate = time(NULL) - storageTime;
+ }
+ }
+
XMLWrapper xml;
if (xml.readXML(feed.content.c_str())) {
xmlNodePtr root = xml.getRootElement();
@@ -959,6 +1009,26 @@ RsFeedReaderErrorState p3FeedReaderThread::process(const RsFeedReaderFeed &feed,
item->pubDate = time(NULL);
}
+ if (feedFormat == FORMAT_RSS) {
+ /* */
+ xmlNodePtr enclosure = xml.findNode(node->children, "enclosure", false);
+ if (enclosure) {
+ std::string enclosureMimeType = xml.getAttr(enclosure, "type");
+ std::string enclosureUrl = xml.getAttr(enclosure, "url");
+ if (!enclosureUrl.empty()) {
+ item->attachmentLink = enclosureUrl;
+ item->attachmentMimeType = enclosureMimeType;
+ }
+ }
+ }
+
+ if (minimumPubDate) {
+ if (item->pubDate < minimumPubDate) {
+ // pubDate is less than storage time, don't add as new item
+ continue;
+ }
+ }
+
entries.push_back(item);
}
} else {
@@ -1013,10 +1083,44 @@ RsFeedReaderErrorState p3FeedReaderThread::processMsg(const RsFeedReaderFeed &fe
RsFeedReaderErrorState result = RS_FEED_ERRORSTATE_OK;
std::string proxy = getProxyForFeed(feed);
+ /* attachment */
+ if (!msg->attachmentLink.empty()) {
+ if (isContentType(msg->attachmentMimeType, "image/")) {
+ CURLWrapper CURL(proxy);
+ CURLcode code = CURL.downloadBinary(msg->attachmentLink, msg->attachmentBinary);
+ if (code == CURLE_OK && CURL.responseCode() == 200) {
+ std::string contentType = CURL.contentType();
+ if (isContentType(contentType, "image/")) {
+ msg->attachmentBinaryMimeType = contentType;
+
+ bool forum = (feed.flag & RS_FEED_FLAG_FORUM) && !feed.preview;
+ bool posted = (feed.flag & RS_FEED_FLAG_POSTED) && !feed.preview;
+
+ if (!forum && ! posted) {
+ /* no need to optimize image */
+ std::vector optimizedBinary;
+ std::string optimizedMimeType;
+ if (mFeedReader->optimizeImage(FeedReaderOptimizeImageTask::SIZE, msg->attachmentBinary, msg->attachmentBinaryMimeType, optimizedBinary, optimizedMimeType)) {
+ if (toBase64(optimizedBinary, msg->attachment)) {
+ msg->attachmentMimeType = optimizedMimeType;
+ } else {
+ msg->attachment.clear();
+ }
+ }
+ }
+ } else {
+ msg->attachmentBinary.clear();
+ }
+ } else {
+ msg->attachmentBinary.clear();
+ }
+ }
+ }
+
std::string url;
if (feed.flag & RS_FEED_FLAG_SAVE_COMPLETE_PAGE) {
#ifdef FEEDREADER_DEBUG
- std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") download page " << msg->link << std::endl;
+ std::cerr << "p3FeedReaderThread::processMsg - feed " << feed.feedId << " (" << feed.name << ") download page " << msg->link << std::endl;
#endif
std::string content;
CURLWrapper CURL(proxy);
@@ -1053,7 +1157,7 @@ RsFeedReaderErrorState p3FeedReaderThread::processMsg(const RsFeedReaderFeed &fe
if (result != RS_FEED_ERRORSTATE_OK) {
#ifdef FEEDREADER_DEBUG
- std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") cannot download page, CURLCode = " << code << ", error = " << errorString << std::endl;
+ std::cerr << "p3FeedReaderThread::processMsg - feed " << feed.feedId << " (" << feed.name << ") cannot download page, CURLCode = " << code << ", error = " << errorString << std::endl;
#endif
return result;
}
@@ -1070,12 +1174,19 @@ RsFeedReaderErrorState p3FeedReaderThread::processMsg(const RsFeedReaderFeed &fe
if (isRunning()) {
/* process description */
+ bool processPostedFirstImage = (feed.flag & RS_FEED_FLAG_POSTED_FIRST_IMAGE) ? true : false;
+ if (!msg->attachmentBinary.empty()) {
+ /* use attachment as image */
+ processPostedFirstImage = false;
+ }
+
//long todo; // encoding
HTMLWrapper html;
if (html.readHTML(msg->description.c_str(), url.c_str())) {
xmlNodePtr root = html.getRootElement();
if (root) {
std::list nodesToDelete;
+ xmlNodePtr postedFirstImageNode = NULL;
/* process all children */
std::list nodes;
@@ -1192,7 +1303,7 @@ RsFeedReaderErrorState p3FeedReaderThread::processMsg(const RsFeedReaderFeed &fe
if (!src.empty()) {
/* download image */
#ifdef FEEDREADER_DEBUG
- std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") download image " << src << std::endl;
+ std::cerr << "p3FeedReaderThread::processMsg - feed " << feed.feedId << " (" << feed.name << ") download image " << src << std::endl;
#endif
std::vector data;
CURLWrapper CURL(proxy);
@@ -1200,14 +1311,24 @@ RsFeedReaderErrorState p3FeedReaderThread::processMsg(const RsFeedReaderFeed &fe
if (code == CURLE_OK && CURL.responseCode() == 200) {
std::string contentType = CURL.contentType();
if (isContentType(contentType, "image/")) {
- std::string base64;
- if (toBase64(data, base64)) {
- std::string imageBase64;
- rs_sprintf(imageBase64, "data:%s;base64,%s", contentType.c_str(), base64.c_str());
- if (html.setAttr(node, "src", imageBase64.c_str())) {
- removeImage = false;
+ std::vector optimizedData;
+ std::string optimizedMimeType;
+ if (mFeedReader->optimizeImage(FeedReaderOptimizeImageTask::SIZE, data, contentType, optimizedData, optimizedMimeType)) {
+ std::string base64;
+ if (toBase64(optimizedData, base64)) {
+ std::string imageBase64;
+ rs_sprintf(imageBase64, "data:%s;base64,%s", optimizedMimeType.c_str(), base64.c_str());
+ if (html.setAttr(node, "src", imageBase64.c_str())) {
+ removeImage = false;
+ }
}
}
+ if (processPostedFirstImage && postedFirstImageNode == NULL) {
+ /* set first image */
+ msg->postedFirstImage = data;
+ msg->postedFirstImageMimeType = contentType;
+ postedFirstImageNode = node;
+ }
}
}
}
@@ -1241,10 +1362,25 @@ RsFeedReaderErrorState p3FeedReaderThread::processMsg(const RsFeedReaderFeed &fe
if (result == RS_FEED_ERRORSTATE_OK) {
if (isRunning()) {
- if (!html.saveHTML(msg->description)) {
+ if (html.saveHTML(msg->description)) {
+ if (postedFirstImageNode) {
+ /* Remove first image and create description without the image */
+ xmlUnlinkNode(postedFirstImageNode);
+ xmlFreeNode(postedFirstImageNode);
+
+ if (!html.saveHTML(msg->postedDescriptionWithoutFirstImage)) {
+ errorString = html.lastError();
+#ifdef FEEDREADER_DEBUG
+ std::cerr << "p3FeedReaderThread::processMsg - feed " << feed.feedId << " (" << feed.name << ") cannot dump html" << std::endl;
+ std::cerr << " Error: " << errorString << std::endl;
+#endif
+ result = RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR;
+ }
+ }
+ } else {
errorString = html.lastError();
#ifdef FEEDREADER_DEBUG
- std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") cannot dump html" << std::endl;
+ std::cerr << "p3FeedReaderThread::processMsg - feed " << feed.feedId << " (" << feed.name << ") cannot dump html" << std::endl;
std::cerr << " Error: " << errorString << std::endl;
#endif
result = RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR;
@@ -1253,14 +1389,14 @@ RsFeedReaderErrorState p3FeedReaderThread::processMsg(const RsFeedReaderFeed &fe
}
} else {
#ifdef FEEDREADER_DEBUG
- std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") no root element" << std::endl;
+ std::cerr << "p3FeedReaderThread::processMsg - feed " << feed.feedId << " (" << feed.name << ") no root element" << std::endl;
#endif
result = RS_FEED_ERRORSTATE_PROCESS_HTML_ERROR;
}
} else {
errorString = html.lastError();
#ifdef FEEDREADER_DEBUG
- std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") cannot read html" << std::endl;
+ std::cerr << "p3FeedReaderThread::processMsg - feed " << feed.feedId << " (" << feed.name << ") cannot read html" << std::endl;
std::cerr << " Error: " << errorString << std::endl;
#endif
result = RS_FEED_ERRORSTATE_PROCESS_HTML_ERROR;
diff --git a/plugins/FeedReader/services/p3FeedReaderThread.h b/plugins/FeedReader/services/p3FeedReaderThread.h
index 48d920dd6..a9be84010 100644
--- a/plugins/FeedReader/services/p3FeedReaderThread.h
+++ b/plugins/FeedReader/services/p3FeedReaderThread.h
@@ -54,6 +54,11 @@ public:
static RsFeedReaderErrorState processXslt(const std::string &xslt, HTMLWrapper &html, std::string &errorString);
static RsFeedReaderErrorState processTransformation(const RsFeedReaderFeed &feed, RsFeedReaderMsg *msg, std::string &errorString);
+
+ static bool isContentType(const std::string &contentType, const char *type);
+ static bool toBase64(const std::vector &data, std::string &base64);
+ static bool fromBase64(const std::string &base64, std::vector &data);
+
private:
virtual void threadTick() override; /// @see RsTickingThread
diff --git a/plugins/FeedReader/services/rsFeedReaderItems.cc b/plugins/FeedReader/services/rsFeedReaderItems.cc
index 04a6c8f1e..0d5b1f60b 100644
--- a/plugins/FeedReader/services/rsFeedReaderItems.cc
+++ b/plugins/FeedReader/services/rsFeedReaderItems.cc
@@ -46,6 +46,7 @@ void RsFeedReaderFeed::clear()
storageTime = 0;
flag = 0;
forumId.clear();
+ postedId.clear();
description.clear();
icon.clear();
errorState = RS_FEED_ERRORSTATE_OK;
@@ -85,6 +86,7 @@ uint32_t RsFeedReaderSerialiser::sizeFeed(RsFeedReaderFeed *item)
s += item->xpathsToUse.TlvSize();
s += item->xpathsToRemove.TlvSize();
s += GetTlvStringSize(item->xslt);
+ s += GetTlvStringSize(item->postedId);
return s;
}
@@ -108,7 +110,7 @@ bool RsFeedReaderSerialiser::serialiseFeed(RsFeedReaderFeed *item, void *data, u
offset += 8;
/* add values */
- ok &= setRawUInt16(data, tlvsize, &offset, 2); /* version */
+ ok &= setRawUInt16(data, tlvsize, &offset, 3); /* version */
ok &= setRawUInt32(data, tlvsize, &offset, item->feedId);
ok &= setRawUInt32(data, tlvsize, &offset, item->parentId);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_LINK, item->url);
@@ -124,6 +126,7 @@ bool RsFeedReaderSerialiser::serialiseFeed(RsFeedReaderFeed *item, void *data, u
ok &= setRawUInt32(data, tlvsize, &offset, item->storageTime);
ok &= setRawUInt32(data, tlvsize, &offset, item->flag);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_VALUE, item->forumId);
+ ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_VALUE, item->postedId);
ok &= setRawUInt32(data, tlvsize, &offset, item->errorState);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_VALUE, item->errorString);
ok &= setRawUInt32(data, tlvsize, &offset, item->transformationType);
@@ -208,6 +211,9 @@ RsFeedReaderFeed *RsFeedReaderSerialiser::deserialiseFeed(void *data, uint32_t *
ok &= getRawUInt32(data, rssize, &offset, &(item->storageTime));
ok &= getRawUInt32(data, rssize, &offset, &(item->flag));
ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_VALUE, item->forumId);
+ if (version >= 3) {
+ ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_VALUE, item->postedId);
+ }
uint32_t errorState = 0;
ok &= getRawUInt32(data, rssize, &offset, &errorState);
item->errorState = (RsFeedReaderErrorState) errorState;
@@ -268,6 +274,9 @@ void RsFeedReaderMsg::clear()
descriptionTransformed.clear();
pubDate = 0;
flag = 0;
+ attachmentLink.clear();
+ attachment.clear();
+ attachmentMimeType.clear();
}
std::ostream &RsFeedReaderMsg::print(std::ostream &out, uint16_t /*indent*/)
@@ -288,6 +297,9 @@ uint32_t RsFeedReaderSerialiser::sizeMsg(RsFeedReaderMsg *item)
s += GetTlvStringSize(item->descriptionTransformed);
s += sizeof(uint32_t); /* pubDate */
s += sizeof(uint32_t); /* flag */
+ s += GetTlvStringSize(item->attachmentLink);
+ s += GetTlvStringSize(item->attachment);
+ s += GetTlvStringSize(item->attachmentMimeType);
return s;
}
@@ -311,7 +323,7 @@ bool RsFeedReaderSerialiser::serialiseMsg(RsFeedReaderMsg *item, void *data, uin
offset += 8;
/* add values */
- ok &= setRawUInt16(data, tlvsize, &offset, 2); /* version */
+ ok &= setRawUInt16(data, tlvsize, &offset, 3); /* version */
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_GENID, item->msgId);
ok &= setRawUInt32(data, tlvsize, &offset, item->feedId);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_NAME, item->title);
@@ -321,6 +333,9 @@ bool RsFeedReaderSerialiser::serialiseMsg(RsFeedReaderMsg *item, void *data, uin
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_COMMENT, item->descriptionTransformed);
ok &= setRawUInt32(data, tlvsize, &offset, item->pubDate);
ok &= setRawUInt32(data, tlvsize, &offset, item->flag);
+ ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_LOCATION, item->attachmentLink);
+ ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_PIC_AUTH, item->attachment);
+ ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_PIC_TYPE, item->attachmentMimeType);
if (offset != tlvsize)
{
@@ -384,6 +399,11 @@ RsFeedReaderMsg *RsFeedReaderSerialiser::deserialiseMsg(void *data, uint32_t *pk
}
ok &= getRawUInt32(data, rssize, &offset, (uint32_t*) &(item->pubDate));
ok &= getRawUInt32(data, rssize, &offset, &(item->flag));
+ if (version >= 3) {
+ ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_LOCATION, item->attachmentLink);
+ ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_PIC_AUTH, item->attachment);
+ ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_PIC_TYPE, item->attachmentMimeType);
+ }
if (offset != rssize)
{
diff --git a/plugins/FeedReader/services/rsFeedReaderItems.h b/plugins/FeedReader/services/rsFeedReaderItems.h
index a8cce7084..4e343be75 100644
--- a/plugins/FeedReader/services/rsFeedReaderItems.h
+++ b/plugins/FeedReader/services/rsFeedReaderItems.h
@@ -34,17 +34,22 @@ const uint8_t RS_PKT_SUBTYPE_FEEDREADER_MSG = 0x03;
/**************************************************************************/
-#define RS_FEED_FLAG_FOLDER 0x001
-#define RS_FEED_FLAG_INFO_FROM_FEED 0x002
-#define RS_FEED_FLAG_STANDARD_STORAGE_TIME 0x004
-#define RS_FEED_FLAG_STANDARD_UPDATE_INTERVAL 0x008
-#define RS_FEED_FLAG_STANDARD_PROXY 0x010
-#define RS_FEED_FLAG_AUTHENTICATION 0x020
-#define RS_FEED_FLAG_DEACTIVATED 0x040
-#define RS_FEED_FLAG_FORUM 0x080
-#define RS_FEED_FLAG_UPDATE_FORUM_INFO 0x100
-#define RS_FEED_FLAG_EMBED_IMAGES 0x200
-#define RS_FEED_FLAG_SAVE_COMPLETE_PAGE 0x400
+#define RS_FEED_FLAG_FOLDER 0x0001
+#define RS_FEED_FLAG_INFO_FROM_FEED 0x0002
+#define RS_FEED_FLAG_STANDARD_STORAGE_TIME 0x0004
+#define RS_FEED_FLAG_STANDARD_UPDATE_INTERVAL 0x0008
+#define RS_FEED_FLAG_STANDARD_PROXY 0x0010
+#define RS_FEED_FLAG_AUTHENTICATION 0x0020
+#define RS_FEED_FLAG_DEACTIVATED 0x0040
+#define RS_FEED_FLAG_FORUM 0x0080
+#define RS_FEED_FLAG_UPDATE_FORUM_INFO 0x0100
+#define RS_FEED_FLAG_EMBED_IMAGES 0x0200
+#define RS_FEED_FLAG_SAVE_COMPLETE_PAGE 0x0400
+#define RS_FEED_FLAG_POSTED 0x0800
+#define RS_FEED_FLAG_UPDATE_POSTED_INFO 0x1000
+#define RS_FEED_FLAG_POSTED_FIRST_IMAGE 0x2000
+#define RS_FEED_FLAG_POSTED_ONLY_IMAGE 0x4000
+#define RS_FEED_FLAG_POSTED_SHRINK_IMAGE 0x8000
class RsFeedReaderFeed : public RsItem
{
@@ -62,6 +67,7 @@ public:
virtual ~RsFeedReaderFeed() {}
virtual void clear();
+ virtual void serial_process(RsGenericSerializer::SerializeJob, RsGenericSerializer::SerializeContext&) {}
uint32_t feedId;
uint32_t parentId;
@@ -75,6 +81,7 @@ public:
time_t lastUpdate;
uint32_t flag; // RS_FEED_FLAG_...
std::string forumId;
+ std::string postedId;
uint32_t storageTime;
std::string description;
std::string icon;
@@ -105,6 +112,7 @@ public:
virtual ~RsFeedReaderMsg() {}
virtual void clear();
+ virtual void serial_process(RsGenericSerializer::SerializeJob, RsGenericSerializer::SerializeContext&) {}
virtual std::ostream& print(std::ostream &out, uint16_t indent = 0);
std::string msgId;
@@ -116,6 +124,16 @@ public:
std::string descriptionTransformed;
time_t pubDate;
uint32_t flag; // RS_FEEDMSG_FLAG_...
+ std::string attachmentLink;
+ std::string attachment; // binary as base64
+ std::string attachmentMimeType;
+
+ // Only in memory when receiving messages
+ std::vector attachmentBinary;
+ std::string attachmentBinaryMimeType;
+ std::vector postedFirstImage;
+ std::string postedFirstImageMimeType;
+ std::string postedDescriptionWithoutFirstImage;
};
class RsFeedReaderSerialiser: public RsSerialType
diff --git a/plugins/FeedReader/util/CURLWrapper.cpp b/plugins/FeedReader/util/CURLWrapper.cpp
index 4e8c5c9bb..94ae7beaa 100644
--- a/plugins/FeedReader/util/CURLWrapper.cpp
+++ b/plugins/FeedReader/util/CURLWrapper.cpp
@@ -21,6 +21,8 @@
#include "CURLWrapper.h"
#include
+#define USERAGENT "Mozilla/5.0 (rv:109.0) Gecko/20100101 Firefox/112.0"
+
CURLWrapper::CURLWrapper(const std::string &proxy)
{
mCurl = curl_easy_init();
@@ -65,6 +67,7 @@ CURLcode CURLWrapper::downloadText(const std::string &link, std::string &data)
curl_easy_setopt(mCurl, CURLOPT_WRITEFUNCTION, writeFunctionString);
curl_easy_setopt(mCurl, CURLOPT_WRITEDATA, &data);
curl_easy_setopt(mCurl, CURLOPT_SSL_VERIFYPEER, false);
+ curl_easy_setopt(mCurl, CURLOPT_USERAGENT, USERAGENT);
return curl_easy_perform(mCurl);
}
@@ -94,6 +97,8 @@ CURLcode CURLWrapper::downloadBinary(const std::string &link, std::vector::iterator pi = handleIncomingClientData(item->pgp_public_key_b64,item->short_invite);
+ RsPeerId pid;
- // No need to test for it==mCurrentClients.end() because it will be directly caught by the exception handling below even before.
- // Respond with a list of potential friends
+ if(!handleIncomingClientData(item->pgp_public_key_b64,item->short_invite,pid))
+ {
+ RsErr() << "Client data is dropped because of error." ;
+ return ;
+ }
+
+ // All good.
+
+ auto pi(mCurrentClientPeers.find(pid));
+
+ // Update the list of closest peers for other peers, based on which known friends it reports, and of current peer depending
+ // on friendship levels of other peers.
+
+ updateClosestPeers(pi->first,pi->second.pgp_fingerprint,item->already_received_peers);
RsDbg() << "Sending response item to " << item->PeerId() ;
- RsFriendServerServerResponseItem *sr_item = new RsFriendServerServerResponseItem;
+ RsFriendServerServerResponseItem sr_item;
- std::map friends;
- sr_item->nonce = pi->second.last_nonce;
- sr_item->friend_invites = computeListOfFriendInvites(item->n_requested_friends,pi->first,friends);
- sr_item->PeerId(item->PeerId());
+ std::set friends;
+ sr_item.unique_identifier = pi->second.last_identifier;
+ sr_item.friend_invites = computeListOfFriendInvites(pi->first,item->n_requested_friends,item->already_received_peers,friends);
+ sr_item.PeerId(item->PeerId());
- // Update the have_added_as_friend for the list of each peer. We do that before sending because sending destroys
- // the item.
+ RsDbg() << " Got " << sr_item.friend_invites.size() << " closest peers not in the list." ;
+ RsDbg() << " Updating local information for destination peer." ;
- for(const auto& pid:friends)
+ // Update friendship levels of the peer that will receive the new list
+
+ for(auto fr:friends)
{
- auto& p(mCurrentClientPeers[pid.first]);
- p.have_added_this_peer[computePeerDistance(p.pgp_fingerprint, pi->second.pgp_fingerprint)] = pi->first;
+ auto& p(pi->second.friendship_levels[fr]);
+
+ RsDbg() << " Already a friend: " << fr << ", with local status " << static_cast(p) ;
+
+ if(static_cast(p) < static_cast(RsFriendServer::PeerFriendshipLevel::HAS_KEY))
+ {
+ p = RsFriendServer::PeerFriendshipLevel::HAS_KEY;
+ RsDbg() << " --> updating status to HAS_KEY" ;
+ }
}
// Now encrypt the item with the public PGP key of the destination. This prevents the wrong person to request for
// someone else's data.
-#warning TODO
+
+ RsDbg() << " Encrypting item..." ;
+
+ RsFriendServerEncryptedServerResponseItem *encrypted_response_item = new RsFriendServerEncryptedServerResponseItem;
+ uint32_t serialized_clear_size = FsSerializer().size(&sr_item);
+ RsTemporaryMemory serialized_clear_mem(serialized_clear_size);
+ FsSerializer().serialise(&sr_item,serialized_clear_mem,&serialized_clear_size);
+
+ uint32_t encrypted_mem_size = serialized_clear_size+1000; // leave some extra space
+ RsTemporaryMemory encrypted_mem(encrypted_mem_size);
+
+ if(!mPgpHandler->encryptDataBin(PGPHandler::pgpIdFromFingerprint(pi->second.pgp_fingerprint),
+ serialized_clear_mem,serialized_clear_size,
+ encrypted_mem,&encrypted_mem_size))
+ {
+ RsErr() << "Cannot encrypt item for PGP Id/FPR " << pi->second.pgp_fingerprint << ". Something went wrong." ;
+ return;
+ }
+ encrypted_response_item->PeerId(item->PeerId());
+ encrypted_response_item->bin_len = encrypted_mem_size;
+ encrypted_response_item->bin_data = malloc(encrypted_mem_size);
+
+ memcpy(encrypted_response_item->bin_data,encrypted_mem,encrypted_mem_size);
// Send the item.
- mni->SendItem(sr_item);
- // Update the list of closest peers for all peers currently in the database.
-
- updateClosestPeers(pi->first,pi->second.pgp_fingerprint);
+ RsDbg() << " Sending item..." ;
+ mni->SendItem(encrypted_response_item);
}
catch(std::exception& e)
{
@@ -121,11 +164,14 @@ void FriendServer::handleClientPublish(const RsFriendServerClientPublishItem *it
}
}
-std::map FriendServer::computeListOfFriendInvites(uint32_t nb_reqs_invites, const RsPeerId &pid, std::map& friends)
+std::map FriendServer::computeListOfFriendInvites(const RsPeerId &pid, uint32_t nb_reqs_invites,
+ const std::map& already_known_peers,
+ std::set& chosen_peers) const
{
// Strategy: we want to return the same set of friends for a given PGP profile key.
// Still, using some closest distance strategy, the n-closest peers for profile A is not the
- // same set than the n-closest peers for profile B. We have multiple options:
+ // same set than the n-closest peers for profile B, so some peers will not be in both sets.
+ // We have multiple options:
//
// Option 1:
//
@@ -145,38 +191,54 @@ std::map FriendServer::computeListOfFriendInvites(uint32_t nb
//
// So we choose Option 2.
- std::map res;
+ std::map res;
+ chosen_peers.clear();
+ auto pinfo_it(mCurrentClientPeers.find(pid));
- auto add_from = [&res,&friends,nb_reqs_invites,this](bool added,const std::map& lst) -> bool
+ if(pinfo_it == mCurrentClientPeers.end())
{
- for(const auto& pid:lst)
- {
- const auto p = mCurrentClientPeers.find(pid.second);
- res.insert(std::make_pair(p->second.short_certificate,added));
- friends.insert(std::make_pair(p->first,p->second.pgp_fingerprint));
-
- if(res.size() >= nb_reqs_invites)
- return true;
- }
- return false;
- };
-
- const auto& pinfo(mCurrentClientPeers[pid]);
-
- // First add from peers who already added the current peer as friend, and leave if we already have enough
-
- if(add_from(true,pinfo.have_added_this_peer))
+ RsErr() << "inconsistency in computeListOfFriendInvites. Something's wrong in the code." ;
return res;
+ }
+ auto pinfo(pinfo_it->second);
- add_from(false,pinfo.closest_peers);
+ for(const auto& pit:pinfo.closest_peers)
+ {
+ if(already_known_peers.find(pit.second) == already_known_peers.end())
+ {
+ RsDbg() << " peer " << pit.second << ": not in supplied list => adding it.";
+
+ const auto p = mCurrentClientPeers.find(pit.second);
+
+ if(p == mCurrentClientPeers.end()) // should not happen, but just an extra security.
+ continue;
+
+ auto pp = p->second.friendship_levels.find(pid);
+
+ auto peer_friendship_level = (pp==p->second.friendship_levels.end())?(RsFriendServer::PeerFriendshipLevel::UNKNOWN):(pp->second);
+
+ res[p->second.short_certificate] = peer_friendship_level;
+ chosen_peers.insert(p->first);
+
+ if(res.size() + already_known_peers.size() >= nb_reqs_invites)
+ break;
+ }
+ else
+ {
+ auto p = already_known_peers.find(pit.second);
+ RsDbg() << " peer " << pit.second << ": already in supplied list, with status " << static_cast(p->second) << ". Not adding it.";
+ }
+ }
return res;
}
-std::map::iterator FriendServer::handleIncomingClientData(const std::string& pgp_public_key_b64,const std::string& short_invite_b64)
+bool FriendServer::handleIncomingClientData(const std::string& pgp_public_key_b64,const std::string& short_invite_b64,RsPeerId& pid)
{
// 1 - Check that the incoming data is sound.
+ try
+ {
RsDbg() << " Checking item data...";
std::string error_string;
@@ -212,24 +274,21 @@ std::map::iterator FriendServer::handleIncomingClientData(con
// 3 - if the key is not already here, add it to keyring.
{
- RsPgpFingerprint fpr_test;
- if(mPgpHandler->isPgpPubKeyAvailable(RsPgpId::fromBufferUnsafe(received_key_info.fingerprint+12)))
- RsDbg() << " PGP Key is already into keyring.";
- else
- {
- RsPgpId pgp_id;
- if(!mPgpHandler->LoadCertificateFromBinaryData(key_binary_data.data(),key_binary_data.size(), pgp_id, error_string))
- throw std::runtime_error("Cannot load client's pgp public key into keyring: " + error_string) ;
+ RsPgpFingerprint fpr_test;
+ if(mPgpHandler->isPgpPubKeyAvailable(RsPgpId::fromBufferUnsafe(received_key_info.fingerprint+12)))
+ RsDbg() << " PGP Key is already into keyring.";
+ else
+ {
+ RsPgpId pgp_id;
+ if(!mPgpHandler->LoadCertificateFromBinaryData(key_binary_data.data(),key_binary_data.size(), pgp_id, error_string))
+ throw std::runtime_error("Cannot load client's pgp public key into keyring: " + error_string) ;
- RsDbg() << " Public key added to keyring.";
- RsDbg() << " Sync-ing the PGP keyring on disk";
+ RsDbg() << " Public key added to keyring.";
+ RsDbg() << " Sync-ing the PGP keyring on disk";
- mPgpHandler->syncDatabase();
+ mPgpHandler->syncDatabase();
+ }
}
- }
-
- // All good.
-
// Store/update the peer info
auto& pi(mCurrentClientPeers[shortInviteDetails.id]);
@@ -238,13 +297,19 @@ std::map::iterator FriendServer::handleIncomingClientData(con
pi.last_connection_TS = time(nullptr);
pi.pgp_fingerprint = shortInviteDetails.fpr;
- while(pi.last_nonce == 0) // reuse the same identifier (so it's not really a nonce, but it's kept secret whatsoever).
- pi.last_nonce = RsRandom::random_u64();
+ while(pi.last_identifier == 0) // reuse the same identifier (so it's not really a nonce, but it's kept secret whatsoever).
+ pi.last_identifier = RsRandom::random_u64();
- return mCurrentClientPeers.find(shortInviteDetails.id);
+ pid = shortInviteDetails.id;
+ return true;
+ }
+ catch (std::exception& e)
+ {
+ RsErr() << "Exception while adding client data: " << e.what() ;
+ return false;
+ }
}
-
void FriendServer::handleClientRemove(const RsFriendServerClientRemoveItem *item)
{
RsDbg() << "Received a client remove item:" << *item ;
@@ -257,14 +322,14 @@ void FriendServer::handleClientRemove(const RsFriendServerClientRemoveItem *item
return;
}
- if(it->second.last_nonce != item->nonce)
+ if(it->second.last_identifier != item->unique_identifier)
{
- RsErr() << " ERROR: Client supplied a nonce " << std::hex << item->nonce << std::dec << " that is not correct (expected "
- << std::hex << it->second.last_nonce << std::dec << ")";
+ RsErr() << " ERROR: Client supplied a nonce " << std::hex << item->unique_identifier << std::dec << " that is not correct (expected "
+ << std::hex << it->second.last_identifier << std::dec << ")";
return;
}
- RsDbg() << " Nonce is correct: " << std::hex << item->nonce << std::dec << ". Removing peer " << item->peer_id ;
+ RsDbg() << " Nonce is correct: " << std::hex << item->unique_identifier << std::dec << ". Removing peer " << item->peer_id ;
removePeer(item->peer_id);
}
@@ -283,7 +348,7 @@ void FriendServer::removePeer(const RsPeerId& peer_id)
for(auto pit(it.second.closest_peers.begin());pit!=it.second.closest_peers.end();)
if(pit->second == peer_id)
{
- RsDbg() << " Removing from n-closest peers of peer " << pit->first ;
+ RsDbg() << " Removing from n-closest peers of peer " << it.first ;
auto tmp(pit);
++tmp;
@@ -293,27 +358,26 @@ void FriendServer::removePeer(const RsPeerId& peer_id)
else
++pit;
- // Also remove that peer from peers that have accepted each peer
+ // Also remove that peer from friendship levels of that particular peer.
- for(auto fit(it.second.have_added_this_peer.begin());fit!=it.second.have_added_this_peer.end();)
- if(fit->second == peer_id)
- {
- RsDbg() << " Removing from have_added_as_friend peers of peer " << fit->first ;
+ auto fit = it.second.friendship_levels.find(peer_id);
- auto tmp(fit);
- ++tmp;
- it.second.closest_peers.erase(fit);
- fit=tmp;
- }
- else
- ++fit;
+ if(fit != it.second.friendship_levels.end())
+ {
+ RsDbg() << " Removing from have_added_as_friend peers of peer " << it.first ;
+ it.second.friendship_levels.erase(fit);
+ }
}
}
PeerInfo::PeerDistance FriendServer::computePeerDistance(const RsPgpFingerprint& p1,const RsPgpFingerprint& p2)
{
- std::cerr << "Computing peer distance: p1=" << p1 << " p2=" << p2 << " p1^p2=" << (p1^p2) << " distance=" << ((p1^p2)^mRandomPeerBias) << std::endl;
- return (p1 ^ p2)^mRandomPeerBias;
+ auto res = (p1 ^ p2)^mRandomPeerBias;
+ auto res2 = RsDirUtil::sha1sum(res.toByteArray(),res.SIZE_IN_BYTES); // sha1sum prevents reverse finding the random bias
+
+ std::cerr << "Computing peer distance: p1=" << p1 << " p2=" << p2 << " p1^p2=" << (p1^p2) << " distance=" << res2 << std::endl;
+
+ return res2;
}
FriendServer::FriendServer(const std::string& base_dir,const std::string& listening_address,uint16_t listening_port)
: mListeningAddress(listening_address),mListeningPort(listening_port)
@@ -349,8 +413,6 @@ void FriendServer::run()
void FriendServer::autoWash()
{
rstime_t now = time(nullptr);
- RsDbg() << "autoWash..." ;
-
std::list to_remove;
for(std::map::iterator it(mCurrentClientPeers.begin());it!=mCurrentClientPeers.end();++it)
@@ -362,51 +424,136 @@ void FriendServer::autoWash()
for(auto peer_id:to_remove)
removePeer(peer_id);
-
- RsDbg() << "done." ;
}
-void FriendServer::updateClosestPeers(const RsPeerId& pid,const RsPgpFingerprint& fpr)
+void FriendServer::updateClosestPeers(const RsPeerId& pid,const RsPgpFingerprint& fpr,const std::map& friended_peers)
{
+ auto find_multi = [](PeerInfo::PeerDistance dist,std::map< std::pair,RsPeerId >& mp)
+ -> std::map< std::pair,RsPeerId >::iterator
+ {
+ auto it = mp.find(std::make_pair(RsFriendServer::PeerFriendshipLevel::UNKNOWN,dist)) ;
+
+ if(it == mp.end()) it = mp.find(std::make_pair(RsFriendServer::PeerFriendshipLevel::NO_KEY ,dist));
+ if(it == mp.end()) it = mp.find(std::make_pair(RsFriendServer::PeerFriendshipLevel::HAS_KEY ,dist));
+ if(it == mp.end()) it = mp.find(std::make_pair(RsFriendServer::PeerFriendshipLevel::HAS_ACCEPTED_KEY,dist));
+
+ return it;
+ };
+ auto remove_from_map = [find_multi](PeerInfo::PeerDistance dist,
+ std::map< std::pair,RsPeerId>& mp) -> bool
+ {
+ auto mpit = find_multi(dist,mp);
+
+ if(mpit != mp.end())
+ {
+ mp.erase(mpit);
+ return true;
+ }
+ else
+ return false;
+ };
+
+ auto& pit(mCurrentClientPeers[pid]);
+
for(auto& it:mCurrentClientPeers)
if(it.first != pid)
{
+ // 1 - for all existing peers, update the level at which the given peer has added the peer as friend.
+
+ auto peer_iterator = friended_peers.find(it.first);
+ auto peer_friendship_level = (peer_iterator==friended_peers.end())? (RsFriendServer::PeerFriendshipLevel::UNKNOWN):(peer_iterator->second);
+
PeerInfo::PeerDistance d = computePeerDistance(fpr,it.second.pgp_fingerprint);
- it.second.closest_peers.insert(std::make_pair(d,pid));
+ // Remove the peer from the map. This is costly. I need to find something better. If the peer is already
+ // in the list, it has a map key with the same distance.
- if(it.second.closest_peers.size() > MAXIMUM_PEERS_TO_REQUEST)
+ remove_from_map(d,it.second.closest_peers);
+
+ it.second.closest_peers[std::make_pair(peer_friendship_level,d)] = pid;
+
+ while(it.second.closest_peers.size() > MAXIMUM_PEERS_TO_REQUEST)
it.second.closest_peers.erase(std::prev(it.second.closest_peers.end()));
+
+ // 2 - for the current peer, update the list of closest peers
+
+ auto pit2 = it.second.friendship_levels.find(pid);
+ peer_friendship_level = (pit2==it.second.friendship_levels.end())? (RsFriendServer::PeerFriendshipLevel::UNKNOWN):(pit2->second);
+
+ remove_from_map(d,pit.closest_peers);
+
+ pit.closest_peers[std::make_pair(peer_friendship_level,d)] = it.first;
+
+ while(pit.closest_peers.size() > MAXIMUM_PEERS_TO_REQUEST)
+ pit.closest_peers.erase(std::prev(pit.closest_peers.end()));
}
+
+ // Also update the friendship levels for the current peer, of all friends from the list.
+
+ for(auto it:friended_peers)
+ pit.friendship_levels[it.first] = it.second;
}
-void FriendServer::debugPrint()
+Sha1CheckSum FriendServer::computeDataHash()
{
- RsDbg() << "========== FriendServer statistics ============";
- RsDbg() << " Base directory: "<< mBaseDirectory;
- RsDbg() << " Random peer bias: "<< mRandomPeerBias;
- RsDbg() << " Network interface: ";
- RsDbg() << " Max peers in n-closest list: " << MAXIMUM_PEERS_TO_REQUEST;
- RsDbg() << " Current active peers: " << mCurrentClientPeers.size() ;
+ librs::crypto::HashStream s(librs::crypto::HashStream::SHA1);
- rstime_t now = time(nullptr);
-
- for(const auto& it:mCurrentClientPeers)
+ for(auto p(mCurrentClientPeers.begin());p!=mCurrentClientPeers.end();++p)
{
- RsDbg() << " " << it.first << ": nonce=" << std::hex << it.second.last_nonce << std::dec << " fpr: " << it.second.pgp_fingerprint << ", last contact: " << now - it.second.last_connection_TS << " secs ago.";
- RsDbg() << " Closest peers:" ;
+ s << p->first;
- for(const auto& pit:it.second.closest_peers)
- RsDbg() << " " << pit.second << " distance=" << pit.first ;
+ const auto& inf(p->second);
- RsDbg() << " Have added this peer:" ;
+ s << inf.pgp_fingerprint;
+ s << inf.short_certificate;
+ s << (uint64_t)inf.last_connection_TS;
+ s << inf.last_identifier;
- for(const auto& pit:it.second.have_added_this_peer)
- RsDbg() << " " << pit.second << " distance=" << pit.first ;
+ for(auto d(inf.closest_peers.begin());d!=inf.closest_peers.end();++d)
+ {
+ s << static_cast(d->first.first) ;
+ s << d->first.second ;
+ s << d->second;
+ }
+ for(auto d:inf.friendship_levels)
+ {
+ s << d.first ;
+ s << static_cast(d.second);
+ }
+ }
+ return s.hash();
+}
+void FriendServer::debugPrint(bool force)
+{
+ auto h = computeDataHash();
+
+ if((h != mCurrentDataHash) || force)
+ {
+ RsDbg() << "========== FriendServer statistics ============";
+ RsDbg() << " Base directory: "<< mBaseDirectory;
+ RsDbg() << " Random peer bias: "<< mRandomPeerBias;
+ RsDbg() << " Current hash: "<< h;
+ RsDbg() << " Network interface: ";
+ RsDbg() << " Max peers in n-closest list: " << MAXIMUM_PEERS_TO_REQUEST;
+ RsDbg() << " Current active peers: " << mCurrentClientPeers.size() ;
+
+ rstime_t now = time(nullptr);
+
+ for(const auto& it:mCurrentClientPeers)
+ {
+ RsDbg() << " " << it.first << ": identifier=" << std::hex << it.second.last_identifier << std::dec << " fpr: " << it.second.pgp_fingerprint << ", last contact: " << now - it.second.last_connection_TS << " secs ago.";
+ RsDbg() << " Closest peers:" ;
+
+ for(auto pit:it.second.closest_peers)
+ RsDbg() << " " << pit.second << " distance=" << pit.first.second << " Peer reciprocal status:" << static_cast(pit.first.first);
+ }
+
+ RsDbg() << "===============================================";
+
+ mCurrentDataHash = h;
}
- RsDbg() << "===============================================";
-
}
diff --git a/retroshare-friendserver/src/friendserver.h b/retroshare-friendserver/src/friendserver.h
index 202f6a1e0..259b0e5ba 100644
--- a/retroshare-friendserver/src/friendserver.h
+++ b/retroshare-friendserver/src/friendserver.h
@@ -24,23 +24,108 @@
#include "util/rsthreads.h"
#include "pqi/pqistreamer.h"
#include "pgp/pgphandler.h"
+#include "retroshare/rsfriendserver.h"
#include "network.h"
class RsFriendServerClientRemoveItem;
class RsFriendServerClientPublishItem;
+// +================================================================+
+// | o---o o |
+// | \ / - Retroshare Friend Server - / \ |
+// | o o---o |
+// +================================================================+
+//
+// The friend server facilitates a group of RS Tor-nodes to make friends. It maintains a pool of
+// participants (RS nodes currently susbscribing to the friend server) and advertise them to each other
+// as possible friends. Its goal is to allow new RS users to quickly experiment with the software without
+// compromising their anonymity.
+//
+// Implementation
+// ==============
+//
+// The implementation is entirely client-based: clients make a request, and get a response. No connection is maintained
+// beyond this interaction. Consequently, the friend server returns a random ID to each client that the client can use to
+// e.g. signal its departure from the friend server and the release of its data.
+//
+// Both client and server use a binary interface linked to a proxy-connected socket to stream RS items, everything
+// happenning on top of Tor connections.
+//
+// Algorithms
+// ==========
+//
+// * Protocol
+//
+// Retroshare Client Server (Friend Server)
+//
+// ------------ Tor connection --------> no action
+// Server online MSG <-------------- Tor ACK ------------
+//
+//
+// Friend Req. loop ------------ Friend Request --------> Friend list calculation / update
+// <---------- Friend list + ID --------
+//
+//
+// FS disabled ------------ FS Close + ID ---------> Data cleaning, peer removal.
+//
+//
+// * Friend selection
+//
+// In order to reduce the ease to retrieve the list of all participants to a friend server, the
+// friend server always returns the same list of friends to a given peer. To do so, participants are sorted
+// for each peer, using a XOR distance such as:
+//
+// d(P1,P2) = P1 (XOR) P2 (XOR) R
+//
+// ...where R is a random bias.
+//
+// Since being in the n closest peers is not a reflexive relationship (P1 may be within the n closest peers
+// to P2 but P2 may not be in the n closest peers to P1), selected friends for peer A are picked from both
+// the closest peers of A, and the peers that received the RS certificate of A.
+//
+// Another important effect of the stability of retrieved friends is to maintain a network that is not
+// fully connected and stable over time, which corresponds to the mesh model of the RS network.
+//
+// * Peer friendship level
+//
+// For display purposes, the friend server also stores the "friendship level" for each pair of peers,
+// that means whether the peer has added the other peer as friend, or only reveived his key, etc.
+//
+// Peers send to the friend server the list of peers they already have, with their own friendship
+// level with that peer. The FS needs to send back a list of peers, with the friendship level to the current peer.
+// In the list of closest peers, the reverse friendship levels are stored: for a peer A the reverse friendship
+// level to peer B is whether B has added A as friend or not.In the list of friends for a peer, the forward FL
+// is stored. The forward FL of a peer A to a peer B is whether A has added B as friend or not.
+//
+// * Security
+//
+// Obviously the friend server knows who is possibly connected to whom. Since the connections to the
+// friend server are anonymous, this information is difficult to protect, although the implementation
+// currently makes it difficult to retrieve.
+//
+// The friend server is only available to Tor nodes, since it allows RS nodes to connect to random peers.
+// This allows trying the software without compromizing one's privacy.
+
struct PeerInfo
{
- typedef RsPgpFingerprint PeerDistance;
+ typedef Sha1CheckSum PeerDistance;
RsPgpFingerprint pgp_fingerprint;
std::string short_certificate;
rstime_t last_connection_TS;
- uint64_t last_nonce;
+ uint64_t last_identifier;
- std::map closest_peers;
- std::map have_added_this_peer;
+ // The following map contains the list of closest peers. The sorting is based
+ // on a combination of the peer XOR distance and the friendship level, so that
+ // peers which already have added a peer are considered first as potential receivers of his key.
+ // The friendship level here is a reverse FL, e.g. whether each closest peer has added the current peer as friend.
+
+ std::map,RsPeerId > closest_peers; // limited in size.
+
+ // Which peers have received the key for that particular peer, along with the direct friendship level: whether current peer has added each peer.
+
+ std::map friendship_levels; // unlimited in size, but no distance sorting.
};
class FriendServer : public RsTickingThread
@@ -60,22 +145,25 @@ private:
void handleClientPublish(const RsFriendServerClientPublishItem *item);
// Updates for each peer in the database, the list of closest peers w.r.t. some arbitrary distance.
- void updateClosestPeers(const RsPeerId& pid,const RsPgpFingerprint& fpr);
+ void updateClosestPeers(const RsPeerId& pid, const RsPgpFingerprint& fpr, const std::map &friended_peers);
// removes a single peer from all lists.
void removePeer(const RsPeerId& peer_id);
// Adds the incoming peer data to the list of current clients and returns the
- std::map::iterator handleIncomingClientData(const std::string& pgp_public_key_b64,const std::string& short_invite_b64);
+ bool handleIncomingClientData(const std::string& pgp_public_key_b64, const std::string& short_invite_b64, RsPeerId &pid);
// Computes the appropriate list of short invites to send to a given peer.
- std::map computeListOfFriendInvites(uint32_t nb_reqs_invites, const RsPeerId &pid, std::map& friends);
+ std::map computeListOfFriendInvites(const RsPeerId &pid, uint32_t nb_reqs_invites,
+ const std::map& already_known_peers,
+ std::set& chosen_peers) const;
// Compute the distance between peers using the random bias (It's not really a distance though. I'm not sure about the triangular inequality).
PeerInfo::PeerDistance computePeerDistance(const RsPgpFingerprint &p1, const RsPgpFingerprint &p2);
void autoWash();
- void debugPrint();
+ void debugPrint(bool force);
+ Sha1CheckSum computeDataHash();
// Local members
@@ -88,4 +176,6 @@ private:
std::map mCurrentClientPeers;
std::string mListeningAddress;
uint16_t mListeningPort;
+
+ Sha1CheckSum mCurrentDataHash;
};
diff --git a/retroshare-friendserver/src/network.cc b/retroshare-friendserver/src/network.cc
index 3ed5b22a2..f28a38de0 100644
--- a/retroshare-friendserver/src/network.cc
+++ b/retroshare-friendserver/src/network.cc
@@ -27,6 +27,14 @@
#include
#include
+#ifdef WINDOWS_SYS
+#include
+#include
+#include
+#else
+#include
+#endif
+
#include "util/rsnet.h"
#include "util/rsprint.h"
#include "util/rsdebug.h"
@@ -45,6 +53,9 @@ FsNetworkInterface::FsNetworkInterface(const std::string& listening_address,uint
mClintListn = 0;
mClintListn = socket(AF_INET, SOCK_STREAM, 0); // creating socket
+ int flags=1;
+ setsockopt(mClintListn,SOL_SOCKET,TCP_NODELAY,(char*)&flags,sizeof(flags));
+
unix_fcntl_nonblock(mClintListn);
struct sockaddr_in ipOfServer;
@@ -102,15 +113,19 @@ void FsNetworkInterface::threadTick()
std::list to_close;
- RS_STACK_MUTEX(mFsNiMtx);
- for(auto& it:mConnections)
- if(it.second.bio->isactive())
+ {
+ RS_STACK_MUTEX(mFsNiMtx);
+ for(auto& it:mConnections)
+ {
it.second.pqi_thread->tick();
- else
- to_close.push_back(it.first);
- for(const auto& pid:to_close)
- locked_closeConnection(pid);
+ if(!it.second.bio->isactive() && !it.second.bio->moretoread(0))
+ to_close.push_back(it.first);
+ }
+
+ for(const auto& pid:to_close)
+ locked_closeConnection(pid);
+ }
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
@@ -134,7 +149,9 @@ bool FsNetworkInterface::checkForNewConnections()
if(clintConnt < 0)
{
- if(errno == EWOULDBLOCK)
+ int err = rs_socket_error();
+
+ if(err == EWOULDBLOCK || err == EAGAIN)
;//RsErr()<< "Incoming connection with nothing to read!" << std::endl;
else
RsErr()<< "Error when accepting connection." << std::endl;
@@ -145,16 +162,24 @@ bool FsNetworkInterface::checkForNewConnections()
// Make the socket non blocking so that we can read from it and return if nothing comes
+ int flags=1;
+ setsockopt(clintConnt,SOL_SOCKET,TCP_NODELAY,(char*)&flags,sizeof(flags));
+
unix_fcntl_nonblock(clintConnt);
// Create connection info
+ RsDbg() << " Creating connection data." ;
+
ConnectionData c;
c.socket = clintConnt;
c.client_address = addr;
-
RsPeerId pid = makePeerId(clintConnt);
+ RsDbg() << " socket: " << clintConnt;
+ RsDbg() << " client address: " << sockaddr_storage_tostring(*(sockaddr_storage*)&addr);
+ RsDbg() << " peer id: " << pid ;
+
// Setup a pqistreamer to deserialize whatever comes from this connection
RsSerialiser *rss = new RsSerialiser ;
@@ -167,11 +192,14 @@ bool FsNetworkInterface::checkForNewConnections()
c.pqi_thread = pqi;
c.bio = bio;
- pqi->start();
+ {
+ RS_STACK_MUTEX(mFsNiMtx);
+ mConnections[pid] = c;
- RS_STACK_MUTEX(mFsNiMtx);
- mConnections[pid] = c;
+ pqi->start();
+ }
+ RsDbg() << " streamer has properly started." ;
return true;
}
@@ -179,6 +207,8 @@ bool FsNetworkInterface::RecvItem(RsItem *item)
{
RS_STACK_MUTEX(mFsNiMtx);
+ RsDbg() << "FsNetworkInterface: received item " << (void*)item;
+
auto it = mConnections.find(item->PeerId());
if(it == mConnections.end())
@@ -203,6 +233,7 @@ RsItem *FsNetworkInterface::GetItem()
RsItem *item = it.second.incoming_items.front();
it.second.incoming_items.pop_front();
+ RsDbg() << "FsNetworkInterface: returning item " << (void*)item << " to caller.";
return item;
}
}
diff --git a/retroshare-friendserver/src/retroshare-friendserver.cc b/retroshare-friendserver/src/retroshare-friendserver.cc
index d80ad71d6..fabe3889d 100644
--- a/retroshare-friendserver/src/retroshare-friendserver.cc
+++ b/retroshare-friendserver/src/retroshare-friendserver.cc
@@ -27,6 +27,7 @@
#include "retroshare/rstor.h"
#include "retroshare/rsinit.h"
+#include "pqi/authgpg.h"
#include "friendserver.h"
@@ -47,20 +48,32 @@ int main(int argc, char* argv[])
//RsControl::earlyInitNotificationSystem();
std::string base_directory = "FSData";
+ std::string tor_executable_path ;
+ bool verbose = false;
argstream as(argc,argv);
as >> parameter( 'c',"base-dir", base_directory, "set base directory to store data files (keys, etc)", false )
- >> help( 'h', "help", "Display this Help" );
+ >> parameter( 't',"tor-executable", tor_executable_path, "set absolute path for tor executable", false )
+ >> option( 'v',"verbose", verbose, "display additional debug information")
+ >> help( 'h', "help", "Display this Help" );
as.defaultErrorHandling(true, true);
RsConfigOptions conf;
+ conf.optBaseDir = base_directory;
conf.main_executable_path = argv[0];
+ if(!tor_executable_path.empty())
+ RsTor::setTorExecutablePath(tor_executable_path);
+
+ RsTor::setVerbose(verbose);
+
RsInit::InitRsConfig();
RsInit::InitRetroShare(conf);
+ AuthPGP::exit(); // This allows to release the keyring created by libretroshare, since it's not useful, as TorManager has its own.
+
// Create the base directory if needed
if(!RsDirUtil::checkCreateDirectory(base_directory))
@@ -68,6 +81,19 @@ int main(int argc, char* argv[])
RsErr() << "Cannot create base directory \"" << base_directory << "\". Check permissions, paths, etc." ;
return 1;
}
+
+ // Check the existance of the Tor executable path
+
+ auto tor_path = RsTor::torExecutablePath();
+
+ if (!RsDirUtil::fileExists(tor_path))
+ {
+ RsErr() << "Tor executable \"" << tor_path << "\" not found. Try supplying the correct full path for a tor executable with the -t option.";
+ return 1;
+ }
+ else
+ RsDbg() << "Using Tor executable: \"" << tor_path << "\"";
+
// Create/start TorManager
RsTor::setTorDataDirectory(RsDirUtil::makePath(base_directory,"tor"));
@@ -89,22 +115,22 @@ int main(int argc, char* argv[])
RsTor::getHiddenServiceInfo(service_id,onion_address,service_port,service_target_address,target_port) ;
- RsDbg() << "Tor properly started: " ;
- RsDbg() << " Hidden service address: " << onion_address << ":" << service_port;
- RsDbg() << " Target address : " << service_target_address << ":" << target_port;
+ RsInfo() << "Tor properly started: " ;
+ RsInfo() << " Hidden service address: " << onion_address << ":" << service_port;
+ RsInfo() << " Target address : " << service_target_address << ":" << target_port;
// Now start the real thing.
FriendServer fs(base_directory,service_target_address,target_port);
fs.start();
- RsDbg() << "";
- RsDbg() << "================== Retroshare Friend Server has properly started =====================" ;
- RsDbg() << "= =";
- RsDbg() << "= Address:Port " << onion_address << ":" << service_port << ((service_port<10000)?" ":"") << " =";
- RsDbg() << "= =";
- RsDbg() << "======================================================================================" ;
- RsDbg() << "";
+ RsInfo() << "";
+ RsInfo() << "================== Retroshare Friend Server has properly started =====================" ;
+ RsInfo() << "= =";
+ RsInfo() << "= Address:Port " << onion_address << ":" << service_port << ((service_port<10000)?" ":"") << " =";
+ RsInfo() << "= =";
+ RsInfo() << "======================================================================================" ;
+ RsInfo() << "";
while(fs.isRunning())
std::this_thread::sleep_for(std::chrono::seconds(2));
diff --git a/retroshare-friendserver/src/retroshare-friendserver.pro b/retroshare-friendserver/src/retroshare-friendserver.pro
index 61c604e43..eda1f35cb 100644
--- a/retroshare-friendserver/src/retroshare-friendserver.pro
+++ b/retroshare-friendserver/src/retroshare-friendserver.pro
@@ -29,8 +29,7 @@ SOURCES += retroshare-friendserver.cc \
network.cc
HEADERS += friendserver.h \
- network.h \
- fsitem.h
+ network.h
################################# Linux ##########################################
@@ -44,6 +43,7 @@ unix {
win32-g++|win32-clang-g++ {
dLib = ws2_32 iphlpapi crypt32
LIBS *= $$linkDynamicLibs(dLib)
+ CONFIG += console
}
################################### COMMON stuff ##################################
diff --git a/retroshare-gui/CMakeLists.txt b/retroshare-gui/CMakeLists.txt
new file mode 100644
index 000000000..314edf1ab
--- /dev/null
+++ b/retroshare-gui/CMakeLists.txt
@@ -0,0 +1,492 @@
+################################################################################
+# retroshare-gui/CMakeLists.txt #
+# Copyright (C) 2022, 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 . #
+################################################################################
+
+cmake_minimum_required (VERSION 3.18.0)
+project(retroshare-gui VERSION 0.6.6 LANGUAGES CXX)
+
+include(CMakeDependentOption)
+
+set(
+ RS_BIN_INSTALL_DIR
+ "${CMAKE_INSTALL_PREFIX}/bin"
+ CACHE PATH
+ "Path where to install retroshare-service compiled binary" )
+
+option(
+ RS_DEVELOPMENT_BUILD
+ "Disable optimization to speed up build, enable verbose build log. \
+ just for development purposes, not suitable for library usage"
+ ON )
+
+option(
+ RS_JSON_API
+ "Use restbed to expose libretroshare as JSON API via HTTP"
+ OFF )
+
+option(
+ RS_SERVICE_DESKTOP
+ "Install icons and shortcuts for desktop environements"
+ OFF )
+
+option(
+ RS_SERVICE_TERMINAL_LOGIN
+ "Enable RetroShare login via terminal"
+ ON )
+
+option( RS_GXSGUI "Enable GXS services in GUI" ON )
+option( RS_GXSCHANNELS "Enable GXS channels in GUI" ON )
+option( RS_GXSFORUMS "Enable GXS forums in GUI" ON )
+option( RS_GXSPOSTED "Enable GXS posted in GUI" ON )
+option( RS_GXSCIRCLES "Enable GXS circles in GUI" ON )
+
+cmake_dependent_option(
+ RS_SERVICE_TERMINAL_WEBUI_PASSWORD
+ "Enable settin Web UI password via terminal in retroshare-service"
+ OFF
+ "RS_WEBUI"
+ ON )
+
+cmake_dependent_option(
+ RS_WEBUI
+ "Install RetroShare Web UI"
+ OFF
+ "RS_JSON_API"
+ ON )
+
+################################ QT FILES #######################################
+
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+set(CMAKE_AUTOUIC ON)
+
+find_package( Qt5 COMPONENTS Core REQUIRED)
+find_package( Qt5 COMPONENTS Widgets REQUIRED)
+find_package( Qt5 COMPONENTS Xml REQUIRED)
+find_package( Qt5 COMPONENTS Network REQUIRED)
+find_package( Qt5 COMPONENTS Multimedia REQUIRED)
+find_package( Qt5 COMPONENTS PrintSupport REQUIRED)
+
+list( APPEND RS_LINK_LIBRARIES Qt5::Multimedia Qt5::Widgets Qt5::Xml Qt5::Network Qt5::PrintSupport)
+
+################################################################################
+
+set(FETCHCONTENT_QUIET OFF)
+include(FetchContent)
+
+find_package(Git REQUIRED)
+
+################################################################################
+
+if(RS_DEVELOPMENT_BUILD)
+ set(CMAKE_VERBOSE_MAKEFILE ON)
+ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+endif(RS_DEVELOPMENT_BUILD)
+
+################################################################################
+
+include(src/CMakeLists.txt)
+
+qt5_wrap_ui(RS_UI_HEADERS ${RS_GUI_FORMS})
+
+add_executable(${PROJECT_NAME} ${RS_GUI_SOURCES} ${RS_UI_HEADERS} ${RS_GUI_QTRESOURCES})
+install(TARGETS ${PROJECT_NAME} DESTINATION ${RS_BIN_INSTALL_DIR})
+
+include_directories( ${CMAKE_BINARY_DIR} )
+
+################################################################################
+
+if(RS_DEVELOPMENT_BUILD)
+ target_compile_options(${PROJECT_NAME} PRIVATE "-O0")
+endif(RS_DEVELOPMENT_BUILD)
+
+################################################################################
+
+set(LIBRETROSHARE_DEVEL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../libretroshare/")
+if(EXISTS "${LIBRETROSHARE_DEVEL_DIR}/.git" )
+ message(
+ STATUS
+ "libretroshare submodule found at ${LIBRETROSHARE_DEVEL_DIR} using it" )
+ add_subdirectory(
+ "${LIBRETROSHARE_DEVEL_DIR}" "${CMAKE_BINARY_DIR}/libretroshare" )
+else()
+ FetchContent_Declare(
+ libretroshare
+ GIT_REPOSITORY "https://gitlab.com/RetroShare/libretroshare.git"
+ GIT_TAG "origin/master"
+ GIT_SHALLOW TRUE
+ GIT_PROGRESS TRUE
+ TIMEOUT 10
+ )
+ FetchContent_MakeAvailable(libretroshare)
+endif()
+
+target_include_directories(${PROJECT_NAME} PRIVATE ${LIBRETROSHARE_DEVEL_DIR}/src/)
+
+################################################################################
+
+if(RS_SERVICE_DESKTOP)
+ if(UNIX AND NOT APPLE)
+ install(
+ FILES data/retroshare.svg
+ DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/ )
+
+ install(
+ FILES data/retroshare.xpm
+ DESTINATION ${CMAKE_INSTALL_PREFIX}/share/pixmaps/ )
+
+ install(
+ FILES data/24x24/apps/retroshare.png
+ DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/24x24/apps/retroshare.png )
+
+ install(
+ FILES data/48x48/apps/retroshare.png
+ DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/48x48/apps/retroshare.png )
+
+ install(
+ FILES data/64x64/apps/retroshare.png
+ DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/64x64/apps/retroshare.png )
+
+ install(
+ FILES data/128x128/apps/retroshare.png
+ DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/128x128/apps/retroshare.png )
+
+ install(
+ FILES data/retroshare.desktop
+ DESTINATION ${CMAKE_INSTALL_PREFIX}/data/retroshare.desktop )
+
+ install(
+ FILES gui/qss/chat/Bubble gui/qss/chat/Bubble_Compact
+ DESTINATION ${CMAKE_INSTALL_PREFIX}/data/stylesheets/ )
+
+ install(
+ FILES src/sounds/ src/qss/
+ DESTINATION ${CMAKE_INSTALL_PREFIX}/ )
+ endif(UNIX AND NOT APPLE)
+endif(RS_SERVICE_DESKTOP)
+
+################################################################################
+
+if(RS_JSON_API)
+ target_compile_definitions(${PROJECT_NAME} PUBLIC RS_JSONAPI)
+endif(RS_JSON_API)
+
+################################# CMark ########################################
+
+if(RS_GUI_CMARK)
+ set(CMARK_DEVEL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../supportlibs/cmark/")
+ if(EXISTS "${LIBRETROSHARE_DEVEL_DIR}/.git" )
+ message( STATUS "cmark submodule found at ${LIBRETROSHARE_DEVEL_DIR} using it" )
+ add_subdirectory( "${LIBRETROSHARE_DEVEL_DIR}" "${CMAKE_BINARY_DIR}/cmark" )
+ else()
+ FetchContent_Declare(
+ cmark
+ GIT_REPOSITORY "https://github.com/commonmark/cmark.git"
+ GIT_TAG "origin/master"
+ GIT_SHALLOW TRUE
+ GIT_PROGRESS TRUE
+ TIMEOUT 10
+ )
+ FetchContent_MakeAvailable(cmark)
+ endif()
+endif(RS_GUI_CMARK)
+
+################################# LibSam v3 ####################################
+
+set(SAM3_DEVEL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../supportlibs/libsam3/")
+if(EXISTS "${SAM3_DEVEL_DIR}/.git" )
+ message( STATUS "libsam3 submodule found at ${SAM3_DEVEL_DIR} using it" )
+ add_subdirectory( "${SAM3_DEVEL_DIR}" "${CMAKE_BINARY_DIR}/libsam3" )
+else()
+ FetchContent_Declare(
+ libsam3
+ GIT_REPOSITORY "https://github.com/i2p/libsam3.git"
+ GIT_TAG "origin/master"
+ GIT_SHALLOW TRUE
+ GIT_PROGRESS TRUE
+ TIMEOUT 10
+ )
+ FetchContent_MakeAvailable(libsam3)
+endif()
+
+################################################################################
+# TODO #
+################################################################################
+
+# # Auto detect installed version of cmark
+# rs_gui_cmark {
+# DEFINES *= USE_CMARK
+# no_rs_cross_compiling {
+# message("Using compiled cmark")
+# CMARK_SRC_PATH=$$clean_path($${RS_SRC_PATH}/supportlibs/cmark)
+# CMARK_BUILD_PATH=$$clean_path($${RS_BUILD_PATH}/supportlibs/cmark/build)
+# INCLUDEPATH *= $$clean_path($${CMARK_SRC_PATH}/src/)
+# DEPENDPATH *= $$clean_path($${CMARK_SRC_PATH}/src/)
+# QMAKE_LIBDIR *= $$clean_path($${CMARK_BUILD_PATH}/)
+# # Using sLibs would fail as libcmark.a is generated at compile-time
+# LIBS *= -L$$clean_path($${CMARK_BUILD_PATH}/src/) -lcmark
+#
+# DUMMYCMARKINPUT = FORCE
+# CMAKE_GENERATOR_OVERRIDE=""
+# 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)
+# gencmarklib.CONFIG += target_predeps combine
+# gencmarklib.variable_out = PRE_TARGETDEPS
+# gencmarklib.commands = \
+# cd $${RS_SRC_PATH} && ( \
+# git submodule update --init supportlibs/cmark ; \
+# cd $${CMARK_SRC_PATH} ; \
+# true ) && \
+# 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. \
+# -H$$shell_path($${CMARK_SRC_PATH}) && \
+# $(MAKE)
+# QMAKE_EXTRA_COMPILERS += gencmarklib
+# } else {
+# message("Using systems cmark")
+# sLibs *= libcmark
+# }
+# }
+
+################################# Linux ##########################################
+# Put lib dir in QMAKE_LFLAGS so it appears before -L/usr/lib
+
+if(UNIX)
+ find_package(PkgConfig REQUIRED)
+
+ pkg_check_modules(X11 REQUIRED x11)
+ pkg_check_modules(XSCRNSAVER REQUIRED xscrnsaver)
+
+ list( APPEND RS_LINK_LIBRARIES ${X11_LIBRARIES} )
+ list( APPEND RS_LINK_LIBRARIES ${XSCRNSAVER_LIBRARIES} )
+
+ target_include_directories(${PROJECT_NAME} PRIVATE ${X11_INCLUDE_DIRS})
+ target_compile_options(${PROJECT_NAME} PRIVATE ${X11_CFLAGS_OTHER})
+
+ target_include_directories(retroshare-gui PRIVATE ${XSCRNSAVER_INCLUDE_DIRS})
+ target_compile_options(retroshare-gui PRIVATE ${XSCRNSAVER_CFLAGS_OTHER})
+
+ target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_XSS)
+ target_compile_definitions(${PROJECT_NAME} PRIVATE _FILE_OFFSET_BITS=64)
+ target_link_options(${PROJECT_NAME} PRIVATE LINKER:-rdynamic)
+endif(UNIX)
+
+if(RS_SANITIZE)
+ list( APPEND RS_LINK_LIBRARIES asan )
+ list( APPEND RS_LINK_LIBRARIES ubsan )
+endif(RS_SANITIZE)
+
+# #################### Cross compilation for windows under Linux ###################
+#
+# win32-x-g++ {
+# OBJECTS_DIR = temp/win32-x-g++/obj
+#
+# LIBS += ../../../../lib/win32-x-g++-v0.5/libssl.a
+# LIBS += ../../../../lib/win32-x-g++-v0.5/libcrypto.a
+# LIBS += ../../../../lib/win32-x-g++-v0.5/libgpgme.dll.a
+# LIBS += ../../../../lib/win32-x-g++-v0.5/libminiupnpc.a
+# LIBS += ../../../../lib/win32-x-g++-v0.5/libz.a
+# LIBS += -L${HOME}/.wine/drive_c/pthreads/lib -lpthreadGCE2
+# LIBS += -lQtUiTools
+# LIBS += -lws2_32 -luuid -lole32 -liphlpapi -lcrypt32 -gdi32
+# LIBS += -lole32 -lwinmm
+#
+# DEFINES *= WINDOWS_SYS WIN32 WIN32_CROSS_UBUNTU
+#
+# INCLUDEPATH += ../../../../gpgme-1.1.8/src/
+# INCLUDEPATH += ../../../../libgpg-error-1.7/src/
+#
+# RC_FILE = gui/images/retroshare_win.rc
+# }
+#
+# #################################### Windows #####################################
+#
+# win32-g++|win32-clang-g++ {
+# CONFIG(debug, debug|release) {
+# # show console output
+# CONFIG += console
+# } else {
+# CONFIG -= console
+# }
+#
+# CONFIG(debug, debug|release) {
+# } else {
+# # Tell linker to use ASLR protection
+# QMAKE_LFLAGS += -Wl,-dynamicbase
+# # Tell linker to use DEP protection
+# QMAKE_LFLAGS += -Wl,-nxcompat
+# }
+#
+# # Fix linking error (ld.exe: Error: export ordinal too large) due to too
+# # many exported symbols.
+# !libretroshare_shared:QMAKE_LFLAGS+=-Wl,--exclude-libs,ALL
+#
+# # Switch off optimization for release version
+# QMAKE_CXXFLAGS_RELEASE -= -O2
+# QMAKE_CXXFLAGS_RELEASE += -O0
+# QMAKE_CFLAGS_RELEASE -= -O2
+# QMAKE_CFLAGS_RELEASE += -O0
+#
+# # Switch on optimization for debug version
+# #QMAKE_CXXFLAGS_DEBUG += -O2
+# #QMAKE_CFLAGS_DEBUG += -O2
+#
+# OBJECTS_DIR = temp/obj
+#
+# dLib = ws2_32 gdi32 uuid ole32 iphlpapi crypt32 winmm
+# LIBS *= $$linkDynamicLibs(dLib)
+#
+# RC_FILE = gui/images/retroshare_win.rc
+#
+# # export symbols for the plugins
+# LIBS += -Wl,--export-all-symbols,--out-implib,lib/libretroshare-gui.a
+#
+# # create lib directory
+# isEmpty(QMAKE_SH) {
+# QMAKE_PRE_LINK = $(CHK_DIR_EXISTS) lib $(MKDIR) lib
+# } else {
+# QMAKE_PRE_LINK = $(CHK_DIR_EXISTS) lib || $(MKDIR) lib
+# }
+#
+# greaterThan(QT_MAJOR_VERSION, 4) {
+# # Qt 5
+# RC_INCLUDEPATH += $$_PRO_FILE_PWD_/../../libretroshare/src
+# } else {
+# # Qt 4
+# QMAKE_RC += --include-dir=$$_PRO_FILE_PWD_/../../libretroshare/src
+# }
+# }
+#
+# ##################################### MacOS ######################################
+#
+# macx {
+# # ENABLE THIS OPTION FOR Univeral Binary BUILD.
+# #CONFIG += ppc x86
+# #QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.4
+# QMAKE_INFO_PLIST = Info.plist
+# mac_icon.files = $$files($$PWD/rsMacIcon.icns)
+# mac_icon.path = Contents/Resources
+# QMAKE_BUNDLE_DATA += mac_icon
+# dplQSS.files = $$PWD/qss
+# dplQSS.path = Contents/Resources
+# QMAKE_BUNDLE_DATA += dplQSS
+# dplChatStyles.files = \
+# $$PWD/gui/qss/chat/Bubble \
+# $$PWD/gui/qss/chat/Bubble_Compact
+# dplChatStyles.path = Contents/Resources/stylesheets
+# QMAKE_BUNDLE_DATA += dplChatStyles
+# # mac_webui.files = $$files($$PWD/../../libresapi/src/webui)
+# # mac_webui.path = Contents/Resources
+# # QMAKE_BUNDLE_DATA += mac_webui
+#
+# OBJECTS_DIR = temp/obj
+#
+# CONFIG += version_detail_bash_script
+# LIBS += -lssl -lcrypto -lz
+# for(lib, LIB_DIR):exists($$lib/libminiupnpc.a){ LIBS += $$lib/libminiupnpc.a}
+# LIBS += -framework CoreFoundation
+# LIBS += -framework Security
+# LIBS += -framework Carbon
+#
+# for(lib, LIB_DIR):LIBS += -L"$$lib"
+# for(bin, BIN_DIR):LIBS += -L"$$bin"
+#
+# DEPENDPATH += . $$INC_DIR
+# INCLUDEPATH += . $$INC_DIR
+#
+# #DEFINES *= MAC_IDLE # for idle feature
+# CONFIG -= uitools
+# }
+#
+# ##################################### FreeBSD ######################################
+#
+# freebsd-* {
+# INCLUDEPATH *= /usr/local/include/gpgme
+# LIBS *= -lssl
+# LIBS *= -lgpgme
+# LIBS *= -lupnp
+# LIBS *= -lgnome-keyring
+#
+# LIBS += -lsqlite3
+# }
+#
+# ##################################### Haiku ######################################
+#
+# haiku-* {
+# PRE_TARGETDEPS *= ../../libretroshare/src/lib/libretroshare.a
+# PRE_TARGETDEPS *= ../../openpgpsdk/src/lib/libops.a
+#
+# LIBS *= ../../libretroshare/src/lib/libretroshare.a
+# LIBS *= ../../openpgpsdk/src/lib/libops.a -lbz2 -lbsd
+# LIBS *= -lssl -lcrypto -lnetwork
+# LIBS *= -lgpgme
+# LIBS *= -lupnp
+# LIBS *= -lz
+# LIBS *= -lixml
+#
+# LIBS += ../../supportlibs/pegmarkdown/lib/libpegmarkdown.a
+# LIBS += -lsqlite3
+# }
+#
+# ##################################### OpenBSD ######################################
+#
+# openbsd-* {
+# INCLUDEPATH *= /usr/local/include
+#
+# LIBS *= -lssl -lcrypto
+# LIBS *= -lgpgme
+# LIBS *= -lupnp
+# LIBS *= -lgnome-keyring
+# LIBS += -lsqlite3
+# 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
+# }
+
+################################ GENERAL #######################################
+
+target_link_libraries(${PROJECT_NAME} PRIVATE ${RS_LINK_LIBRARIES})
+
+target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/)
+
+set( CMAKE_CXX_FLAGS "-Wno-deprecated-declarations" )
+target_compile_definitions(${PROJECT_NAME} PUBLIC RS_NO_WARN_DEPRECATED )
+target_compile_definitions(${PROJECT_NAME} PRIVATE RS_RELEASE_VERSION )
+target_compile_definitions(${PROJECT_NAME} PRIVATE TARGET=\"retroshare\")
+
+if(RS_GXS_CIRCLES)
+ target_compile_definitions(${PROJECT_NAME} PRIVATE RS_USE_CIRCLES )
+endif(RS_GXS_CIRCLES)
+
+#add_dependencies(${PROJECT_NAME} libretroshare)
+
diff --git a/retroshare-gui/src/CMakeLists.txt b/retroshare-gui/src/CMakeLists.txt
new file mode 100644
index 000000000..b936552ed
--- /dev/null
+++ b/retroshare-gui/src/CMakeLists.txt
@@ -0,0 +1,1279 @@
+################################################################################
+# uselibresapi.pri #
+# Copyright (C) 2018, 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 . #
+################################################################################
+
+list(
+ APPEND RS_GUI_SOURCES
+ src/TorControl/TorControlWindow.cpp
+
+ src/main.cpp
+ src/rshare.cpp
+
+ src/gui/notifyqt.cpp
+ src/gui/AboutDialog.cpp
+ src/gui/AboutWidget.cpp
+ src/gui/QuickStartWizard.cpp
+ src/gui/StartDialog.cpp
+ src/gui/HomePage.cpp
+ src/gui/ChatLobbyWidget.cpp
+ src/gui/GetStartedDialog.cpp
+ src/gui/GenCertDialog.cpp
+ src/gui/NetworkDialog.cpp
+ src/gui/mainpagestack.cpp
+ src/gui/MainWindow.cpp
+ src/gui/NetworkView.cpp
+ src/gui/FriendsDialog.cpp
+ src/gui/ServicePermissionDialog.cpp
+ src/gui/RemoteDirModel.cpp
+ src/gui/RsAutoUpdatePage.cpp
+ src/gui/RetroShareLink.cpp
+ src/gui/SearchTreeWidget.cpp
+ src/gui/ShareManager.cpp
+ # src/gui/ShareDialog.cpp
+ src/gui/NewsFeed.cpp
+ src/gui/MainPage.cpp
+ src/gui/HelpDialog.cpp
+ src/gui/LogoBar.cpp
+ src/gui/SoundManager.cpp
+
+ src/gui/im_history/ImHistoryBrowser.cpp
+ src/gui/im_history/IMHistoryItemDelegate.cpp
+ src/gui/im_history/IMHistoryItemPainter.cpp
+
+ src/gui/help/browser/helpbrowser.cpp
+ src/gui/help/browser/helptextbrowser.cpp
+
+ src/gui/FileTransfer/SearchDialog.cpp
+ src/gui/FileTransfer/SharedFilesDialog.cpp
+ src/gui/FileTransfer/TransfersDialog.cpp
+ src/gui/FileTransfer/FileTransferInfoWidget.cpp
+ src/gui/FileTransfer/DLListDelegate.cpp
+ src/gui/FileTransfer/ULListDelegate.cpp
+ src/gui/FileTransfer/xprogressbar.cpp
+ src/gui/FileTransfer/DetailsDialog.cpp
+ src/gui/FileTransfer/TransferUserNotify.cpp
+ src/gui/FileTransfer/BannedFilesDialog.cpp
+
+ src/lang/languagesupport.cpp
+
+ src/util/RsProtectedTimer.cpp
+ src/util/stringutil.cpp
+ src/util/RsNetUtil.cpp
+ src/util/DateTime.cpp
+ src/util/RetroStyleLabel.cpp
+ src/util/WidgetBackgroundImage.cpp
+ src/util/PixmapMerging.cpp
+ src/util/MouseEventFilter.cpp
+ src/util/EventFilter.cpp
+ src/util/Widget.cpp
+ src/util/RsAction.cpp
+ src/util/printpreview.cpp
+ src/util/log.cpp
+ src/util/misc.cpp
+ src/util/HandleRichText.cpp
+ src/util/ObjectPainter.cpp
+ src/util/RsFile.cpp
+ src/util/RichTextEdit.cpp
+ src/util/ClickableLabel.cpp
+ src/util/AspectRatioPixmapLabel.cpp
+
+ src/gui/profile/ProfileWidget.cpp
+ src/gui/profile/StatusMessage.cpp
+ src/gui/profile/ProfileManager.cpp
+
+ src/gui/chat/PopupChatWindow.cpp
+ src/gui/chat/PopupChatDialog.cpp
+ src/gui/chat/PopupDistantChatDialog.cpp
+ src/gui/chat/ChatTabWidget.cpp
+ src/gui/chat/ChatWidget.cpp
+ src/gui/chat/ChatDialog.cpp
+ src/gui/chat/ChatLobbyDialog.cpp
+ src/gui/chat/CreateLobbyDialog.cpp
+ src/gui/chat/ChatStyle.cpp
+ src/gui/chat/ChatUserNotify.cpp
+ src/gui/chat/ChatLobbyUserNotify.cpp
+
+ src/gui/connect/ConfCertDialog.cpp
+ src/gui/connect/PGPKeyDialog.cpp
+ src/gui/connect/ConnectFriendWizard.cpp
+ src/gui/connect/ConnectProgressDialog.cpp
+ src/gui/connect/FriendRecommendDialog.cpp
+
+ src/gui/msgs/MessagesDialog.cpp
+ src/gui/msgs/MessageComposer.cpp
+ src/gui/msgs/MessageWidget.cpp
+ src/gui/msgs/MessageWindow.cpp
+ src/gui/msgs/MessageModel.cpp
+ src/gui/msgs/TagsMenu.cpp
+ src/gui/msgs/MessageUserNotify.cpp
+
+ src/gui/common/RsButtonOnText.cpp
+ src/gui/common/RSGraphWidget.cpp
+ src/gui/common/ElidedLabel.cpp
+ src/gui/common/vmessagebox.cpp
+ src/gui/common/RsCollectionDialog.cpp
+ src/gui/common/RsCollection.cpp
+ src/gui/common/RsUrlHandler.cpp
+ src/gui/common/rwindow.cpp
+ src/gui/common/rshtml.cpp
+ src/gui/common/AvatarDefs.cpp
+ src/gui/common/AvatarDialog.cpp
+ src/gui/common/GroupFlagsWidget.cpp
+ src/gui/common/GroupSelectionBox.cpp
+ src/gui/common/GroupChooser.cpp
+ src/gui/common/StatusDefs.cpp
+ src/gui/common/TagDefs.cpp
+ src/gui/common/GroupDefs.cpp
+ src/gui/common/Emoticons.cpp
+ src/gui/common/RSComboBox.cpp
+ src/gui/common/RSListWidgetItem.cpp
+ src/gui/common/RSTextEdit.cpp
+ src/gui/common/RSPlainTextEdit.cpp
+ src/gui/common/RSTreeWidget.cpp
+ src/gui/common/RSTreeWidgetItem.cpp
+ src/gui/common/RSFeedWidget.cpp
+ src/gui/common/RSTabWidget.cpp
+ src/gui/common/RSElidedItemDelegate.cpp
+ src/gui/common/RSItemDelegate.cpp
+ src/gui/common/PeerDefs.cpp
+ src/gui/common/FilesDefs.cpp
+ src/gui/common/PopularityDefs.cpp
+ src/gui/common/RsBanListDefs.cpp
+ src/gui/common/GroupTreeWidget.cpp
+ src/gui/common/RSTreeView.cpp
+ src/gui/common/AvatarWidget.cpp
+ src/gui/common/FriendListModel.cpp
+ src/gui/common/NewFriendList.cpp
+ src/gui/common/FriendSelectionWidget.cpp
+ src/gui/common/FriendSelectionDialog.cpp
+ src/gui/common/HashBox.cpp
+ src/gui/common/LineEditClear.cpp
+ src/gui/common/DropLineEdit.cpp
+ src/gui/common/RSTextBrowser.cpp
+ src/gui/common/RSImageBlockWidget.cpp
+ src/gui/common/FeedNotify.cpp
+ src/gui/common/UserNotify.cpp
+ src/gui/common/HeaderFrame.cpp
+ src/gui/common/MimeTextEdit.cpp
+ src/gui/common/UIStateHelper.cpp
+ src/gui/common/FloatingHelpBrowser.cpp
+ src/gui/common/SubscribeToolButton.cpp
+ src/gui/common/RsBanListToolButton.cpp
+ src/gui/common/FlowLayout.cpp
+ src/gui/common/PictureFlow.cpp
+ src/gui/common/ToasterNotify.cpp
+
+ src/gui/style/RSStyle.cpp
+ src/gui/style/StyleDialog.cpp
+
+ src/gui/settings/RSPermissionMatrixWidget.cpp
+ src/gui/settings/rsharesettings.cpp
+ src/gui/settings/RsharePeerSettings.cpp
+ src/gui/settings/rsettings.cpp
+ src/gui/settings/rsettingswin.cpp
+ src/gui/settings/GeneralPage.cpp
+ src/gui/settings/AboutPage.cpp
+ src/gui/settings/ServerPage.cpp
+ src/gui/settings/NotifyPage.cpp
+ src/gui/settings/CryptoPage.cpp
+ src/gui/settings/PeoplePage.cpp
+ src/gui/settings/MessagePage.cpp
+ src/gui/settings/NewTag.cpp
+ src/gui/settings/ForumPage.cpp
+ src/gui/settings/PluginsPage.cpp
+ src/gui/settings/PluginItem.cpp
+ src/gui/settings/AppearancePage.cpp
+ src/gui/settings/FileAssociationsPage.cpp
+ src/gui/settings/SoundPage.cpp
+ src/gui/settings/TransferPage.cpp
+ src/gui/settings/ChatPage.cpp
+ src/gui/settings/ChannelPage.cpp
+ src/gui/settings/PostedPage.cpp
+ src/gui/settings/ServicePermissionsPage.cpp
+ src/gui/settings/AddFileAssociationDialog.cpp
+ src/gui/settings/GroupFrameSettingsWidget.cpp
+
+ src/gui/statusbar/peerstatus.cpp
+ src/gui/statusbar/natstatus.cpp
+ src/gui/statusbar/dhtstatus.cpp
+ src/gui/statusbar/torstatus.cpp
+ src/gui/statusbar/ratesstatus.cpp
+ src/gui/statusbar/hashingstatus.cpp
+ src/gui/statusbar/discstatus.cpp
+ src/gui/statusbar/SoundStatus.cpp
+ src/gui/statusbar/OpModeStatus.cpp
+ src/gui/statusbar/ToasterDisable.cpp
+ src/gui/statusbar/SysTrayStatus.cpp
+
+ src/gui/toaster/ToasterItem.cpp
+ src/gui/toaster/MessageToaster.cpp
+ src/gui/toaster/DownloadToaster.cpp
+ src/gui/toaster/OnlineToaster.cpp
+ src/gui/toaster/ChatToaster.cpp
+ src/gui/toaster/GroupChatToaster.cpp
+ src/gui/toaster/ChatLobbyToaster.cpp
+ src/gui/toaster/FriendRequestToaster.cpp
+
+ src/gui/advsearch/advancedsearchdialog.cpp
+ src/gui/advsearch/expressionwidget.cpp
+ src/gui/advsearch/guiexprelement.cpp
+
+ src/gui/elastic/graphwidget.cpp
+ src/gui/elastic/edge.cpp
+ src/gui/elastic/arrow.cpp
+ src/gui/elastic/elnode.cpp
+
+ src/gui/feeds/BoardsCommentsItem.cpp
+ src/gui/feeds/FeedItem.cpp
+ src/gui/feeds/FeedHolder.cpp
+ src/gui/feeds/GxsCircleItem.cpp
+ src/gui/feeds/ChannelsCommentsItem.cpp
+ src/gui/feeds/PeerItem.cpp
+ src/gui/feeds/MsgItem.cpp
+ src/gui/feeds/ChatMsgItem.cpp
+ src/gui/feeds/SubFileItem.cpp
+ src/gui/feeds/AttachFileItem.cpp
+ src/gui/feeds/SecurityItem.cpp
+ src/gui/feeds/SecurityIpItem.cpp
+ src/gui/feeds/NewsFeedUserNotify.cpp
+
+ src/gui/groups/CreateGroup.cpp
+
+ src/gui/statistics/BandwidthGraphWindow.cpp
+ src/gui/statistics/BandwidthStatsWidget.cpp
+ src/gui/statistics/DhtWindow.cpp
+ src/gui/statistics/Histogram.cpp
+ src/gui/statistics/TurtleRouterDialog.cpp
+ src/gui/statistics/TurtleRouterStatistics.cpp
+ src/gui/statistics/GxsIdStatistics.cpp
+ src/gui/statistics/GlobalRouterStatistics.cpp
+ src/gui/statistics/GxsTransportStatistics.cpp
+ src/gui/statistics/StatisticsWindow.cpp
+ src/gui/statistics/BwCtrlWindow.cpp
+ src/gui/statistics/RttStatistics.cpp
+ src/gui/statistics/BWGraph.cpp
+
+ src/util/RsSyntaxHighlighter.cpp
+ src/util/imageutil.cpp
+ src/util/retroshareWin32.cpp
+
+ src/gui/NetworkDialog/pgpid_item_model.cpp
+ src/gui/NetworkDialog/pgpid_item_proxy.cpp
+ )
+
+list(
+ APPEND RS_GUI_FORMS
+ src/TorControl/TorControlWindow.ui
+
+ src/gui/StartDialog.ui
+ src/gui/HomePage.ui
+ src/gui/GetStartedDialog.ui
+ src/gui/GenCertDialog.ui
+ src/gui/AboutDialog.ui
+ src/gui/AboutWidget.ui
+ src/gui/QuickStartWizard.ui
+ src/gui/NetworkDialog.ui
+ src/gui/common/AvatarDialog.ui
+ src/gui/MainWindow.ui
+ src/gui/NetworkView.ui
+ src/gui/FriendsDialog.ui
+ src/gui/NewsFeed.ui
+ src/gui/ShareManager.ui
+ src/gui/help/browser/helpbrowser.ui
+ src/gui/HelpDialog.ui
+ src/gui/ServicePermissionDialog.ui
+ src/gui/ChatLobbyWidget.ui
+
+ src/gui/FileTransfer/TransfersDialog.ui
+ src/gui/FileTransfer/DetailsDialog.ui
+ src/gui/FileTransfer/SearchDialog.ui
+ src/gui/FileTransfer/SharedFilesDialog.ui
+ src/gui/FileTransfer/BannedFilesDialog.ui
+
+ src/gui/profile/ProfileWidget.ui
+ src/gui/profile/StatusMessage.ui
+ src/gui/profile/ProfileManager.ui
+
+ src/gui/chat/PopupChatWindow.ui
+ src/gui/chat/PopupChatDialog.ui
+ src/gui/chat/ChatTabWidget.ui
+ src/gui/chat/ChatWidget.ui
+ src/gui/chat/ChatLobbyDialog.ui
+ src/gui/chat/CreateLobbyDialog.ui
+
+ src/gui/connect/ConfCertDialog.ui
+ src/gui/connect/PGPKeyDialog.ui
+ src/gui/connect/ConnectFriendWizard.ui
+ src/gui/connect/ConnectProgressDialog.ui
+ src/gui/connect/FriendRecommendDialog.ui
+
+ src/gui/msgs/MessagesDialog.ui
+ src/gui/msgs/MessageComposer.ui
+ src/gui/msgs/MessageWindow.ui
+ src/gui/msgs/MessageWidget.ui
+
+ src/gui/settings/settingsw.ui
+ src/gui/settings/GeneralPage.ui
+ src/gui/settings/ServerPage.ui
+ src/gui/settings/NotifyPage.ui
+ src/gui/settings/PeoplePage.ui
+ src/gui/settings/CryptoPage.ui
+ src/gui/settings/MessagePage.ui
+ src/gui/settings/NewTag.ui
+ src/gui/settings/ForumPage.ui
+ src/gui/settings/AboutPage.ui
+ src/gui/settings/PluginsPage.ui
+ src/gui/settings/AppearancePage.ui
+ src/gui/settings/TransferPage.ui
+ src/gui/settings/SoundPage.ui
+ src/gui/settings/ChatPage.ui
+ src/gui/settings/ChannelPage.ui
+ src/gui/settings/PostedPage.ui
+ src/gui/settings/ServicePermissionsPage.ui
+ src/gui/settings/PluginItem.ui
+ src/gui/settings/GroupFrameSettingsWidget.ui
+
+ src/gui/toaster/MessageToaster.ui
+ src/gui/toaster/OnlineToaster.ui
+ src/gui/toaster/DownloadToaster.ui
+ src/gui/toaster/ChatToaster.ui
+ src/gui/toaster/GroupChatToaster.ui
+ src/gui/toaster/ChatLobbyToaster.ui
+ src/gui/toaster/FriendRequestToaster.ui
+
+ src/gui/advsearch/AdvancedSearchDialog.ui
+ src/gui/advsearch/expressionwidget.ui
+
+ src/gui/feeds/BoardsCommentsItem.ui
+ src/gui/feeds/GxsCircleItem.ui
+ src/gui/feeds/ChannelsCommentsItem.ui
+ src/gui/feeds/PeerItem.ui
+ src/gui/feeds/MsgItem.ui
+ src/gui/feeds/ChatMsgItem.ui
+ src/gui/feeds/SubFileItem.ui
+ src/gui/feeds/AttachFileItem.ui
+ src/gui/feeds/SecurityItem.ui
+ src/gui/feeds/SecurityIpItem.ui
+
+ src/gui/im_history/ImHistoryBrowser.ui
+
+ src/gui/groups/CreateGroup.ui
+
+ src/gui/common/GroupTreeWidget.ui
+ src/gui/common/AvatarWidget.ui
+ src/gui/common/NewFriendList.ui
+ src/gui/common/FriendSelectionWidget.ui
+ src/gui/common/HashBox.ui
+ src/gui/common/RSImageBlockWidget.ui
+ src/gui/common/RsCollectionDialog.ui
+ src/gui/common/HeaderFrame.ui
+ src/gui/common/RSFeedWidget.ui
+
+ src/gui/style/StyleDialog.ui
+
+ src/gui/statistics/BandwidthGraphWindow.ui
+ src/gui/statistics/BandwidthStatsWidget.ui
+ src/gui/statistics/DhtWindow.ui
+ src/gui/statistics/TurtleRouterDialog.ui
+ src/gui/statistics/TurtleRouterStatistics.ui
+ src/gui/statistics/GxsIdStatistics.ui
+ src/gui/statistics/GlobalRouterStatistics.ui
+ src/gui/statistics/GxsTransportStatistics.ui
+ src/gui/statistics/StatisticsWindow.ui
+ src/gui/statistics/BwCtrlWindow.ui
+ src/gui/statistics/RttStatistics.ui
+
+ src/util/RichTextEdit.ui
+ )
+
+list(
+ APPEND RS_IMPLEMENTATION_HEADERS
+
+ src/TorControl/TorControlWindow.h
+
+ src/chat/distantchat.h
+ src/chat/distributedchat.h
+ src/chat/p3chatservice.h
+ src/chat/rschatitems.h
+
+ src/rshare.h
+ src/retroshare-gui/configpage.h
+ src/retroshare-gui/RsAutoUpdatePage.h
+ src/retroshare-gui/mainpage.h
+
+ src/control/bandwidthevent.h
+ src/control/eventtype.h
+
+ src/gui/QuickStartWizard.h
+ src/gui/notifyqt.h
+ src/gui/GetStartedDialog.h
+ src/gui/StartDialog.h
+ src/gui/HomePage.h
+ src/gui/NetworkDialog.h
+ src/gui/GenCertDialog.h
+ src/gui/linetypes.h
+ src/gui/mainpagestack.h
+ src/gui/MainWindow.h
+ src/gui/RSHumanReadableDelegate.h
+ src/gui/AboutDialog.h
+ src/gui/AboutWidget.h
+ src/gui/NetworkView.h
+ src/gui/FriendsDialog.h
+ src/gui/ServicePermissionDialog.h
+ src/gui/RemoteDirModel.h
+ src/gui/RetroShareLink.h
+ src/gui/SearchTreeWidget.h
+ src/gui/ShareManager.h
+ src/gui/NewsFeed.h
+ src/gui/ShareDialog.h
+ src/gui/SFListDelegate.h
+ src/gui/SoundManager.h
+ src/gui/HelpDialog.h
+ src/gui/LogoBar.h
+
+ src/gui/common/AvatarDialog.h
+
+ src/gui/NetworkDialog/pgpid_item_model.h
+ src/gui/NetworkDialog/pgpid_item_proxy.h
+
+ src/gui/FileTransfer/SearchDialog.h
+ src/gui/FileTransfer/SharedFilesDialog.h
+ src/gui/FileTransfer/xprogressbar.h
+ src/gui/FileTransfer/DetailsDialog.h
+ src/gui/FileTransfer/FileTransferInfoWidget.h
+ src/gui/FileTransfer/DLListDelegate.h
+ src/gui/FileTransfer/ULListDelegate.h
+ src/gui/FileTransfer/TransfersDialog.h
+ src/gui/FileTransfer/BannedFilesDialog.h
+ src/gui/FileTransfer/TransferUserNotify.h
+
+ src/gui/statistics/TurtleRouterDialog.h
+ src/gui/statistics/TurtleRouterStatistics.h
+ src/gui/statistics/GxsIdStatistics.h
+ src/gui/statistics/dhtgraph.h
+ src/gui/statistics/Histogram.h
+ src/gui/statistics/BandwidthGraphWindow.h
+ src/gui/statistics/turtlegraph.h
+ src/gui/statistics/BandwidthStatsWidget.h
+ src/gui/statistics/DhtWindow.h
+ src/gui/statistics/GlobalRouterStatistics.h
+ src/gui/statistics/GxsTransportStatistics.h
+ src/gui/statistics/StatisticsWindow.h
+ src/gui/statistics/BwCtrlWindow.h
+ src/gui/statistics/BWGraph.h
+ src/gui/statistics/RttStatistics.h
+
+ src/gui/plugins/PluginInterface.h
+
+ src/gui/im_history/ImHistoryBrowser.h
+ src/gui/im_history/IMHistoryItemDelegate.h
+ src/gui/im_history/IMHistoryItemPainter.h
+
+ src/lang/languagesupport.h
+
+ src/util/RsSyntaxHighlighter.h
+ src/util/imageutil.h
+ src/util/RichTextEdit.h
+ src/util/retroshareWin32.h
+ src/util/RsProtectedTimer.h
+ src/util/stringutil.h
+ src/util/RsNetUtil.h
+ src/util/DateTime.h
+ src/util/RetroStyleLabel.h
+ src/util/dllexport.h
+ src/util/NonCopyable.h
+ src/util/rsutildll.h
+ src/util/dllexport.h
+ src/util/global.h
+ src/util/rsqtutildll.h
+ src/util/Interface.h
+ src/util/PixmapMerging.h
+ src/util/MouseEventFilter.h
+ src/util/EventFilter.h
+ src/util/Widget.h
+ src/util/RsAction.h
+ src/util/RsUserdata.h
+ src/util/printpreview.h
+ src/util/log.h
+ src/util/misc.h
+ src/util/HandleRichText.h
+ src/util/ObjectPainter.h
+ src/util/QtVersion.h
+ src/util/RsFile.h
+ src/util/qtthreadsutils.h
+ src/util/ClickableLabel.h
+ src/util/AspectRatioPixmapLabel.h
+
+ src/gui/profile/ProfileWidget.h
+ src/gui/profile/ProfileManager.h
+ src/gui/profile/StatusMessage.h
+
+ src/gui/chat/PopupChatWindow.h
+ src/gui/chat/PopupChatDialog.h
+ src/gui/chat/PopupDistantChatDialog.h
+ src/gui/chat/ChatTabWidget.h
+ src/gui/chat/ChatWidget.h
+ src/gui/chat/ChatDialog.h
+ src/gui/ChatLobbyWidget.h
+ src/gui/chat/ChatLobbyDialog.h
+ src/gui/chat/CreateLobbyDialog.h
+ src/gui/chat/ChatStyle.h
+ src/gui/chat/ChatUserNotify.h
+ src/gui/chat/ChatLobbyUserNotify.h
+
+ src/gui/connect/ConfCertDialog.h
+ src/gui/connect/PGPKeyDialog.h
+ src/gui/connect/FriendRecommendDialog.h
+
+ src/gui/msgs/MessagesDialog.h
+ src/gui/msgs/MessageInterface.h
+ src/gui/msgs/MessageComposer.h
+ src/gui/msgs/MessageWindow.h
+ src/gui/msgs/MessageWidget.h
+ src/gui/msgs/MessageModel.h
+ src/gui/msgs/TagsMenu.h
+ src/gui/msgs/textformat.h
+ src/gui/msgs/MessageUserNotify.h
+
+ src/gui/images/retroshare_win.rc.h
+
+ src/gui/settings/RSPermissionMatrixWidget.h
+ src/gui/settings/rsharesettings.h
+ src/gui/settings/RsharePeerSettings.h
+ src/gui/settings/rsettings.h
+ src/gui/settings/rsettingswin.h
+ src/gui/settings/GeneralPage.h
+ src/gui/settings/PeoplePage.h
+ src/gui/settings/AboutPage.h
+ src/gui/settings/ServerPage.h
+ src/gui/settings/NotifyPage.h
+ src/gui/settings/CryptoPage.h
+ src/gui/settings/MessagePage.h
+ src/gui/settings/NewTag.h
+ src/gui/settings/ForumPage.h
+ src/gui/settings/PluginsPage.h
+ src/gui/settings/PluginItem.h
+ src/gui/settings/AppearancePage.h
+ src/gui/settings/FileAssociationsPage.h
+ src/gui/settings/SoundPage.h
+ src/gui/settings/TransferPage.h
+ src/gui/settings/ChatPage.h
+ src/gui/settings/ChannelPage.h
+ src/gui/settings/PostedPage.h
+ src/gui/settings/ServicePermissionsPage.h
+ src/gui/settings/AddFileAssociationDialog.h
+ src/gui/settings/GroupFrameSettingsWidget.h
+
+ src/gui/toaster/ToasterItem.h
+ src/gui/toaster/MessageToaster.h
+ src/gui/toaster/OnlineToaster.h
+ src/gui/toaster/DownloadToaster.h
+ src/gui/toaster/ChatToaster.h
+ src/gui/toaster/GroupChatToaster.h
+ src/gui/toaster/ChatLobbyToaster.h
+ src/gui/toaster/FriendRequestToaster.h
+
+ src/gui/common/RsButtonOnText.h
+ src/gui/common/RsCollection.h
+ src/gui/common/RSGraphWidget.h
+ src/gui/common/ElidedLabel.h
+ src/gui/common/vmessagebox.h
+ src/gui/common/RsUrlHandler.h
+ src/gui/common/RsCollectionDialog.h
+ src/gui/common/rwindow.h
+ src/gui/common/rshtml.h
+ src/gui/common/AvatarDefs.h
+ src/gui/common/GroupFlagsWidget.h
+ src/gui/common/GroupSelectionBox.h
+ src/gui/common/GroupChooser.h
+ src/gui/common/StatusDefs.h
+ src/gui/common/TagDefs.h
+ src/gui/common/GroupDefs.h
+ src/gui/common/Emoticons.h
+ src/gui/common/RSComboBox.h
+ src/gui/common/RSListWidgetItem.h
+ src/gui/common/RSTextEdit.h
+ src/gui/common/RSPlainTextEdit.h
+ src/gui/common/RSTreeWidget.h
+ src/gui/common/RSTreeWidgetItem.h
+ src/gui/common/RSFeedWidget.h
+ src/gui/common/RSTabWidget.h
+ src/gui/common/RSElidedItemDelegate.h
+ src/gui/common/RSItemDelegate.h
+ src/gui/common/PeerDefs.h
+ src/gui/common/FilesDefs.h
+ src/gui/common/PopularityDefs.h
+ src/gui/common/RsBanListDefs.h
+ src/gui/common/GroupTreeWidget.h
+ src/gui/common/RSTreeView.h
+ src/gui/common/AvatarWidget.h
+ src/gui/common/FriendListModel.h
+ src/gui/common/NewFriendList.h
+ src/gui/common/FriendSelectionWidget.h
+ src/gui/common/FriendSelectionDialog.h
+ src/gui/common/HashBox.h
+ src/gui/common/LineEditClear.h
+ src/gui/common/DropLineEdit.h
+ src/gui/common/RSTextBrowser.h
+ src/gui/common/RSImageBlockWidget.h
+ src/gui/common/FeedNotify.h
+ src/gui/common/UserNotify.h
+ src/gui/common/HeaderFrame.h
+ src/gui/common/MimeTextEdit.h
+ src/gui/common/UIStateHelper.h
+ src/gui/common/FloatingHelpBrowser.h
+ src/gui/common/SubscribeToolButton.h
+ src/gui/common/RsBanListToolButton.h
+ src/gui/common/FlowLayout.h
+ src/gui/common/PictureFlow.h
+ src/gui/common/ToasterNotify.h
+
+ src/gui/style/RSStyle.h
+ src/gui/style/StyleDialog.h
+
+ src/gui/help/browser/helpbrowser.h
+ src/gui/help/browser/helptextbrowser.h
+
+ src/gui/statusbar/peerstatus.h
+ src/gui/statusbar/natstatus.h
+ src/gui/statusbar/dhtstatus.h
+ src/gui/statusbar/torstatus.h
+ src/gui/statusbar/ratesstatus.h
+ src/gui/statusbar/hashingstatus.h
+ src/gui/statusbar/discstatus.h
+ src/gui/statusbar/SoundStatus.h
+ src/gui/statusbar/OpModeStatus.h
+ src/gui/statusbar/ToasterDisable.h
+ src/gui/statusbar/SysTrayStatus.h
+
+ src/gui/advsearch/advancedsearchdialog.h
+ src/gui/advsearch/expressionwidget.h
+ src/gui/advsearch/guiexprelement.h
+
+ src/gui/elastic/graphwidget.h
+ src/gui/elastic/edge.h
+ src/gui/elastic/arrow.h
+ src/gui/elastic/elnode.h
+
+ src/gui/feeds/BoardsCommentsItem.h
+ src/gui/feeds/FeedItem.h
+ src/gui/feeds/FeedHolder.h
+ src/gui/feeds/GxsCircleItem.h
+ src/gui/feeds/ChannelsCommentsItem.h
+ src/gui/feeds/PeerItem.h
+ src/gui/feeds/MsgItem.h
+ src/gui/feeds/ChatMsgItem.h
+ src/gui/feeds/SubFileItem.h
+ src/gui/feeds/AttachFileItem.h
+ src/gui/feeds/SecurityItem.h
+ src/gui/feeds/SecurityIpItem.h
+ src/gui/feeds/NewsFeedUserNotify.h
+
+ src/gui/connect/ConnectFriendWizard.h
+ src/gui/connect/ConnectProgressDialog.h
+
+ src/gui/groups/CreateGroup.h
+ )
+
+if(RS_JSON_API)
+ list(
+ APPEND RS_GUI_SOURCES
+ src/gui/settings/JsonApiPage.cc
+ )
+
+ list(
+ APPEND RS_IMPLEMENTATION_HEADERS
+ src/gui/settings/JsonApiPage.h
+ )
+
+ list(
+ APPEND RS_GUI_FORMS
+ src/gui/settings/JsonApiPage.ui
+ )
+endif(RS_JSON_API)
+
+if(RS_WEBUI)
+ list(
+ APPEND RS_GUI_SOURCES
+ src/gui/settings/WebuiPage.cpp
+ )
+
+ list(
+ APPEND RS_IMPLEMENTATION_HEADERS
+ src/jsonapi/p3webui.h
+ src/gui/settings/WebuiPage.h
+ )
+
+ list(
+ APPEND RS_GUI_FORMS
+ src/gui/settings/WebuiPage.ui
+ )
+endif(RS_WEBUI)
+
+list(
+ APPEND RS_GUI_QTRESOURCES
+ src/gui/images.qrc
+ src/gui/icons.qrc
+ src/lang/lang.qrc
+ src/gui/help/content/content.qrc
+ src/gui/emojione.qrc
+ src/gui/qss/stylesheet/qdarkstyle/dark/Standard_Dark.qrc
+ src/gui/qss/stylesheet/qdarkstyle/light/Standard_Light.qrc
+ )
+
+list(
+ APPEND RS_TRANSLATIONS
+ src/lang/retroshare_ca_ES.ts
+ src/lang/retroshare_cs.ts
+ src/lang/retroshare_da.ts
+ src/lang/retroshare_de.ts
+ src/lang/retroshare_el.ts
+ src/lang/retroshare_en.ts
+ src/lang/retroshare_es.ts
+ src/lang/retroshare_fi.ts
+ src/lang/retroshare_fr.ts
+ src/lang/retroshare_hu.ts
+ src/lang/retroshare_it.ts
+ src/lang/retroshare_ja_JP.ts
+ src/lang/retroshare_nl.ts
+ src/lang/retroshare_ko.ts
+ src/lang/retroshare_pl.ts
+ src/lang/retroshare_ru.ts
+ src/lang/retroshare_sv.ts
+ src/lang/retroshare_tr.ts
+ src/lang/retroshare_zh_CN.ts
+ )
+
+if(RS_UNFINISHED_TRANSLATIONS)
+ list(
+ APPEND RS_TRANSLATIONS
+ src/lang/retroshare_bg.ts
+ src/lang/retroshare_af.ts
+ src/lang/retroshare_pt.ts
+ src/lang/retroshare_sl.ts
+ src/lang/retroshare_sr.ts
+ src/lang/retroshare_zh_TW.ts
+ )
+endif(RS_UNFINISHED_TRANSLATIONS)
+
+if(RS_MESSENGER)
+
+ target_compile_definitions( ${PROJECT_NAME} PUBLIC MESSENGER_WINDOW )
+
+ list(
+ APPEND RS_GUI_SOURCES
+ src/gui/MessengerWindow.cpp
+ src/gui/common/FriendList.cpp
+ )
+ list(
+ APPEND RS_IMPLEMENTATION_HEADERS
+ src/gui/MessengerWindow.h
+ src/gui/common/FriendList.h
+ )
+ list(
+ APPEND RS_GUI_FORMS
+ src/gui/MessengerWindow.ui
+ src/gui/common/FriendList.ui
+ )
+endif(RS_MESSENGER)
+
+if(RS_IDLE)
+ list(
+ APPEND RS_IMPLEMENTATION_HEADERS
+ src/idle/idle.h
+ )
+ list(
+ APPEND RS_GUI_SOURCES
+ src/idle/idle.cpp
+ src/idle/idle_platform.cpp
+ )
+endif(RS_IDLE)
+
+if(RS_FRAMECATCHER)
+ list(
+ APPEND RS_IMPLEMENTATION_HEADERS
+ src/util/framecatcher.h
+ )
+ list(
+ APPEND RS_GUI_SOURCES
+ src/util/framecatcher.cpp
+ )
+
+ # TODO: LIBS += -lxine
+
+ target_compile_definitions(
+ ${PROJECT_NAME} PUBLIC CHANNELS_FRAME_CATCHER
+ )
+endif(RS_FRAMECATCHER)
+
+# Embedded Friend Server
+
+if(RS_EFS)
+ target_compile_definitions(
+ ${PROJECT_NAME} PUBLIC RS_EMBEDED_FRIEND_SERVER
+ )
+ list(
+ APPEND RS_GUI_SOURCES
+ src/gui/FriendServerControl.cpp
+ )
+ list(
+ APPEND RS_IMPLEMENTATION_HEADERS
+ src/gui/FriendServerControl.h
+ )
+ list(
+ APPEND RS_GUI_FORMS
+ src/gui/FriendServerControl.ui
+ )
+endif(RS_EFS)
+
+# BELOW IS GXS Unfinished Services.
+
+if(RS_UNFINISHED)
+
+ list(
+ APPEND RS_IMPLEMENTATION_HEADERS
+ src/gui/unfinished/ApplicationWindow.h
+
+ # gui/unfinished/CalDialog.h
+ # gui/unfinished/ExampleDialog.h
+ # gui/unfinished/GamesDialog.h
+ # gui/unfinished/profile/ProfileView.h
+ # gui/unfinished/profile/ProfileEdit.h
+ # gui/unfinished/StatisticDialog.h
+ # gui/unfinished/PhotoDialog.h
+ # gui/unfinished/PhotoShow.h
+ )
+
+ list(
+ APPEND RS_GUI_FORMS
+ src/gui/unfinished/ApplicationWindow.ui
+
+ # gui/unfinished/CalDialog.ui
+ # gui/unfinished/ExampleDialog.ui
+ # gui/unfinished/GamesDialog.ui
+ # gui/unfinished/profile/ProfileView.ui
+ # gui/unfinished/profile/ProfileEdit.ui
+ # gui/unfinished/StatisticDialog.ui
+ # gui/unfinished/PhotoDialog.ui
+ # gui/unfinished/PhotoShow.ui
+ )
+
+ list(
+ APPEND RS_GUI_SOURCES
+ src/gui/unfinished/ApplicationWindow.cpp
+
+ # gui/unfinished/CalDialog.cpp
+ # gui/unfinished/ExampleDialog.cpp
+ # gui/unfinished/GamesDialog.cpp
+ # gui/unfinished/profile/ProfileView.cpp
+ # gui/unfinished/profile/ProfileEdit.cpp
+ # gui/unfinished/StatisticDialog.cpp
+ # gui/unfinished/PhotoDialog.cpp
+ # gui/unfinished/PhotoShow.cpp
+ )
+
+ target_compile_definitions(
+ ${PROJECT_NAME} PUBLIC UNFINISHED
+ )
+endif(RS_UNFINISHED)
+
+if(RS_GXSPHOTOSHARE)
+ target_compile_definitions(
+ ${PROJECT_NAME} PUBLIC RS_USE_PHOTO
+ )
+
+ list(
+ APPEND RS_IMPLEMENTATION_HEADERS
+ src/gui/PhotoShare/AlbumGroupDialog.h
+ src/gui/PhotoShare/AlbumExtra.h
+ src/gui/PhotoShare/PhotoDrop.h
+ src/gui/PhotoShare/AlbumItem.h
+ src/gui/PhotoShare/AlbumDialog.h
+ src/gui/PhotoShare/PhotoItem.h
+ src/gui/PhotoShare/PhotoShareItemHolder.h
+ src/gui/PhotoShare/PhotoShare.h
+ src/gui/PhotoShare/PhotoSlideShow.h
+ src/gui/PhotoShare/PhotoDialog.h
+ )
+ list(
+ APPEND RS_GUI_FORMS
+ src/gui/PhotoShare/AlbumExtra.ui
+ src/gui/PhotoShare/PhotoItem.ui
+ src/gui/PhotoShare/PhotoDialog.ui
+ src/gui/PhotoShare/AlbumItem.ui
+ src/gui/PhotoShare/AlbumDialog.ui
+ src/gui/PhotoShare/PhotoShare.ui
+ src/gui/PhotoShare/PhotoSlideShow.ui
+ )
+
+ list(
+ APPEND RS_GUI_SOURCES
+ src/gui/PhotoShare/AlbumGroupDialog.cpp
+ src/gui/PhotoShare/AlbumExtra.cpp
+ src/gui/PhotoShare/PhotoItem.cpp
+ src/gui/PhotoShare/PhotoDialog.cpp
+ src/gui/PhotoShare/PhotoDrop.cpp
+ src/gui/PhotoShare/AlbumItem.cpp
+ src/gui/PhotoShare/AlbumDialog.cpp
+ src/gui/PhotoShare/PhotoShareItemHolder.cpp
+ src/gui/PhotoShare/PhotoShare.cpp
+ src/gui/PhotoShare/PhotoSlideShow.cpp
+ )
+
+ list(
+ APPEND RS_GUI_QTRESOURCES
+ src/gui/PhotoShare/Photo_images.qrc
+ )
+endif(RS_GXSPHOTOSHARE)
+
+if(RS_GXSWIKIPOS)
+ target_compile_definitions(
+ ${PROJECT_NAME} PUBLIC RS_USE_WIKI
+ )
+
+ target_include_directories(${PROJECT_NAME} PUBLIC ../../supportlibs/pegmarkdown)
+
+ list(
+ APPEND RS_IMPLEMENTATION_HEADERS
+ src/gui/WikiPoos/WikiDialog.h
+ src/gui/WikiPoos/WikiAddDialog.h
+ src/gui/WikiPoos/WikiEditDialog.h
+ src/gui/gxs/WikiGroupDialog.h
+ src/gui/gxs/RsGxsUpdateBroadcastBase.h
+ src/gui/gxs/RsGxsUpdateBroadcastWidget.h
+ src/gui/gxs/RsGxsUpdateBroadcastPage.h
+ )
+
+ list(
+ APPEND RS_GUI_FORMS
+ src/gui/WikiPoos/WikiDialog.ui
+ src/gui/WikiPoos/WikiAddDialog.ui
+ src/gui/WikiPoos/WikiEditDialog.ui
+ )
+
+ list(
+ APPEND RS_GUI_SOURCES
+ src/gui/WikiPoos/WikiDialog.cpp
+ src/gui/WikiPoos/WikiAddDialog.cpp
+ src/gui/WikiPoos/WikiEditDialog.cpp
+ src/gui/gxs/WikiGroupDialog.cpp
+ src/gui/gxs/RsGxsUpdateBroadcastBase.cpp
+ src/gui/gxs/RsGxsUpdateBroadcastWidget.cpp
+ src/gui/gxs/RsGxsUpdateBroadcastPage.cpp
+ )
+
+ list(
+ APPEND RS_GUI_QTRESOURCES
+ src/gui/WikiPoos/Wiki_images.qrc
+ )
+endif(RS_GXSWIKIPOS)
+
+if(RS_GXSTHEWIRE)
+ target_compile_definitions(
+ ${PROJECT_NAME} PUBLIC RS_USE_WIRE
+ )
+
+ list(
+ APPEND RS_IMPLEMENTATION_HEADERS
+ src/gui/TheWire/WireDialog.h
+ src/gui/TheWire/WireGroupItem.h
+ src/gui/TheWire/WireGroupDialog.h
+ src/gui/TheWire/WireGroupExtra.h
+ src/gui/TheWire/PulseAddDialog.h
+ src/gui/TheWire/PulseViewItem.h
+ src/gui/TheWire/PulseTopLevel.h
+ src/gui/TheWire/PulseViewGroup.h
+ src/gui/TheWire/PulseReply.h
+ src/gui/TheWire/PulseReplySeperator.h
+ src/gui/TheWire/PulseMessage.h
+ )
+
+ list(
+ APPEND RS_GUI_FORMS
+ src/gui/TheWire/WireDialog.ui
+ src/gui/TheWire/WireGroupItem.ui
+ src/gui/TheWire/WireGroupExtra.ui
+ src/gui/TheWire/PulseAddDialog.ui
+ src/gui/TheWire/PulseTopLevel.ui
+ src/gui/TheWire/PulseViewGroup.ui
+ src/gui/TheWire/PulseReply.ui
+ src/gui/TheWire/PulseReplySeperator.ui
+ src/gui/TheWire/PulseMessage.ui
+ )
+
+ list(
+ APPEND RS_GUI_SOURCES
+ src/gui/TheWire/WireDialog.cpp
+ src/gui/TheWire/WireGroupItem.cpp
+ src/gui/TheWire/WireGroupDialog.cpp
+ src/gui/TheWire/WireGroupExtra.cpp
+ src/gui/TheWire/PulseAddDialog.cpp
+ src/gui/TheWire/PulseViewItem.cpp
+ src/gui/TheWire/PulseTopLevel.cpp
+ src/gui/TheWire/PulseViewGroup.cpp
+ src/gui/TheWire/PulseReply.cpp
+ src/gui/TheWire/PulseReplySeperator.cpp
+ src/gui/TheWire/PulseMessage.cpp
+ )
+
+ list(
+ APPEND RS_GUI_QTRESOURCES
+ RESOURCES += src/gui/TheWire/TheWire_images.qrc
+ )
+endif(RS_GXSTHEWIRE)
+
+if(RS_GXSIDENTITIES)
+ list(
+ APPEND RS_IMPLEMENTATION_HEADERS
+ src/gui/Identity/IdDialog.h
+ src/gui/Identity/IdEditDialog.h
+ src/gui/Identity/IdDetailsDialog.h
+ )
+
+ list(
+ APPEND RS_GUI_FORMS
+ src/gui/Identity/IdDialog.ui
+ src/gui/Identity/IdEditDialog.ui
+ src/gui/Identity/IdDetailsDialog.ui
+ )
+
+ list(
+ APPEND RS_GUI_SOURCES
+ src/gui/Identity/IdDialog.cpp
+ src/gui/Identity/IdEditDialog.cpp
+ src/gui/Identity/IdDetailsDialog.cpp
+ )
+endif(RS_GXSIDENTITIES)
+
+if(RS_GXSCIRCLES)
+ list(
+ APPEND RS_IMPLEMENTATION_HEADERS
+ src/gui/Circles/CirclesDialog.h
+ src/gui/Circles/CreateCircleDialog.h
+ src/gui/People/PeopleDialog.h
+ src/gui/People/CircleWidget.h
+ src/gui/People/IdentityWidget.h
+ )
+
+ list(
+ APPEND RS_GUI_FORMS
+ src/gui/Circles/CirclesDialog.ui
+ src/gui/Circles/CreateCircleDialog.ui
+ src/gui/People/PeopleDialog.ui
+ src/gui/People/CircleWidget.ui
+ src/gui/People/IdentityWidget.ui
+ )
+
+ list(
+ APPEND RS_GUI_SOURCES
+ src/gui/Circles/CirclesDialog.cpp
+ src/gui/Circles/CreateCircleDialog.cpp
+ src/gui/People/PeopleDialog.cpp
+ src/gui/People/CircleWidget.cpp
+ src/gui/People/IdentityWidget.cpp
+ )
+endif(RS_GXSCIRCLES)
+
+if(RS_GXSGUI)
+ list(
+ APPEND RS_IMPLEMENTATION_HEADERS
+ src/gui/gxs/GxsGroupDialog.h
+ src/gui/gxs/GxsIdDetails.h
+ src/gui/gxs/GxsIdChooser.h
+ src/gui/gxs/GxsIdLabel.h
+ src/gui/gxs/GxsCircleChooser.h
+ src/gui/gxs/GxsCircleLabel.h
+ src/gui/gxs/GxsIdTreeWidgetItem.h
+ src/gui/gxs/GxsCommentTreeWidget.h
+ src/gui/gxs/GxsCommentContainer.h
+ src/gui/gxs/GxsCommentDialog.h
+ src/gui/gxs/GxsCreateCommentDialog.h
+ src/gui/gxs/GxsGroupFrameDialog.h
+ src/gui/gxs/GxsMessageFrameWidget.h
+ src/gui/gxs/GxsMessageFramePostWidget.h
+ src/gui/gxs/GxsGroupFeedItem.h
+ src/gui/gxs/GxsFeedItem.h
+ src/gui/gxs/GxsGroupShareKey.h
+ src/gui/gxs/GxsUserNotify.h
+ src/gui/gxs/GxsFeedWidget.h
+ src/util/TokenQueue.h
+ src/util/RsGxsUpdateBroadcast.h
+ )
+
+ list(
+ APPEND RS_GUI_FORMS
+ src/gui/gxs/GxsGroupDialog.ui
+ src/gui/gxs/GxsCommentContainer.ui
+ src/gui/gxs/GxsCommentDialog.ui
+ src/gui/gxs/GxsCreateCommentDialog.ui
+ src/gui/gxs/GxsGroupFrameDialog.ui
+ src/gui/gxs/GxsGroupShareKey.ui
+ )
+
+ list(
+ APPEND RS_GUI_SOURCES
+ src/gui/gxs/GxsGroupDialog.cpp
+ src/gui/gxs/GxsIdDetails.cpp
+ src/gui/gxs/GxsIdChooser.cpp
+ src/gui/gxs/GxsIdLabel.cpp
+ src/gui/gxs/GxsCircleChooser.cpp
+ src/gui/gxs/GxsGroupShareKey.cpp
+ src/gui/gxs/GxsCircleLabel.cpp
+ src/gui/gxs/GxsIdTreeWidgetItem.cpp
+ src/gui/gxs/GxsCommentTreeWidget.cpp
+ src/gui/gxs/GxsCommentContainer.cpp
+ src/gui/gxs/GxsCommentDialog.cpp
+ src/gui/gxs/GxsCreateCommentDialog.cpp
+ src/gui/gxs/GxsGroupFrameDialog.cpp
+ src/gui/gxs/GxsMessageFrameWidget.cpp
+ src/gui/gxs/GxsMessageFramePostWidget.cpp
+ src/gui/gxs/GxsGroupFeedItem.cpp
+ src/gui/gxs/GxsFeedItem.cpp
+ src/gui/gxs/GxsUserNotify.cpp
+ src/gui/gxs/GxsFeedWidget.cpp
+ src/util/TokenQueue.cpp
+ src/util/RsGxsUpdateBroadcast.cpp
+ )
+endif(RS_GXSGUI)
+
+if(RS_GXSFORUMS)
+ list(
+ APPEND RS_IMPLEMENTATION_HEADERS
+ src/gui/gxsforums/GxsForumsDialog.h
+ src/gui/gxsforums/GxsForumGroupDialog.h
+ src/gui/gxsforums/CreateGxsForumMsg.h
+ src/gui/gxsforums/GxsForumThreadWidget.h
+ src/gui/gxsforums/GxsForumModel.h
+ src/gui/gxsforums/GxsForumUserNotify.h
+ src/gui/feeds/GxsForumGroupItem.h
+ src/gui/feeds/GxsForumMsgItem.h
+ )
+
+ list(
+ APPEND RS_GUI_FORMS
+ src/gui/gxsforums/CreateGxsForumMsg.ui
+ src/gui/gxsforums/GxsForumThreadWidget.ui
+ src/gui/feeds/GxsForumGroupItem.ui
+ src/gui/feeds/GxsForumMsgItem.ui
+ )
+
+ list(
+ APPEND RS_GUI_SOURCES
+ src/gui/gxsforums/GxsForumsDialog.cpp
+ src/gui/gxsforums/GxsForumGroupDialog.cpp
+ src/gui/gxsforums/CreateGxsForumMsg.cpp
+ src/gui/gxsforums/GxsForumThreadWidget.cpp
+ src/gui/gxsforums/GxsForumModel.cpp
+ src/gui/gxsforums/GxsForumUserNotify.cpp
+ src/gui/feeds/GxsForumGroupItem.cpp
+ src/gui/feeds/GxsForumMsgItem.cpp
+ )
+endif(RS_GXSFORUMS)
+
+
+if(RS_GXSCHANNELS)
+ list(
+ APPEND RS_IMPLEMENTATION_HEADERS
+ src/gui/gxschannels/GxsChannelDialog.h
+ src/gui/gxschannels/GxsChannelGroupDialog.h
+ src/gui/gxschannels/CreateGxsChannelMsg.h
+ src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h
+ src/gui/gxschannels/GxsChannelPostsModel.h
+ src/gui/gxschannels/GxsChannelPostFilesModel.h
+ src/gui/gxschannels/GxsChannelPostThumbnail.h
+ src/gui/gxschannels/GxsChannelFilesStatusWidget.h
+ src/gui/feeds/GxsChannelGroupItem.h
+ src/gui/feeds/GxsChannelPostItem.h
+ src/gui/gxschannels/GxsChannelUserNotify.h
+ )
+
+ list(
+ APPEND RS_GUI_FORMS
+ src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui
+ src/gui/gxschannels/GxsChannelFilesStatusWidget.ui
+ src/gui/gxschannels/CreateGxsChannelMsg.ui
+ src/gui/feeds/GxsChannelGroupItem.ui
+ src/gui/feeds/GxsChannelPostItem.ui
+ )
+
+ list(
+ APPEND RS_GUI_SOURCES
+ src/gui/gxschannels/GxsChannelDialog.cpp
+ src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp
+ src/gui/gxschannels/GxsChannelPostsModel.cpp
+ src/gui/gxschannels/GxsChannelPostFilesModel.cpp
+ src/gui/gxschannels/GxsChannelFilesStatusWidget.cpp
+ src/gui/gxschannels/GxsChannelPostThumbnail.cpp
+ src/gui/gxschannels/GxsChannelGroupDialog.cpp
+ src/gui/gxschannels/CreateGxsChannelMsg.cpp
+ src/gui/feeds/GxsChannelGroupItem.cpp
+ src/gui/feeds/GxsChannelPostItem.cpp
+ src/gui/gxschannels/GxsChannelUserNotify.cpp
+ )
+endif(RS_GXSCHANNELS)
+
+if(RS_GXSPOSTED)
+ list(
+ APPEND RS_IMPLEMENTATION_HEADERS
+ src/gui/Posted/PostedDialog.h
+ src/gui/Posted/PostedListWidgetWithModel.h
+ src/gui/Posted/PostedPostsModel.h
+ src/gui/Posted/BoardPostDisplayWidget.h
+ src/gui/Posted/PostedItem.h
+ src/gui/Posted/PostedCardView.h
+ src/gui/Posted/PostedGroupDialog.h
+ src/gui/feeds/PostedGroupItem.h
+ src/gui/Posted/PostedCreatePostDialog.h
+ src/gui/Posted/PhotoView.h
+ src/gui/Posted/PostedUserNotify.h
+ )
+
+ list(
+ APPEND RS_GUI_FORMS
+ src/gui/Posted/PostedListWidgetWithModel.ui
+ src/gui/feeds/PostedGroupItem.ui
+ src/gui/Posted/BoardPostDisplayWidget_compact.ui
+ src/gui/Posted/BoardPostDisplayWidget_card.ui
+ src/gui/Posted/PostedItem.ui
+ src/gui/Posted/PostedCardView.ui
+ src/gui/Posted/PostedCreatePostDialog.ui
+ src/gui/Posted/PhotoView.ui
+ )
+
+ list(
+ APPEND RS_GUI_SOURCES
+ src/gui/Posted/PostedDialog.cpp
+ src/gui/Posted/PostedListWidgetWithModel.cpp
+ src/gui/Posted/BoardPostDisplayWidget.cpp
+ src/gui/Posted/PostedPostsModel.cpp
+ src/gui/feeds/PostedGroupItem.cpp
+ src/gui/Posted/PostedItem.cpp
+ src/gui/Posted/PostedCardView.cpp
+ src/gui/Posted/PostedGroupDialog.cpp
+ src/gui/Posted/PostedCreatePostDialog.cpp
+ src/gui/Posted/PhotoView.cpp
+ src/gui/Posted/PostedUserNotify.cpp
+ )
+
+ list(
+ APPEND RS_GUI_QTRESOURCES
+ src/gui/Posted/Posted_images.qrc
+ )
+endif(RS_GXSPOSTED)
+
+
+
diff --git a/retroshare-gui/src/README.txt b/retroshare-gui/src/README.txt
index 7f7f35c8e..231d16b23 100644
--- a/retroshare-gui/src/README.txt
+++ b/retroshare-gui/src/README.txt
@@ -2,17 +2,14 @@
README for RetroShare
=======================================================================================
-RetroShare web site . . . . http://retroshare.net/index.html
+RetroShare web site . . . . https://retroshare.cc/
Developer's blog . . . . . https://retroshareteam.wordpress.com
-Documentation . . . . . . . https://retroshare.readthedocs.io/en/latest/
-Support . . . . . . . . . . http://retroshare.net/support.html
-Forums . . . . . . . . . . http://retroshare.sourceforge.net/forum/
+Documentation . . . . . . . https://retrosharedocs.readthedocs.io/en/latest/
Wiki . . . . . . . . . . . https://github.com/RetroShare/documentation/wiki
-Old developers site . . . . http://retroshare.sourceforge.net/wiki/index.php/Developers_Corner
Project site . . . . . . . https://github.com/RetroShare/RetroShare
-Relted projects/plugins . . https://github.com/RetroShare
+Related projects/plugins . .https://github.com/RetroShare
-Contact: . . . . . . . . . retroshare@lunamutt.com ,defnax@users.sourceforge.net
+Contact: . . . . . . . . . retroshare.project@gmail.com
=========================================================================================
Compiling RetroShare
@@ -22,9 +19,9 @@ Build Scripts are avaible on GIT:
https://github.com/RetroShare/RetroShare/tree/master/build_scripts
You can find here instructions howto compile RetroShare:
-https://retroshare.readthedocs.io/en/latest/developer/compilation/
+https://retrosharedocs.readthedocs.io/en/latest/developer/compilation/
-You can go on over to our forum or chat lobby when you have trouble with compiling:
+You can go on over to our forum or chat room when you have trouble with compiling:
retroshare://forum?name=Developers%20Discussions&id=8fd22bd8f99754461e7ba1ca8a727995
retroshare://chat_room?name=Retroshare%20Devel%20%28signed%29&id=L68DB0A1E09BDA3A5
-http://retroshare.sourceforge.net/forum/
+
diff --git a/retroshare-gui/src/gui/AboutWidget.cpp b/retroshare-gui/src/gui/AboutWidget.cpp
index 7dccb1693..1c4ae6827 100644
--- a/retroshare-gui/src/gui/AboutWidget.cpp
+++ b/retroshare-gui/src/gui/AboutWidget.cpp
@@ -21,7 +21,7 @@
#include "AboutDialog.h"
#include "HelpDialog.h"
-#include "qmake_info.h"
+#include "include/qmake_info.h"
#include "rshare.h"
#ifdef RS_JSONAPI
@@ -142,7 +142,7 @@ void AboutWidget::updateTitle()
{
if (tWidget == NULL)
{
- setWindowTitle(QString("%1 %2").arg(tr("About RetroShare"), Rshare::retroshareVersion(true)));
+ setWindowTitle(QString("%1 %2").arg(tr("About RetroShare"), RsApplication::retroshareVersion(true)));
}
else
{
@@ -228,7 +228,7 @@ void AWidget::initImages()
#ifdef RS_ONLYHIDDENNODE
p.drawText(QPointF(10, 50), QString("%1 : %2 (With embedded Tor)").arg(tr("Retroshare version"), Rshare::retroshareVersion(true)));
#else
- p.drawText(QPointF(10, 50), QString("%1 : %2").arg(tr("Retroshare version"), Rshare::retroshareVersion(true)));
+ p.drawText(QPointF(10, 50), QString("%1 : %2").arg(tr("Retroshare version"), RsApplication::retroshareVersion(true)));
#endif
/* Draw Qt's version number */
@@ -936,7 +936,7 @@ void AboutWidget::on_copy_button_clicked()
{
QString verInfo;
QString rsVerString = "RetroShare Version: ";
- rsVerString+=Rshare::retroshareVersion(true);
+ rsVerString+=RsApplication::retroshareVersion(true);
verInfo+=rsVerString;
#ifdef RS_ONLYHIDDENNODE
verInfo+=" " + tr("Only Hidden Node");
diff --git a/retroshare-gui/src/gui/ChatLobbyWidget.cpp b/retroshare-gui/src/gui/ChatLobbyWidget.cpp
index 13fdc4329..9682bc4c5 100644
--- a/retroshare-gui/src/gui/ChatLobbyWidget.cpp
+++ b/retroshare-gui/src/gui/ChatLobbyWidget.cpp
@@ -22,19 +22,20 @@
#include "ChatLobbyWidget.h"
#include "notifyqt.h"
+#include "RetroShareLink.h"
#include "chat/ChatLobbyDialog.h"
#include "chat/ChatLobbyUserNotify.h"
#include "chat/ChatTabWidget.h"
#include "chat/CreateLobbyDialog.h"
+#include "common/FilesDefs.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 "gxs/GxsIdDetails.h"
+#include "Identity/IdEditDialog.h"
+#include "settings/rsharesettings.h"
#include "util/HandleRichText.h"
+#include "util/misc.h"
#include "util/QtVersion.h"
-#include "gui/common/FilesDefs.h"
#include "retroshare/rsmsgs.h"
#include "retroshare/rspeers.h"
@@ -142,28 +143,28 @@ ChatLobbyWidget::ChatLobbyWidget(QWidget *parent, Qt::WindowFlags flags)
QHeaderView_setSectionResizeModeColumn(header, COLUMN_TOPIC, QHeaderView::Interactive);
privateSubLobbyItem = new RSTreeWidgetItem(compareRole, TYPE_FOLDER);
- privateSubLobbyItem->setText(COLUMN_NAME, tr("Private Subscribed chat rooms"));
+ privateSubLobbyItem->setText(COLUMN_NAME, tr("Private Subscribed"));
privateSubLobbyItem->setData(COLUMN_NAME, ROLE_SORT, "1");
// privateLobbyItem->setIcon(COLUMN_NAME, QIcon(IMAGE_PRIVATE));
privateSubLobbyItem->setData(COLUMN_DATA, ROLE_PRIVACYLEVEL, CHAT_LOBBY_PRIVACY_LEVEL_PRIVATE);
ui.lobbyTreeWidget->insertTopLevelItem(0, privateSubLobbyItem);
publicSubLobbyItem = new RSTreeWidgetItem(compareRole, TYPE_FOLDER);
- publicSubLobbyItem->setText(COLUMN_NAME, tr("Public Subscribed chat rooms"));
+ publicSubLobbyItem->setText(COLUMN_NAME, tr("Public Subscribed"));
publicSubLobbyItem->setData(COLUMN_NAME, ROLE_SORT, "2");
// publicLobbyItem->setIcon(COLUMN_NAME, QIcon(IMAGE_PUBLIC));
publicSubLobbyItem->setData(COLUMN_DATA, ROLE_PRIVACYLEVEL, CHAT_LOBBY_PRIVACY_LEVEL_PUBLIC);
ui.lobbyTreeWidget->insertTopLevelItem(1, publicSubLobbyItem);
privateLobbyItem = new RSTreeWidgetItem(compareRole, TYPE_FOLDER);
- privateLobbyItem->setText(COLUMN_NAME, tr("Private chat rooms"));
+ privateLobbyItem->setText(COLUMN_NAME, tr("Private"));
privateLobbyItem->setData(COLUMN_NAME, ROLE_SORT, "3");
// privateLobbyItem->setIcon(COLUMN_NAME, QIcon(IMAGE_PRIVATE));
privateLobbyItem->setData(COLUMN_DATA, ROLE_PRIVACYLEVEL, CHAT_LOBBY_PRIVACY_LEVEL_PRIVATE);
ui.lobbyTreeWidget->insertTopLevelItem(2, privateLobbyItem);
publicLobbyItem = new RSTreeWidgetItem(compareRole, TYPE_FOLDER);
- publicLobbyItem->setText(COLUMN_NAME, tr("Public chat rooms"));
+ publicLobbyItem->setText(COLUMN_NAME, tr("Public"));
publicLobbyItem->setData(COLUMN_NAME, ROLE_SORT, "4");
// publicLobbyItem->setIcon(COLUMN_NAME, QIcon(IMAGE_PUBLIC));
publicLobbyItem->setData(COLUMN_DATA, ROLE_PRIVACYLEVEL, CHAT_LOBBY_PRIVACY_LEVEL_PUBLIC);
@@ -206,28 +207,29 @@ ChatLobbyWidget::ChatLobbyWidget(QWidget *parent, Qt::WindowFlags flags)
// load settings
processSettings(true);
- QString help_str = tr("\
-
Chat Rooms
\
-
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 ). \
- 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 \
- chat lobbies your friends are participating in. You can either \
-
\
-
Right click to create a new chat room
\
-
Double click a chat room to enter, chat, and show it to your friends
\
-
\
- Note: For the chat rooms to work properly, your computer needs be on time. So check your system clock!\
-
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 )."
+ " 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"
+ " chat lobbies your friends are participating in. You can either"
+ "
"
+ "
Right click to create a new chat room
"
+ "
Double click a chat room to enter, chat, and show it to your friends
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.
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.
This configuration panel allows you to specify the onion address of a \
+ friend server. Retroshare will talk to that server anonymously through Tor \
+ and use it to acquire a fixed number of friends.
\
+
The friend server will continue supplying new friends until that number is reached \
+ in particular if you add your own friends manually, the friend server may become useless \
+ and you will save bandwidth disabling it. When disabling it, you will keep existing friends.
\
+
The friend server only knows your peer ID and profile public key. It doesn't know your IP address.
\
+ "
+ ).arg(QString::number(2*H), QString::number(2*H)) ;
+
+ registerHelpButton(helpButton,help_str,"Friend Server") ;
+
mConnectionCheckTimer = new QTimer;
- // init values
+ whileBlocking(autoAccept_CB)->setChecked(rsFriendServer->autoAddFriends());
- torServerFriendsToRequest_SB->setValue(rsFriendServer->friendsToRequest());
- torServerAddress_LE->setText(QString::fromStdString(rsFriendServer->friendsServerAddress().c_str()));
- torServerPort_SB->setValue(rsFriendServer->friendsServerPort());
+ // Init values
- // connect slignals/slots
+ whileBlocking(torServerFriendsToRequest_SB)->setValue(rsFriendServer->friendsToRequest());
+
+ // Connect slignals/slots
QObject::connect(friendServerOnOff_CB,SIGNAL(toggled(bool)),this,SLOT(onOnOffClick(bool)));
QObject::connect(torServerFriendsToRequest_SB,SIGNAL(valueChanged(int)),this,SLOT(onFriendsToRequestChanged(int)));
- QObject::connect(torServerAddress_LE,SIGNAL(textChanged(const QString&)),this,SLOT(onOnionAddressEdit(const QString&)));
+ QObject::connect(torServerAddress_LE,SIGNAL(textEdited(const QString&)),this,SLOT(onOnionAddressEdit(const QString&)));
QObject::connect(torServerPort_SB,SIGNAL(valueChanged(int)),this,SLOT(onOnionPortEdit(int)));
+ QObject::connect(autoAccept_CB,SIGNAL(toggled(bool)),this,SLOT(onAutoAddFriends(bool)));
QObject::connect(mConnectionCheckTimer,SIGNAL(timeout()),this,SLOT(checkServerAddress()));
+ QObject::connect(status_TW, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(launchStatusContextMenu(QPoint)));
mCheckingServerMovie = new QMovie(":/images/loader/circleball-16.gif");
- serverStatusCheckResult_LB->setMovie(mCheckingServerMovie);
+
+ // Do not block signal for these two, since we want to trigger a test to initialise the online status.
+ torServerAddress_LE->setText(QString::fromStdString(rsFriendServer->friendsServerAddress().c_str()));
+ torServerPort_SB->setValue(rsFriendServer->friendsServerPort());
updateFriendServerStatusIcon(false);
- updateTorProxyInfo();
+
+ makeFriend_ACT = new QAction(tr("Make friend")); // makes SSL-only friend with the peer
+
+ QObject::connect(makeFriend_ACT,SIGNAL(triggered()),this,SLOT(makeFriend()));
+
+ mEventHandlerId_fs = 0;
+
+ rsEvents->registerEventsHandler( [this](std::shared_ptr event)
+ {
+ RsQThreadUtils::postToObject([=](){ handleEvent_main_thread(event); }, this );
+ }, mEventHandlerId_fs, RsEventType::FRIEND_SERVER );
+
+ mEventHandlerId_peer = 0;
+
+ rsEvents->registerEventsHandler( [this](std::shared_ptr event)
+ {
+ RsQThreadUtils::postToObject([=](){ handleEvent_main_thread(event); }, this );
+ }, mEventHandlerId_peer, RsEventType::PEER_CONNECTION );
}
-void FriendServerControl::updateTorProxyInfo()
+void FriendServerControl::onAutoAddFriends(bool b)
{
- std::string friend_proxy_address;
- uint16_t friend_proxy_port;
+ rsFriendServer->setAutoAddFriends(b);
+}
+void FriendServerControl::handleEvent_main_thread(std::shared_ptr event)
+{
+ {
+ const RsFriendServerEvent *fe = dynamic_cast(event.get());
- RsTor::getProxyServerInfo(friend_proxy_address,friend_proxy_port);
+ if(fe)
+ switch(fe->mFriendServerEventType)
+ {
+ case RsFriendServerEventCode::PEER_INFO_CHANGED: updateContactsStatus();
+ break;
- torProxyPort_SB->setValue(friend_proxy_port);
- torProxyAddress_LE->setText(QString::fromStdString(friend_proxy_address));
+ case RsFriendServerEventCode::FRIEND_SERVER_STATUS_CHANGED: updateFriendServerStatusIcon(fe->mFriendServerStatus == RsFriendServerStatus::ONLINE);
+ break;
+
+ default:
+ case RsFriendServerEventCode::UNKNOWN: break;
+ }
+ }
+
+ {
+ const RsConnectionEvent *pe = dynamic_cast(event.get());
+
+ if(pe)
+ switch(pe->mConnectionInfoCode)
+ {
+ case RsConnectionEventCode::PEER_ADDED:
+ case RsConnectionEventCode::PEER_REMOVED:
+ case RsConnectionEventCode::PEER_CONNECTED: updateContactsStatus();
+ break;
+
+ default: ;
+ }
+ }
}
FriendServerControl::~FriendServerControl()
{
delete mCheckingServerMovie;
delete mConnectionCheckTimer;
+
+ rsEvents->unregisterEventsHandler(mEventHandlerId_fs);
+ rsEvents->unregisterEventsHandler(mEventHandlerId_peer);
}
+void FriendServerControl::launchStatusContextMenu(QPoint p)
+{
+ RsPeerId peer_id = getCurrentPeer();
+
+ RsPeerDetails det;
+ if(rsPeers->getPeerDetails(peer_id,det) && det.accept_connection)
+ return;
+
+ QMenu contextMnu(this);
+ contextMnu.addAction(makeFriend_ACT);
+
+ contextMnu.exec(QCursor::pos());
+}
void FriendServerControl::onOnOffClick(bool b)
{
if(b)
+ {
+ if(passphrase_LE->text().isNull())
+ {
+ QMessageBox::critical(nullptr,tr("Missing profile passphrase."),tr("Your profile passphrase is missing. Please enter is in the field below before enabling the friend server."));
+ whileBlocking(friendServerOnOff_CB)->setCheckState(Qt::Unchecked);
+ return;
+ }
+ rsFriendServer->setProfilePassphrase(passphrase_LE->text().toStdString());
rsFriendServer->startServer();
+ }
else
rsFriendServer->stopServer();
}
@@ -115,31 +217,51 @@ void FriendServerControl::onOnionPortEdit(int)
void FriendServerControl::onOnionAddressEdit(const QString&)
{
+ while(mCurrentlyCheckingServerAddress)
+ {
+ std::cerr << " waiting for free slot" << std::endl;
+ std::this_thread::sleep_for(std::chrono::milliseconds(500)); // wait for ongoing check to finish.
+ }
+
+ std::cerr << "Resetting connection proxy timer" << std::endl;
+
// Setup timer to auto-check the friend server address
- mConnectionCheckTimer->stop();
mConnectionCheckTimer->setSingleShot(true);
mConnectionCheckTimer->setInterval(5000); // check in 5 secs unless something is changed in the mean time.
-
mConnectionCheckTimer->start();
-
- if(mCheckingServerMovie->fileName() != QString(":/images/loader/circleball-16.gif" ))
- {
- mCheckingServerMovie->setFileName(":/images/loader/circleball-16.gif");
- mCheckingServerMovie->start();
- }
}
void FriendServerControl::checkServerAddress()
{
- rsFriendServer->checkServerAddress_async(torServerAddress_LE->text().toStdString(),torServerPort_SB->value(),5000,
- [this](const std::string& address,uint16_t port,bool test_result)
- {
- if(test_result)
- rsFriendServer->setServerAddress(address,port);
+ std::cerr << "In checkServerAddress() ..." << std::endl;
- RsQThreadUtils::postToObject( [=]() { updateFriendServerStatusIcon(test_result); },this);
- }
- );
+ mCurrentlyCheckingServerAddress = true;
+
+ serverStatusCheckResult_LB->setMovie(mCheckingServerMovie);
+ serverStatusCheckResult_LB->setToolTip(tr("Trying to contact friend server\nThis may take up to 1 min.")) ;
+ mCheckingServerMovie->setFileName(":/images/loader/circleball-16.gif");
+ mCheckingServerMovie->start();
+
+ RsThread::async( [this]()
+ {
+ auto port = torServerPort_SB->value();
+ auto addr = torServerAddress_LE->text().toStdString();
+
+ std::cerr << "calling sync test..." << std::endl;
+ bool succeed = rsFriendServer->checkServerAddress(addr,port,5000);
+ std::cerr << "result is " << succeed << std::endl;
+
+ RsQThreadUtils::postToObject( [addr,port,succeed,this]()
+ {
+ if(succeed)
+ rsFriendServer->setServerAddress(addr,port);
+
+ mCheckingServerMovie->stop();
+ updateFriendServerStatusIcon(succeed);
+ mCurrentlyCheckingServerAddress = false;
+
+ },this);
+ });
}
void FriendServerControl::onNbFriendsToRequestsChanged(int n)
@@ -149,20 +271,92 @@ void FriendServerControl::onNbFriendsToRequestsChanged(int n)
void FriendServerControl::updateFriendServerStatusIcon(bool ok)
{
- mCheckingServerMovie->stop();
+ std::cerr << "updating proxy status icon" << std::endl;
if(ok)
{
- torServerStatus_LB->setToolTip(tr("Friend server is currently reachable.")) ;
- mCheckingServerMovie->setFileName(ICON_STATUS_OK);
+ serverStatusCheckResult_LB->setToolTip(tr("Friend server is currently reachable.")) ;
+ serverStatusCheckResult_LB->setPixmap(QPixmap(ICON_STATUS_OK));
+ friendServerOnOff_CB->setEnabled(true);
+ status_TW->setEnabled(true);
}
else
{
- torServerStatus_LB->setToolTip(tr("The proxy is not enabled or broken.\nAre all services up and running fine??\nAlso check your ports!")) ;
- mCheckingServerMovie->setFileName(ICON_STATUS_UNKNOWN);
+ rsFriendServer->stopServer();
+ serverStatusCheckResult_LB->setToolTip(tr("The proxy is not enabled or broken.\nAre all services up and running fine??\nAlso check your ports!")) ;
+ serverStatusCheckResult_LB->setPixmap(QPixmap(ICON_STATUS_UNKNOWN));
+ friendServerOnOff_CB->setChecked(false);
+ friendServerOnOff_CB->setEnabled(false);
+ status_TW->setEnabled(false);
}
- mCheckingServerMovie->start();
+}
+
+void FriendServerControl::updateContactsStatus()
+{
+ std::map pinfo = rsFriendServer->getPeersInfo();
+
+ status_TW->clear();
+ int row = 0;
+ status_TW->setRowCount(pinfo.size());
+ status_TW->setColumnCount(4);
+
+ status_TW->setHorizontalHeaderItem(NAME_COLUMN,new QTableWidgetItem(QObject::tr("Name")));
+ status_TW->setHorizontalHeaderItem(NODE_COLUMN,new QTableWidgetItem(QObject::tr("Node")));
+ status_TW->setHorizontalHeaderItem(ADDR_COLUMN,new QTableWidgetItem(QObject::tr("Address")));
+ status_TW->setHorizontalHeaderItem(STAT_COLUMN,new QTableWidgetItem(QObject::tr("Status")));
+
+ for(auto it:pinfo)
+ {
+ uint32_t err_code=0;
+ RsPeerDetails details;
+
+ rsPeers->parseShortInvite(it.second.mInvite,details,err_code);
+
+ status_TW->setItem(row,NAME_COLUMN,new QTableWidgetItem(QString::fromStdString(details.name)));
+ status_TW->setItem(row,NODE_COLUMN,new QTableWidgetItem(QString::fromStdString(details.id.toStdString())));
+ status_TW->setItem(row,ADDR_COLUMN,new QTableWidgetItem(QString::fromStdString(details.hiddenNodeAddress)+":"+QString::number(details.hiddenNodePort)));
+
+ QString status_string;
+ if(details.accept_connection)
+ status_string += QString("Friend");
+ else
+ status_string += QString("Not friend");
+
+ status_string += QString(" / ");
+
+ switch(it.second.mPeerLevel)
+ {
+ case RsFriendServer::PeerFriendshipLevel::NO_KEY: status_string += "Doesn't have my key" ; break;
+ case RsFriendServer::PeerFriendshipLevel::HAS_KEY: status_string += "Has my key" ; break;
+ case RsFriendServer::PeerFriendshipLevel::HAS_ACCEPTED_KEY: status_string += "Has friended me" ; break;
+ default:
+ case RsFriendServer::PeerFriendshipLevel::UNKNOWN: status_string += "Unkn" ; break;
+ }
+
+ status_TW->setItem(row,STAT_COLUMN,new QTableWidgetItem(status_string));
+
+ row++;
+ }
+}
+
+RsPeerId FriendServerControl::getCurrentPeer()
+{
+ QTableWidgetItem *item = status_TW->currentItem();
+
+ if(!item)
+ return RsPeerId();
+
+ return RsPeerId(status_TW->item(item->row(),NODE_COLUMN)->text().toStdString());
+}
+void FriendServerControl::makeFriend()
+{
+ RsPeerId peer_id = getCurrentPeer();
+
+ rsFriendServer->allowPeer(peer_id);
}
+
+
+
diff --git a/retroshare-gui/src/gui/FriendServerControl.h b/retroshare-gui/src/gui/FriendServerControl.h
index 7217bcd5d..ddc66ee8a 100644
--- a/retroshare-gui/src/gui/FriendServerControl.h
+++ b/retroshare-gui/src/gui/FriendServerControl.h
@@ -22,9 +22,12 @@
#include
+#include "retroshare/rsevents.h"
+
+#include "retroshare-gui/mainpage.h"
#include "ui_FriendServerControl.h"
-class FriendServerControl : public QWidget, public Ui::FriendServerControl
+class FriendServerControl : public MainPage, public Ui::FriendServerControl
{
Q_OBJECT
@@ -34,15 +37,29 @@ public:
protected slots:
void onOnOffClick(bool b);
- void onOnionAddressEdit(const QString&);
+ void onOnionAddressEdit(const QString &);
void onOnionPortEdit(int);
void onNbFriendsToRequestsChanged(int n);
- void updateTorProxyInfo();
void checkServerAddress();
+ void onAutoAddFriends(bool b);
+ void launchStatusContextMenu(QPoint p);
+ void makeFriend();
private:
+ RsPeerId getCurrentPeer();
+ void updateContactsStatus();
void updateFriendServerStatusIcon(bool ok);
+ void handleEvent_main_thread(std::shared_ptr event);
QTimer *mConnectionCheckTimer;
QMovie *mCheckingServerMovie;
+ bool mCurrentlyCheckingServerAddress;
+
+ RsEventsHandlerId_t mEventHandlerId_fs;
+ RsEventsHandlerId_t mEventHandlerId_peer;
+
+ QAction *makeFriend_ACT;
+ QAction *unmakeFriend_ACT;
+ QAction *removePeer_ACT;
+ QAction *removePeerPermanently_ACT;
};
diff --git a/retroshare-gui/src/gui/FriendServerControl.ui b/retroshare-gui/src/gui/FriendServerControl.ui
index 73a5625d6..e3faac36b 100644
--- a/retroshare-gui/src/gui/FriendServerControl.ui
+++ b/retroshare-gui/src/gui/FriendServerControl.ui
@@ -11,52 +11,8 @@
-
-
-
- On/Off
-
-
-
-
-
-
- Friends to request:
-
-
-
-
-
-
- 1
-
-
- 15
-
-
- 5
-
-
-
-
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
-
-
-
-
@@ -72,13 +28,19 @@
0
-
- .onion
+
+ <html><head/><body><p>Enter here the onion address of the Friend Server that was given to you. The address will be automatically checked after you enter it and a green bullet will appear if the server is online.</p></body></html>
+
+
+ Onion address of the friend server
+
+ <html><head/><body><p>Communication port of the server. You usually get a server address as somestring.onion:port. The port is the number right after ":"</p></body></html>
+ 1025
@@ -98,71 +60,165 @@
-
-
-
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Maximum
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+ Qt::NoFocus
+
+
+
+ :/icons/help_64.png:/icons/help_64.png
+
+
+ true
+
+
+ false
+
+
+ true
-
+
+
+
+
-
+
- Tor proxy address:
+ Retroshare passphrase:
-
-
- 127.0.0.1
+
+
+
+ 0
+ 0
+
+
+
+ <html><head/><body><p>Your Retroshare login passphrase is needed to ensure the security of data exchange with the friend server.</p></body></html>
+
+
+ QLineEdit::Password
+
+
+ Your retroshare passphrase
-
-
-
- 1025
-
-
- 65535
-
-
- 9050
-
-
-
-
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
-
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
+
+
+ On/Off
+
+
+
+
+
+
+ Qt::Horizontal
+
+
+
+ 302
+ 26
+
+
+
+
+
+
+
+ Friends to request:
+
+
+
+
+
+
+ 1
+
+
+ 15
+
+
+ 5
+
+
+
+
+
+
+ Auto accept received certificates as friends
+
+
+ Auto-accept
+
+
+
+
+
+
+
+
+
+ Qt::CustomContextMenu
+
+
+
+ Name
+
+
+
+
+ Node ID
+
+
+
+
+ Address
+
+
+
+
+ Status
+
+
+
+
@@ -178,6 +234,8 @@
-
+
+
+
diff --git a/retroshare-gui/src/gui/FriendsDialog.cpp b/retroshare-gui/src/gui/FriendsDialog.cpp
index e3f74fcfd..532a87f4a 100644
--- a/retroshare-gui/src/gui/FriendsDialog.cpp
+++ b/retroshare-gui/src/gui/FriendsDialog.cpp
@@ -27,6 +27,7 @@
#include
#include
+#include
#include "chat/ChatUserNotify.h"
#include "connect/ConnectFriendWizard.h"
@@ -92,7 +93,8 @@ FriendsDialog::FriendsDialog(QWidget *parent) : MainPage(parent)
ui.tabWidget->setTabPosition(QTabWidget::North);
#ifdef RS_EMBEDED_FRIEND_SERVER
- ui.tabWidget->addTab(friendServerControl = new FriendServerControl(),QIcon(IMAGE_PEERS), tr("Friend Server"));
+ if(RsAccounts::isTorAuto())
+ ui.tabWidget->addTab(friendServerControl = new FriendServerControl(),QIcon(IMAGE_PEERS), tr("Friend Server"));
#endif
ui.tabWidget->addTab(networkView = new NetworkView(),QIcon(IMAGE_NETWORK2), tr("Network graph"));
ui.tabWidget->addTab(networkDialog = new NetworkDialog(),QIcon(IMAGE_PEERS), tr("Keyring"));
@@ -123,25 +125,24 @@ FriendsDialog::FriendsDialog(QWidget *parent) : MainPage(parent)
// add self nick and Avatar to Friends.
RsPeerDetails pd ;
- if (rsPeers->getPeerDetails(rsPeers->getOwnId(),pd)) {
+ if (rsPeers->getPeerDetails(rsPeers->getOwnId(),pd))
ui.nicknameLabel->setText(QString::fromUtf8(pd.name.c_str()) + " (" + QString::fromUtf8(pd.location.c_str())+")");
- }
- QString hlp_str = tr(
- "
Network
\
-
The Network tab shows your friend Retroshare nodes: the neighbor Retroshare nodes that are connected to you. \
-
\
-
You can group nodes together to allow a finer level of information access, for instance to only allow \
- some nodes to see some of your files.
\
-
On the right, you will find 3 useful tabs: \
-
\
-
Broadcast sends messages to all connected nodes at once
\
-
Local network graph shows the network around you, based on discovery information
\
-
Keyring contains node keys you collected, mostly forwarded to you by your friend nodes
\
-
\
- ") ;
+ int H = misc::getFontSizeFactor("HelpButton").height();
+ QString hlp_str = tr(
+ "
Network
"
+ "
The Network tab shows your friend Retroshare nodes: the neighbor Retroshare nodes that are connected to you.
"
+ "
You can group nodes together to allow a finer level of information access, for instance to only allow"
+ " some nodes to see some of your files.
"
+ "
On the right, you will find 3 useful tabs:"
+ "
"
+ "
Broadcast sends messages to all connected nodes at once
"
+ "
Local network graph shows the network around you, based on discovery information
"
+ "
Keyring contains node keys you collected, mostly forwarded to you by your friend nodes
"
+ "
"
+ ).arg(QString::number(2*H));
- registerHelpButton(ui.helpButton, hlp_str,"FriendsDialog") ;
+ registerHelpButton(ui.helpButton, hlp_str,"FriendsDialog") ;
}
FriendsDialog::~FriendsDialog ()
@@ -258,13 +259,9 @@ void FriendsDialog::loadmypersonalstatus()
QString statustring = QString::fromUtf8(rsMsgs->getCustomStateString().c_str());
if (statustring.isEmpty())
- {
ui.mypersonalstatusLabel->setText(tr("Set your status message here."));
- }
else
- {
ui.mypersonalstatusLabel->setText(statustring);
- }
}
void FriendsDialog::clearChatNotify()
@@ -281,13 +278,11 @@ void FriendsDialog::statusmessage()
/*static*/ bool FriendsDialog::isGroupChatActive()
{
FriendsDialog *friendsDialog = dynamic_cast(MainWindow::getPage(MainWindow::Friends));
- if (!friendsDialog) {
+ if (!friendsDialog)
return false;
- }
- if (friendsDialog->ui.tabWidget->currentWidget() == friendsDialog->ui.groupChatTab) {
+ if (friendsDialog->ui.tabWidget->currentWidget() == friendsDialog->ui.groupChatTab)
return true;
- }
return false;
}
diff --git a/retroshare-gui/src/gui/GenCertDialog.cpp b/retroshare-gui/src/gui/GenCertDialog.cpp
index 1abdb698b..0f318049d 100644
--- a/retroshare-gui/src/gui/GenCertDialog.cpp
+++ b/retroshare-gui/src/gui/GenCertDialog.cpp
@@ -48,7 +48,7 @@
#include
#define IMAGE_GOOD ":/images/accepted16.png"
-#define IMAGE_BAD ":/images/cancel.png"
+#define IMAGE_BAD ":/icons/cancel.svg"
class EntropyCollectorWidget: public QTextBrowser
{
@@ -251,7 +251,7 @@ void GenCertDialog::initKeyList()
void GenCertDialog::mouseMoveEvent(QMouseEvent *e)
{
- std::cerr << "Mouse : " << e->x() << ", " << e->y() << std::endl;
+ //std::cerr << "Mouse : " << e->x() << ", " << e->y() << std::endl;
QDialog::mouseMoveEvent(e) ;
}
@@ -609,7 +609,9 @@ void GenCertDialog::genPerson()
QCoreApplication::processEvents();
QAbstractEventDispatcher* ed = QAbstractEventDispatcher::instance();
- std::cout << "Waiting ed->processEvents()" << std::endl;
+#ifdef DEBUG_GENCERTDIALOG
+ std::cout << "Waiting ed->processEvents()" << std::endl;
+#endif
time_t waitEnd = time(NULL) + 10;//Wait no more than 10 sec to processEvents
if (ed->hasPendingEvents())
while(ed->processEvents(QEventLoop::AllEvents) && (time(NULL) < waitEnd));
@@ -647,7 +649,7 @@ void GenCertDialog::genPerson()
{
/* complete the process */
RsInit::LoadPassword(sslPasswd);
- if (Rshare::loadCertificate(sslId, false)) {
+ if (RsApplication::loadCertificate(sslId, false)) {
// Normally we should clear the cached passphrase as soon as possible. However,some other GUI components may still need it at start.
// (csoler) This is really bad: we have to guess that 30 secs will be enough. I have no better way to do this.
diff --git a/retroshare-gui/src/gui/GetStartedDialog.cpp b/retroshare-gui/src/gui/GetStartedDialog.cpp
index 21fe60be0..399cc36d7 100644
--- a/retroshare-gui/src/gui/GetStartedDialog.cpp
+++ b/retroshare-gui/src/gui/GetStartedDialog.cpp
@@ -229,7 +229,7 @@ void GetStartedDialog::inviteFriends()
{
RsAutoUpdatePage::lockAllEvents();
- cert = rsPeers->GetRetroshareInvite(RsPeerId(),RetroshareInviteFlags::DNS | RetroshareInviteFlags::CURRENT_IP | RetroshareInviteFlags::FULL_IP_HISTORY);
+ cert = rsPeers->GetRetroshareInvite(RsPeerId(),RsPeers::defaultCertificateFlags | RetroshareInviteFlags::FULL_IP_HISTORY);
RsAutoUpdatePage::unlockAllEvents() ;
}
@@ -421,7 +421,7 @@ void GetStartedDialog::emailSupport()
sysVersion = "Linux";
#endif
#endif
- text += QString("My RetroShare Configuration is: (%1, %2, %3)").arg(Rshare::retroshareVersion(true)
+ text += QString("My RetroShare Configuration is: (%1, %2, %3)").arg(RsApplication::retroshareVersion(true)
, sysVersion
).arg(static_cast::type>(userLevel)) + "\n";
text += "\n";
diff --git a/retroshare-gui/src/gui/HelpDialog.cpp b/retroshare-gui/src/gui/HelpDialog.cpp
index cc3f1d407..ddaf925d4 100644
--- a/retroshare-gui/src/gui/HelpDialog.cpp
+++ b/retroshare-gui/src/gui/HelpDialog.cpp
@@ -80,7 +80,7 @@ HelpDialog::HelpDialog(QWidget *parent) :
ui->thanks->setHtml(in.readAll());
}
- ui->version->setText(Rshare::retroshareVersion(true));
+ ui->version->setText(RsApplication::retroshareVersion(true));
/* Add version numbers of libretroshare */
std::list libraries;
diff --git a/retroshare-gui/src/gui/HomePage.cpp b/retroshare-gui/src/gui/HomePage.cpp
index 6c1737076..ff7622b4b 100644
--- a/retroshare-gui/src/gui/HomePage.cpp
+++ b/retroshare-gui/src/gui/HomePage.cpp
@@ -24,6 +24,7 @@
#include "retroshare/rsinit.h"
#include "util/qtthreadsutils.h"
+#include "util/misc.h"
#include "gui/notifyqt.h"
#include "gui/msgs/MessageComposer.h"
@@ -49,14 +50,10 @@
HomePage::HomePage(QWidget *parent) :
MainPage(parent),
- ui(new Ui::HomePage),
- mIncludeAllIPs(false),
- mUseShortFormat(true)
+ ui(new Ui::HomePage)
{
ui->setupUi(this);
- updateCertificate();
-
connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addFriend()));
connect(ui->copyIDButton, SIGNAL(clicked()), this, SLOT(copyId()));
@@ -72,52 +69,72 @@ HomePage::HomePage(QWidget *parent) :
QAction *CopyIdAction = new QAction(QIcon(),tr("Copy your Retroshare ID to Clipboard"), this);
connect(CopyIdAction, SIGNAL(triggered()), this, SLOT(copyId()));
- QMenu *menu = new QMenu();
+ QMenu *menu = new QMenu();
menu->addAction(CopyIdAction);
- if(!RsAccounts::isHiddenNode())
- {
- QAction *includeIPsAct = new QAction(QIcon(), tr("Include all your known IPs"),this);
- connect(includeIPsAct, SIGNAL(triggered()), this, SLOT(toggleIncludeAllIPs()));
- includeIPsAct->setCheckable(true);
- includeIPsAct->setChecked(mIncludeAllIPs);
-
- menu->addAction(includeIPsAct);
- }
- QAction *useOldFormatAct = new QAction(QIcon(), tr("Use old certificate format"),this);
- useOldFormatAct->setToolTip(tr("Displays the certificate format used up to version 0.6.5\nOld Retroshare nodes will not understand the\nnew short format"));
- connect(useOldFormatAct, SIGNAL(triggered()), this, SLOT(toggleUseOldFormat()));
- useOldFormatAct->setCheckable(true);
- useOldFormatAct->setChecked(!mUseShortFormat);
- menu->addAction(useOldFormatAct);
-
menu->addSeparator();
menu->addAction(SendAction);
menu->addAction(WebMailAction);
menu->addAction(RecAction);
+ menu->addSeparator();
+
+ mUseOldFormatact = new QAction(QIcon(), tr("Use old certificate format"),this);
+ mUseOldFormatact->setToolTip(tr("Displays the certificate format used up to version 0.6.5\nOld Retroshare nodes will not understand the\nnew short format"));
+ connect(mUseOldFormatact, SIGNAL(triggered()), this, SLOT(updateOwnCert()));
+ mUseOldFormatact->setCheckable(true);
+ mUseOldFormatact->setChecked(false);
+ menu->addAction(mUseOldFormatact);
+
+ if(!RsAccounts::isHiddenNode())
+ {
+ mIncludeLocIPact = new QAction(QIcon(), tr("Include current local IP"),this);
+ connect(mIncludeLocIPact, SIGNAL(triggered()), this, SLOT(updateOwnCert()));
+ mIncludeLocIPact->setCheckable(true);
+ mIncludeLocIPact->setChecked(true);
+ menu->addAction(mIncludeLocIPact);
+
+ mIncludeExtIPact = new QAction(QIcon(), tr("Include current external IP"),this);
+ connect(mIncludeExtIPact, SIGNAL(triggered()), this, SLOT(updateOwnCert()));
+ mIncludeExtIPact->setCheckable(true);
+ mIncludeExtIPact->setChecked(true);
+ menu->addAction(mIncludeExtIPact);
+
+ mIncludeDNSact = new QAction(QIcon(), tr("Include my DNS"),this);
+ connect(mIncludeDNSact, SIGNAL(triggered()), this, SLOT(updateOwnCert()));
+ mIncludeDNSact->setCheckable(true);
+ mIncludeDNSact->setChecked(true);
+ menu->addAction(mIncludeDNSact);
+
+ mIncludeIPHistoryact = new QAction(QIcon(), tr("Include all IPs history"),this);
+ connect(mIncludeIPHistoryact, SIGNAL(triggered()), this, SLOT(updateOwnCert()));
+ mIncludeIPHistoryact->setCheckable(true);
+ mIncludeIPHistoryact->setChecked(false);
+ menu->addAction(mIncludeIPHistoryact);
+ }
ui->shareButton->setMenu(menu);
connect(ui->openwebhelp,SIGNAL(clicked()), this,SLOT(openWebHelp())) ;
- int S = QFontMetricsF(font()).height();
- QString help_str = tr(
- "
Welcome to Retroshare!
\
-
You need to make friends! After you create a network of friends or join an existing network,\
- you'll be able to exchange files, chat, talk in forums, etc.
\
-
\
- \
-
\
-
To do so, copy your Retroshare ID on this page and send it to friends, and add your friends' Retroshare ID.
\
-
Another option is to search the internet for \"Retroshare chat servers\" (independently administrated). These servers allow you to exchange \
- Retroshare ID with a dedicated Retroshare node, through which\
- you will be able to anonymously meet other people.
").arg(QString::number(2*S)).arg(width()*0.5);
- registerHelpButton(ui->helpButton,help_str,"HomePage") ;
+ int H = misc::getFontSizeFactor("HelpButton").height();
+ QString help_str = tr(
+ "
Welcome to Retroshare!
"
+ "
You need to make friends! After you create a network of friends or join an existing network,"
+ " you'll be able to exchange files, chat, talk in forums, etc.
"
+ "
"
+ "
To do so, copy your Retroshare ID on this page and send it to friends, and add your friends' Retroshare ID.
"
+ "
Another option is to search the internet for \"Retroshare chat servers\" (independently administrated). These servers allow you to exchange"
+ " Retroshare ID with a dedicated Retroshare node, through which"
+ " you will be able to anonymously meet other people.
In this tab you can create/edit pseudo-anonymous identities, and circles.
\
-
Identities are used to securely identify your data: sign messages in chat lobbies, forum and channel posts,\
- receive feedback using the Retroshare built-in email system, post comments \
- after channel posts, chat using secured tunnels, etc.
\
-
Identities can optionally be signed by your Retroshare node's certificate. \
- Signed identities are easier to trust but are easily linked to your node's IP address.
\
-
Anonymous identities allow you to anonymously interact with other users. They cannot be \
- spoofed, but noone can prove who really owns a given identity.
\
-
Circles are groups of identities (anonymous or signed), that are shared at a distance over the network. They can be \
- used to restrict the visibility to forums, channels, etc.
\
-
An circle can be restricted to another circle, thereby limiting its visibility to members of that circle \
- or even self-restricted, meaning that it is only visible to invited members.
") ;
+ "
Identities
"
+ "
In this tab you can create/edit pseudo-anonymous identities, and circles.
"
+ "
Identities are used to securely identify your data: sign messages in chat lobbies, forum and channel posts,"
+ " receive feedback using the Retroshare built-in email system, post comments"
+ " after channel posts, chat using secured tunnels, etc.
"
+ "
Identities can optionally be signed by your Retroshare node's certificate."
+ " Signed identities are easier to trust but are easily linked to your node's IP address.
"
+ "
Anonymous identities allow you to anonymously interact with other users. They cannot be"
+ " spoofed, but noone can prove who really owns a given identity.
"
+ "
Circles are groups of identities (anonymous or signed), that are shared at a distance over the network. They can be"
+ " used to restrict the visibility to forums, channels, etc.
"
+ "
An circle can be restricted to another circle, thereby limiting its visibility to members of that circle"
+ " or even self-restricted, meaning that it is only visible to invited members.
"
+ ).arg(QString::number(2*H));
registerHelpButton(ui->helpButton, hlp_str,"PeopleDialog") ;
@@ -477,9 +479,9 @@ void IdDialog::handleEvent_main_thread(std::shared_ptr event)
void IdDialog::clearPerson()
{
- QFontMetricsF f(ui->avLabel_Person->font()) ;
+ //QFontMetricsF f(ui->avLabel_Person->font()) ;
- ui->avLabel_Person->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/people.png").scaled(f.height()*4,f.height()*4,Qt::KeepAspectRatio,Qt::SmoothTransformation));
+ //ui->avLabel_Person->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/people.png").scaled(f.height()*4,f.height()*4,Qt::KeepAspectRatio,Qt::SmoothTransformation));
ui->headerTextLabel_Person->setText(tr("People"));
ui->info_Frame_Invite->hide();
@@ -1109,7 +1111,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",":/icons/png/enter.png",":/images/cancel.png" } ;
+ const QString image_names[4] = { ":/images/edit_add24.png",":/images/accepted16.png",":/icons/png/enter.png",":/icons/cancel.svg" } ;
std::vector< std::vector > ids(4) ;
@@ -1699,8 +1701,8 @@ void IdDialog::loadIdentity(RsGxsIdGroup data)
//ui->avLabel_Person->setPixmap(pixmap);
//ui->avatarLabel->setPixmap(pixmap);
- QFontMetricsF f(ui->avLabel_Person->font()) ;
- ui->avLabel_Person->setPixmap(pixmap.scaled(f.height()*4,f.height()*4,Qt::KeepAspectRatio,Qt::SmoothTransformation));
+ //QFontMetricsF f(ui->avLabel_Person->font()) ;
+ //ui->avLabel_Person->setPixmap(pixmap.scaled(f.height()*4,f.height()*4,Qt::KeepAspectRatio,Qt::SmoothTransformation));
ui->avatarLabel->setPixmap(pixmap.scaled(ui->inviteButton->width(),ui->inviteButton->width(),Qt::IgnoreAspectRatio,Qt::SmoothTransformation));
ui->avatarLabel->setScaledContents(true);
@@ -2258,7 +2260,7 @@ void IdDialog::IdListCustomPopupMenu( QPoint )
contextMenu->addAction(QIcon(""),tr("Copy identity to clipboard"),this,SLOT(copyRetroshareLink())) ;
if(n_is_not_a_contact == 0)
- contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(":/images/cancel.png"), tr("Remove from Contacts"), this, SLOT(removefromContacts()));
+ contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(":/icons/cancel.svg"), tr("Remove from Contacts"), this, SLOT(removefromContacts()));
contextMenu->addSeparator();
@@ -2277,8 +2279,8 @@ void IdDialog::IdListCustomPopupMenu( QPoint )
contextMenu->addSeparator();
contextMenu->addAction(QIcon(""),tr("Copy identity to clipboard"),this,SLOT(copyRetroshareLink())) ;
- contextMenu->addAction(ui->editIdentity);
- contextMenu->addAction(ui->removeIdentity);
+ contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_EDIT),tr("Edit identity"),this,SLOT(editIdentity())) ;
+ contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(":/icons/cancel.svg"),tr("Delete identity"),this,SLOT(removeIdentity())) ;
}
}
diff --git a/retroshare-gui/src/gui/Identity/IdDialog.ui b/retroshare-gui/src/gui/Identity/IdDialog.ui
index 8e40da52b..1c504f8fc 100644
--- a/retroshare-gui/src/gui/Identity/IdDialog.ui
+++ b/retroshare-gui/src/gui/Identity/IdDialog.ui
@@ -6,7 +6,7 @@
00
- 800
+ 878584
@@ -301,213 +301,349 @@
00
- 634
- 523
+ 505
+ 716
-
-
-
+
+
+
+
+ 0
+
+
+
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 128
+ 128
+
+
+
+
+ 128
+ 128
+
+
+
+ QFrame::Box
+
+
+ QFrame::Sunken
+
+
+ Your Avatar
+
+
+ true
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+
+ 0
+ 0
+
+
+
+ Edit Identity
+
+
+
+
+
+
+
+ 0
+ 0
+
+
+
+ Send Invite
+
+
+
+
+
+
+
+
+ 6
+
+
+ 2
+
+
+
+
+
+ 34
+ 34
+
+
+
+
+
+
+ :/icons/png/thumbs-up.png
+
+
+ true
+
+
+
+
+
+
+
+ 16
+
+
+
+ Positive votes
+
+
+ 0
+
+
+
+
+
+
+ Qt::Vertical
+
+
+
+
+
+
+
+ 34
+ 34
+
+
+
+
+
+
+ :/icons/png/thumbs-down.png
+
+
+ true
+
+
+
+
+
+
+
+ 16
+
+
+
+ Negative votes
+
+
+ 0
+
+
+
+
+
+
+
+
+ Qt::Vertical
+
+
+
+ 118
+ 17
+
+
+
+
+
+
+
+
-
+ 00
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+ 255
+ 255
+ 178
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+ 255
+ 255
+ 178
+
+
+
+
+
+
+
+
+ 154
+ 154
+ 154
+
+
+
+
+
+
+ 255
+ 255
+ 178
+
+
+
+
+
+
+ 255
+ 255
+ 178
+
+
+
+
+
+
+
+ true
+
+
+
+
- QFrame::StyledPanel
+ QFrame::Box
-
- QFrame::Raised
-
-
-
- 12
+
+
+ 6
-
-
-
-
-
-
- 22
-
-
-
- People
-
-
-
-
-
-
- Qt::Vertical
-
-
-
- 518
- 17
-
-
-
-
-
-
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 255
- 255
- 255
-
-
-
-
-
-
- 255
- 255
- 178
-
-
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 255
- 255
- 255
-
-
-
-
-
-
- 255
- 255
- 178
-
-
-
-
-
-
-
-
- 154
- 154
- 154
-
-
-
-
-
-
- 255
- 255
- 178
-
-
-
-
-
-
- 255
- 255
- 178
-
-
-
-
-
-
-
- true
-
-
-
-
-
- QFrame::Box
-
-
-
- 6
-
-
- 6
-
-
- 6
-
-
- 6
-
-
-
-
-
- 16
- 16
-
-
-
-
-
-
- :/images/info16.png
-
-
- Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
-
-
-
-
-
-
- Invite messages stay into your Outbox until an acknowledgement of receipt has been received.
-
-
-
-
-
-
-
- 16
- 16
-
-
-
- Qt::NoFocus
-
-
- Close
-
-
- QToolButton
+
+ 6
+
+
+ 6
+
+
+ 6
+
+
+
+
+
+ 16
+ 16
+
+
+
+
+
+
+ :/images/info16.png
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+
+
+
+
+ Invite messages stay into your Outbox until an acknowledgement of receipt has been received.
+
+
+ true
+
+
+
+
+
+
+
+ 16
+ 16
+
+
+
+ Qt::NoFocus
+
+
+ Close
+
+
+ QToolButton
{
border-image: url(:/images/closenormal.png)
}
@@ -520,44 +656,8 @@ border-image: url(:/images/closehover.png)
QToolButton:pressed {
border-image: url(:/images/closepressed.png)
}
-
-
- true
-
-
-
-
-
-
-
-
-
-
-
-
- 0
- 0
-
-
-
- 64
- 64
-
-
-
-
- 1000
- 1000
-
-
-
-
-
-
-
-
-
+ true
@@ -565,8 +665,39 @@ border-image: url(:/images/closepressed.png)
-
+
+
+
+ Usage statistics
+
+
+
+
+
+
+
+
+
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+ 0
+ 0
+
+ 0
@@ -577,23 +708,6 @@ border-image: url(:/images/closepressed.png)
Identity info
-
-
-
- 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>
-
-
- true
-
-
-
@@ -604,189 +718,6 @@ border-image: url(:/images/closepressed.png)
-
-
-
- 0
-
-
-
-
-
- 0
- 0
-
-
-
-
- 128
- 128
-
-
-
-
- 128
- 128
-
-
-
- QFrame::Box
-
-
- QFrame::Sunken
-
-
- Your Avatar
-
-
- true
-
-
- Qt::AlignCenter
-
-
-
-
-
-
- Send Invite
-
-
-
-
-
-
- Edit Identity
-
-
-
-
-
-
- 6
-
-
- 2
-
-
-
-
-
- 34
- 34
-
-
-
-
-
-
- :/icons/png/thumbs-up.png
-
-
- true
-
-
-
-
-
-
-
- 16
-
-
-
- Positive votes
-
-
- 0
-
-
-
-
-
-
- Qt::Vertical
-
-
-
-
-
-
-
- 34
- 34
-
-
-
-
-
-
- :/icons/png/thumbs-down.png
-
-
- true
-
-
-
-
-
-
-
- 16
-
-
-
- Negative votes
-
-
- 0
-
-
-
-
-
-
-
-
- Qt::Vertical
-
-
-
- 118
- 17
-
-
-
-
-
-
-
-
-
- true
-
-
-
-
-
-
- Type:
-
-
-
-
-
-
-
- 75
- true
-
-
-
- Overall:
-
-
-
@@ -794,17 +725,10 @@ border-image: url(:/images/closepressed.png)
-
-
+
+
- Identity ID :
-
-
-
-
-
-
- Created on :
+ Identity name :
@@ -815,25 +739,42 @@ border-image: url(:/images/closepressed.png)
-
-
-
- Ban-option:
+
+
+
+ true
+
+
+ true
-
+
+
+
+ Auto-Ban all identities signed by the same node
+
+
+ Auto-Ban profile
+
+
+
+
+
+
+ true
+
+
+
+ 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
@@ -846,7 +787,95 @@ border-image: url(:/images/closepressed.png)
-
+
+
+
+
+ 75
+ true
+
+
+
+ Overall:
+
+
+
+
+
+
+ Created on :
+
+
+
+
+
+
+ Owner node ID :
+
+
+
+
+
+
+ Ban-option:
+
+
+
+
+
+
+ <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
+
+
+
+
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 1
+
+
+
+
+
+
+
+ Identity ID :
+
+
+
+
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+ true
+
+
+ true
+
+
+
+
+
+
+ Type:
+
+
+
+ <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 -> people. </span></p></body></html>
@@ -886,20 +915,6 @@ border-image: url(:/images/closepressed.png)
-
-
-
- Identity name :
-
-
-
-
-
-
- Auto-Ban all identities signed by the same node
-
-
-
@@ -910,43 +925,22 @@ border-image: url(:/images/closepressed.png)
-
-
+
+
+
+ <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>
+ true
-
+ Friend votes:
-
-
-
-
-
- true
-
-
- true
-
-
-
-
-
-
- Qt::Horizontal
-
-
-
-
-
-
- true
-
-
+ true
@@ -954,35 +948,19 @@ border-image: url(:/images/closepressed.png)
-
-
-
- Usage statistics
+
+
+
+
+ 22
+
+
+
+ People
-
-
-
-
-
-
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
- detailsGroupBox
- usageStatisticsGBox
- headerBFramePerson
@@ -1091,10 +1069,6 @@ border-image: url(:/images/closepressed.png)
-
-
- :/images/edit_16.png:/images/edit_16.png
- Edit identity
@@ -1103,19 +1077,11 @@ border-image: url(:/images/closepressed.png)
-
-
- :/images/delete.png:/images/delete.png
- Delete identity
-
-
- :/images/toaster/chat.png:/images/toaster/chat.png
- Chat with this peer
diff --git a/retroshare-gui/src/gui/Identity/IdEditDialog.cpp b/retroshare-gui/src/gui/Identity/IdEditDialog.cpp
index 9396a9758..7e5544413 100644
--- a/retroshare-gui/src/gui/Identity/IdEditDialog.cpp
+++ b/retroshare-gui/src/gui/Identity/IdEditDialog.cpp
@@ -31,9 +31,10 @@
#include "util/misc.h"
#include "gui/notifyqt.h"
-#include
-#include
+#include "retroshare/rsidentity.h"
+#include "retroshare/rspeers.h"
#include "gui/common/FilesDefs.h"
+#include "util/imageutil.h"
#include
@@ -88,6 +89,7 @@ IdEditDialog::IdEditDialog(QWidget *parent) :
connect(ui->toolButton_Tag4, SIGNAL(clicked(bool)), this, SLOT(rmTag4()));
connect(ui->toolButton_Tag5, SIGNAL(clicked(bool)), this, SLOT(rmTag5()));
connect(ui->avatarButton, SIGNAL(clicked(bool)), this, SLOT(changeAvatar()));
+ connect(ui->removeButton, SIGNAL(clicked(bool)), this, SLOT(removeAvatar()));
connect(ui->avatarLabel,SIGNAL(cleared()),this,SLOT(avatarCleared()));
@@ -102,6 +104,8 @@ IdEditDialog::IdEditDialog(QWidget *parent) :
ui->plainTextEdit_Tag->hide();
ui->label_TagCheck->hide();
ui->frame_Tags->setHidden(true);
+
+ updateInterface();
}
IdEditDialog::~IdEditDialog() {}
@@ -136,6 +140,8 @@ void IdEditDialog::changeAvatar()
// shows the tooltip for a while
QToolTip::showText( ui->avatarLabel->mapToGlobal( QPoint( 0, 0 ) ), ui->avatarLabel->toolTip() );
+
+ updateInterface();
}
void IdEditDialog::setupNewId(bool pseudo,bool enable_anon)
@@ -353,6 +359,7 @@ void IdEditDialog::loadExistingId(const RsGxsIdGroup& id_group)
ui->frame_Tags->setHidden(true);
loadRecognTags();
+ updateInterface();
}
#define MAX_RECOGN_TAGS 5
@@ -570,8 +577,10 @@ void IdEditDialog::createId()
QByteArray ba;
QBuffer buffer(&ba);
+ bool has_transparency = ImageUtil::hasAlphaContent(mAvatar.toImage());
+
buffer.open(QIODevice::WriteOnly);
- mAvatar.save(&buffer, "PNG"); // writes image into ba in PNG format
+ mAvatar.save(&buffer, has_transparency?"PNG":"JPG"); // writes image into ba in PNG format
params.mImage.copy((uint8_t *) ba.data(), ba.size());
}
@@ -642,8 +651,10 @@ void IdEditDialog::updateId()
QByteArray ba;
QBuffer buffer(&ba);
- buffer.open(QIODevice::WriteOnly);
- mAvatar.save(&buffer, "PNG"); // writes image into ba in PNG format
+ bool has_transparency = ImageUtil::hasAlphaContent(mAvatar.toImage());
+
+ buffer.open(QIODevice::WriteOnly);
+ mAvatar.save(&buffer, has_transparency?"PNG":"JPG"); // writes image into ba in PNG format
mEditGroup.mImage.copy((uint8_t *) ba.data(), ba.size());
}
@@ -679,3 +690,22 @@ void IdEditDialog::updateId()
accept();
}
+void IdEditDialog::removeAvatar()
+{
+ ui->avatarLabel->setPicture(QPixmap());
+ mEditGroup.mImage.clear();
+
+ updateInterface();
+}
+
+void IdEditDialog::updateInterface()
+{
+ const QPixmap *pixmap = ui->avatarLabel->pixmap();
+ if (pixmap && !pixmap->isNull()) {
+ ui->removeButton->setEnabled(true);
+ } else if (mEditGroup.mImage.mSize > 0) {
+ ui->removeButton->setEnabled(true);
+ } else {
+ ui->removeButton->setEnabled(false);
+ }
+}
diff --git a/retroshare-gui/src/gui/Identity/IdEditDialog.h b/retroshare-gui/src/gui/Identity/IdEditDialog.h
index 8ee6b15c9..714a79256 100644
--- a/retroshare-gui/src/gui/Identity/IdEditDialog.h
+++ b/retroshare-gui/src/gui/Identity/IdEditDialog.h
@@ -23,7 +23,6 @@
#include
-#include "util/TokenQueue.h"
#include
#include
#include
@@ -54,6 +53,8 @@ private slots:
void avatarCleared();
void changeAvatar();
+ void removeAvatar();
+ void updateInterface();
void addRecognTag();
void checkNewTag();
@@ -69,7 +70,6 @@ private:
void updateIdType(bool pseudo);
void loadExistingId(const RsGxsIdGroup& id_group);
void setAvatar(const QPixmap &avatar);
- void idCreated(uint32_t token);
void loadRecognTags();
// extract details.
diff --git a/retroshare-gui/src/gui/Identity/IdEditDialog.ui b/retroshare-gui/src/gui/Identity/IdEditDialog.ui
index 1b4e53186..d0a625c00 100644
--- a/retroshare-gui/src/gui/Identity/IdEditDialog.ui
+++ b/retroshare-gui/src/gui/Identity/IdEditDialog.ui
@@ -369,6 +369,13 @@
+
+
+
+ Remove
+
+
+
diff --git a/retroshare-gui/src/gui/MainPage.cpp b/retroshare-gui/src/gui/MainPage.cpp
index 545f1270e..6136343d7 100644
--- a/retroshare-gui/src/gui/MainPage.cpp
+++ b/retroshare-gui/src/gui/MainPage.cpp
@@ -18,12 +18,14 @@
* *
*******************************************************************************/
-#include
-#include
-
#include
+
#include "common/FloatingHelpBrowser.h"
#include "gui/settings/rsharesettings.h"
+#include "util/misc.h"
+
+#include
+#include
MainPage::MainPage(QWidget *parent , Qt::WindowFlags flags ) : QWidget(parent, flags)
{
@@ -44,13 +46,13 @@ UserNotify *MainPage::getUserNotify()
void MainPage::registerHelpButton(QToolButton *button, const QString& help_html_text, const QString &code_name)
{
- mHelpCodeName = code_name ;
+ mHelpCodeName = code_name ;
- if (mHelpBrowser == NULL)
+ if (mHelpBrowser == nullptr)
mHelpBrowser = new FloatingHelpBrowser(this, button) ;
- float S = QFontMetricsF(button->font()).height() ;
- button->setIconSize(QSize(S,S)) ;
+ int H = misc::getFontSizeFactor("HelpButton").height();
+ button->setIconSize(QSize(H, H)) ;//Square Icon
mHelpBrowser->setHelpText(help_html_text) ;
}
diff --git a/retroshare-gui/src/gui/MainWindow.cpp b/retroshare-gui/src/gui/MainWindow.cpp
index 207fc5130..b1e4b1e69 100644
--- a/retroshare-gui/src/gui/MainWindow.cpp
+++ b/retroshare-gui/src/gui/MainWindow.cpp
@@ -28,9 +28,15 @@
#include
#include
#include
+#include
#include
#include
+#include
+
+#if defined(Q_OS_DARWIN)
+#include "gui/common/MacDockIconHandler.h"
+#endif
#ifdef MESSENGER_WINDOW
#include "MessengerWindow.h"
@@ -114,7 +120,7 @@
#include "gui/statistics/StatisticsWindow.h"
#include "gui/connect/ConnectFriendWizard.h"
-#include "gui/common/RsCollection.h"
+#include "gui/common/RsCollectionDialog.h"
#include "settings/rsettingswin.h"
#include "settings/rsharesettings.h"
#include "common/StatusDefs.h"
@@ -141,12 +147,10 @@
#define IMAGE_OVERLAY ":/icons/star_overlay_128.png"
#define IMAGE_BWGRAPH ":/icons/png/bandwidth.png"
-#define IMAGE_MESSENGER ":/images/rsmessenger48.png"
#define IMAGE_COLOR ":/images/highlight.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 ":/icons/fullscreen.png"
#define IMAGE_MAXIMIZE ":/icons/fullscreen-exit.png"
@@ -208,7 +212,7 @@ MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags)
hiddenmode = true;
}
- setWindowTitle(tr("RetroShare %1 a secure decentralized communication platform").arg(Rshare::retroshareVersion(true)) + " - " + nameAndLocation);
+ setWindowTitle(tr("RetroShare %1 a secure decentralized communication platform").arg(RsApplication::retroshareVersion(true)) + " - " + nameAndLocation);
connect(rApp, SIGNAL(newArgsReceived(QStringList)), this, SLOT(receiveNewArgs(QStringList)));
/* add url handler for RetroShare links */
@@ -379,6 +383,11 @@ MainWindow::~MainWindow()
delete sysTrayStatus;
delete trayIcon;
delete trayMenu;
+#if defined(Q_OS_DARWIN)
+ delete menuBar;
+ delete dockMenu;
+ MacDockIconHandler::cleanup();
+#endif
// delete notifyMenu; // already deleted by the deletion of trayMenu
StatisticsWindow::releaseInstance();
@@ -468,10 +477,8 @@ void MainWindow::initStackedPage()
}
#ifndef RS_RELEASE_VERSION
-#ifdef PLUGINMGR
addPage(pluginsPage = new gui::PluginsPage(ui->stackPages), grp, NULL);
#endif
-#endif
#undef GETSTARTED_GUI
#ifdef GETSTARTED_GUI
@@ -489,11 +496,11 @@ void MainWindow::initStackedPage()
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();
+ for (auto notifyIt:notify)
+ {
+ 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);
connect(userNotify, SIGNAL(countChanged()), this, SLOT(updateTrayCombine()));
userNotifyList.push_back(userNotify);
}
@@ -507,7 +514,7 @@ void MainWindow::initStackedPage()
#ifdef UNFINISHED
- addAction(new QAction(QIcon(IMAGE_UNFINISHED), tr("Unfinished"), ui->toolBar), &MainWindow::showApplWindow, SLOT(showApplWindow()));
+ addAction(new QAction(QIcon(), tr("Unfinished"), ui->toolBar), &MainWindow::showApplWindow, SLOT(showApplWindow()));
ui->toolBarAction->addSeparator();
notify += applicationWindow->getNotify();
#endif
@@ -625,7 +632,7 @@ void MainWindow::createTrayIcon()
trayMenu->addSeparator();
#ifdef MESSENGER_WINDOW
- trayMenu->addAction(QIcon(IMAGE_MESSENGER), tr("Open Messenger"), this, SLOT(showMessengerWindow()));
+ trayMenu->addAction(QIcon(), tr("Open Messenger"), this, SLOT(showMessengerWindow()));
#endif
trayMenu->addAction(QIcon(IMAGE_MESSAGES), tr("Open Messages"), this, SLOT(showMess()));
#ifdef RS_JSONAPI
@@ -638,7 +645,7 @@ void MainWindow::createTrayIcon()
#ifdef UNFINISHED
- trayMenu->addAction(QIcon(IMAGE_UNFINISHED), tr("Applications"), this, SLOT(showApplWindow()));
+ trayMenu->addAction(QIcon(), tr("Applications"), this, SLOT(showApplWindow()));
#endif
trayMenu->addAction(QIcon(IMAGE_PREFERENCES), tr("Options"), this, SLOT(showSettings()));
trayMenu->addAction(QIcon(IMG_HELP), tr("Help"), this, SLOT(showHelpDialog()));
@@ -655,10 +662,75 @@ void MainWindow::createTrayIcon()
trayIcon->setContextMenu(trayMenu);
trayIcon->setIcon(QIcon(IMAGE_NOONLINE));
+#if defined(Q_OS_DARWIN)
+ // Note: On macOS, the Dock icon is used to provide the tray's functionality.
+ MacDockIconHandler* dockIconHandler = MacDockIconHandler::instance();
+ connect(dockIconHandler, &MacDockIconHandler::dockIconClicked, [this] {
+ show();
+ activateWindow();
+ });
+#endif
+
+#if defined(Q_OS_DARWIN)
+ createMenuBar();
+#endif
+
connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(toggleVisibility(QSystemTrayIcon::ActivationReason)));
trayIcon->show();
}
+#if defined(Q_OS_DARWIN)
+/** Creates a new menubar for macOS */
+void MainWindow::createMenuBar()
+{
+ /* Mac users sure like their shortcuts. */
+ actionMinimize = new QAction(tr("Minimize"),this);
+ actionMinimize->setShortcutContext(Qt::ApplicationShortcut);
+ actionMinimize->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_M));
+ actionMinimize->setShortcutVisibleInContextMenu(true);
+ connect(actionMinimize,SIGNAL(triggered()),this,SLOT(minimizeWindow())) ;
+
+ actionCloseWindow = new QAction(tr("Close window"),this);
+ actionCloseWindow->setShortcutContext(Qt::ApplicationShortcut);
+ actionCloseWindow->setShortcut(QKeySequence::Close);
+ actionCloseWindow->setShortcutVisibleInContextMenu(true);
+ connect(actionCloseWindow,SIGNAL(triggered()),this,SLOT(closeWindow())) ;
+
+ menuBar = new QMenuBar(this);
+ QMenu *fileMenu = menuBar->addMenu("");
+ fileMenu->addAction(actionMinimize);
+ fileMenu->addAction(actionCloseWindow);
+
+ dockMenu = new QMenu(this);
+ dockMenu->setAsDockMenu();
+ dockMenu->addAction(tr("Open Messages"), this, SLOT(showMess()));
+ dockMenu->addAction(tr("Bandwidth Graph"), this, SLOT(showBandwidthGraph()));
+ dockMenu->addAction(tr("Statistics"), this, SLOT(showStatisticsWindow()));
+ dockMenu->addAction(tr("Options"), this, SLOT(showSettings()));
+ dockMenu->addAction(tr("Help"), this, SLOT(showHelpDialog()));
+
+ dockMenu->addSeparator();
+ QMenu *statusMenu = dockMenu->addMenu(tr("Status"));
+ initializeStatusObject(statusMenu, true);
+
+}
+#endif
+
+#if defined(Q_OS_DARWIN)
+void MainWindow::minimizeWindow()
+{
+ setWindowState(windowState() | Qt::WindowMinimized);
+}
+#endif
+
+#if defined(Q_OS_DARWIN)
+void MainWindow::closeWindow()
+{
+ // On macOS window close is basically equivalent to window hide.
+ close();
+}
+#endif
+
void MainWindow::showBandwidthGraph()
{
if(_bandwidthGraph == NULL)
@@ -1083,7 +1155,9 @@ void SetForegroundWindowInternal(HWND hWnd)
return _instance->gxsforumDialog;
case Posted:
return _instance->postedDialog;
- }
+ case Home:
+ return _instance->homePage;
+ }
return NULL;
}
@@ -1180,10 +1254,91 @@ void MainWindow::doQuit()
rApp->quit();
}
+// This method parses arguments passed by the operating system. All arguments
+// except -r, -f, -o and lists of rscollection files and rslinks are discarded.
+//
void MainWindow::receiveNewArgs(QStringList args)
{
- Rshare::parseArguments(args, false);
- processLastArgs();
+ RsInfo() << "Received new arguments from operating system call.";
+
+ std::string argstring = RsApplication::applicationFilePath().toStdString() ;
+
+ for(auto l:args)
+ argstring += " " + l.toStdString();
+
+ // This class does all the job at once: validate arguments, and parses them.
+
+ std::vector links_and_files;
+
+ argstream as(argstring.c_str());
+
+ QString omValues = QString(";full;noturtle;gaming;minimal;");
+ std::string opModeStr;
+ std::string retroshare_link_url;
+ std::string rscollection_file;
+
+ as >> parameter('r',"rslink",retroshare_link_url,"Retroshare:// link","Retroshare link to open in Downloads " ,false)
+ >> parameter('f',"rsfile",rscollection_file,"file","File to open " ,false)
+ >> parameter('o',"opmode",opModeStr,"opmode","Set mode (Full, NoTurtle, Gaming, Minimal) " ,false)
+ >> values(back_inserter(links_and_files),"links and files");
+
+ if(!as.isOk())
+ {
+ RsErr() << "Error while parsing arguments:" ;
+ RsErr() << as.errorLog() ;
+ return;
+ }
+ if(!opModeStr.empty() && omValues.contains(";"+QString::fromStdString(opModeStr).toLower()+";"))
+ {
+ QString opmode = QString::fromStdString(opModeStr).toLower();
+ //RsApplication::setOpMode(opModeStr.toLower()); // Do we need this??
+
+ RsInfo() << "Setting new operating mode to \"" << opmode.toStdString() << "\"";
+
+ if (opmode == "noturtle")
+ opModeStatus->setCurrentIndex(static_cast::type>(RsOpMode::NOTURTLE) - 1);
+ else if (opmode == "gaming")
+ opModeStatus->setCurrentIndex(static_cast::type>(RsOpMode::GAMING) - 1);
+ else if (opmode == "minimal")
+ opModeStatus->setCurrentIndex(static_cast::type>(RsOpMode::MINIMAL) - 1);
+ else if (opmode != "")
+ opModeStatus->setCurrentIndex(static_cast::type>(RsOpMode::FULL) - 1);
+
+ opModeStatus->setOpMode();
+ }
+
+ // Sort all collected arguments into rscollection files and retroshare links, accordingly
+
+ QStringList rscollection_files;
+ QList rslinks;
+
+ auto sort = [&](const QString s) {
+
+ if(QFile(s).exists() && s.endsWith(".rscollection"))
+ rscollection_files.append(QString::fromUtf8(rscollection_file.c_str()));
+ else if(s.startsWith("retroshare://"))
+ {
+ RetroShareLink link(s);
+
+ if(link.valid())
+ rslinks.push_back(link);
+ }
+ };
+
+ sort(QString::fromUtf8(rscollection_file.c_str()));
+ sort(QString::fromUtf8(retroshare_link_url.c_str()));
+
+ for(auto s:links_and_files)
+ sort(QString::fromUtf8(s.c_str()));
+
+ // Now handle links and rscollection files.
+
+ for(auto file:rscollection_files)
+ if(file.endsWith(".rscollection"))
+ openRsCollection(file);
+
+ for(auto link:rslinks)
+ retroshareLinkActivated(link.toUrl());
}
void MainWindow::displayErrorMessage(int /*a*/,int /*b*/,const QString& error_msg)
@@ -1222,7 +1377,11 @@ void MainWindow::updateMenu()
void MainWindow::toggleVisibility(QSystemTrayIcon::ActivationReason e)
{
+#if defined(Q_OS_DARWIN)
+ if (e == QSystemTrayIcon::DoubleClick) {
+#else
if (e == QSystemTrayIcon::Trigger || e == QSystemTrayIcon::DoubleClick) {
+#endif
if (isHidden() || isMinimized()) {
show();
if (isMinimized()) {
@@ -1546,45 +1705,9 @@ void MainWindow::retroshareLinkActivated(const QUrl &url)
void MainWindow::openRsCollection(const QString &filename)
{
QFileInfo qinfo(filename);
- if (qinfo.exists()) {
- if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) {
- RsCollection collection;
- collection.openColl(qinfo.absoluteFilePath());
- }
- }
-}
-
-void MainWindow::processLastArgs()
-{
- while (!Rshare::links()->isEmpty()) {
- std::cerr << "MainWindow::processLastArgs() : " << Rshare::links()->count() << std::endl;
- /* Now use links from the command line, because no RetroShare was running */
- RetroShareLink link(Rshare::links()->takeFirst());
- if (link.valid()) {
- retroshareLinkActivated(link.toUrl());
- }
- }
- while (!Rshare::files()->isEmpty()) {
- /* Now use files from the command line, because no RetroShare was running */
- openRsCollection(Rshare::files()->takeFirst());
- }
- /* Handle the -opmode options. */
- if (opModeStatus) {
- QString opmode = Rshare::opmode().toLower();
- if (opmode == "noturtle") {
- opModeStatus->setCurrentIndex(static_cast::type>(RsOpMode::NOTURTLE) - 1);
- } else if (opmode == "gaming") {
- opModeStatus->setCurrentIndex(static_cast::type>(RsOpMode::GAMING) - 1);
- } else if (opmode == "minimal") {
- opModeStatus->setCurrentIndex(static_cast::type>(RsOpMode::MINIMAL) - 1);
- } else if (opmode != "") {
- opModeStatus->setCurrentIndex(static_cast::type>(RsOpMode::FULL) - 1);
- }
- opModeStatus->setOpMode();
- } else {
- std::cerr << "ERR: MainWindow::processLastArgs opModeStatus is not initialized.";
- }
+ if (qinfo.exists() && qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString))
+ RsCollectionDialog::openExistingCollection(qinfo.absoluteFilePath());
}
void MainWindow::switchVisibilityStatus(StatusElement e,bool b)
@@ -1630,6 +1753,11 @@ NATStatus *MainWindow::natstatusInstance()
return natstatus;
}
+void MainWindow::torstatusReset()
+{
+ if(torstatus != nullptr)
+ torstatus->reset();
+}
DHTStatus *MainWindow::dhtstatusInstance()
{
return dhtstatus;
diff --git a/retroshare-gui/src/gui/MainWindow.h b/retroshare-gui/src/gui/MainWindow.h
index b4f3cccd5..a7f3013f1 100644
--- a/retroshare-gui/src/gui/MainWindow.h
+++ b/retroshare-gui/src/gui/MainWindow.h
@@ -100,9 +100,10 @@ public:
Channels = 6, /** Channels page. */
Forums = 7, /** Forums page. */
Search = 8, /** Search page. */
- Posted = 11, /** Posted links */
- People = 12, /** People page. */
- Options = 13 /** People page. */
+ Posted = 11, /** Posted links */
+ People = 12, /** People page. */
+ Options = 13, /** People page. */
+ Home = 14 /** Home page. */
};
@@ -186,6 +187,7 @@ public:
RSComboBox *statusComboBoxInstance();
PeerStatus *peerstatusInstance();
NATStatus *natstatusInstance();
+ void torstatusReset();
DHTStatus *dhtstatusInstance();
HashingStatus *hashingstatusInstance();
DiscStatus *discstatusInstance();
@@ -212,7 +214,6 @@ public slots:
void externalLinkActivated(const QUrl &url);
void retroshareLinkActivated(const QUrl &url);
void openRsCollection(const QString &filename);
- void processLastArgs();
//! Go to a specific part of the control panel.
void setNewPage(int page);
void setCompactStatusMode(bool compact);
@@ -263,6 +264,11 @@ private slots:
void toggleVisibility(QSystemTrayIcon::ActivationReason e);
void toggleVisibilitycontextmenu();
+#if defined(Q_OS_DARWIN)
+ void minimizeWindow();
+ void closeWindow();
+#endif
+
/** Toolbar fns. */
void addFriend();
//void newRsCollection();
@@ -306,6 +312,10 @@ private:
void initStackedPage();
void addPage(MainPage *page, QActionGroup *grp, QList > > *notify);
void createTrayIcon();
+#if defined(Q_OS_DARWIN)
+ /** Creates a default menubar on Mac */
+ void createMenuBar();
+#endif
void createNotifyIcons();
static MainWindow *_instance;
@@ -322,6 +332,14 @@ private:
QString nameAndLocation;
+#if defined(Q_OS_DARWIN)
+ /** The menubar (Mac OS X only). */
+ QMenuBar *menuBar;
+ QMenu *dockMenu;
+ QAction* actionMinimize;
+ QAction* actionCloseWindow;
+#endif
+
QSystemTrayIcon *trayIcon;
QMenu *notifyMenu;
QMenu *trayMenu;
diff --git a/retroshare-gui/src/gui/NetworkDialog.cpp b/retroshare-gui/src/gui/NetworkDialog.cpp
index 1b02cbc7c..7c2b3fd14 100644
--- a/retroshare-gui/src/gui/NetworkDialog.cpp
+++ b/retroshare-gui/src/gui/NetworkDialog.cpp
@@ -73,7 +73,7 @@ NetworkDialog::NetworkDialog(QWidget */*parent*/)
PGPIdItemProxy = new pgpid_item_proxy(this);
connect(ui.onlyTrustedKeys, SIGNAL(toggled(bool)), PGPIdItemProxy, SLOT(use_only_trusted_keys(bool)));
PGPIdItemProxy->setSourceModel(PGPIdItemModel);
- PGPIdItemProxy->setFilterKeyColumn(COLUMN_PEERNAME);
+ PGPIdItemProxy->setFilterKeyColumn(pgpid_item_model::PGP_ITEM_MODEL_COLUMN_PEERNAME);
PGPIdItemProxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
PGPIdItemProxy->setSortRole(Qt::EditRole); //use edit role to get raw data since we do not have edit for this model.
ui.connectTreeWidget->setModel(PGPIdItemProxy);
@@ -83,16 +83,16 @@ NetworkDialog::NetworkDialog(QWidget */*parent*/)
ui.connectTreeWidget->setUpdatesEnabled(true);
ui.connectTreeWidget->setSortingEnabled(true);
ui.connectTreeWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
- ui.connectTreeWidget->setSelectionMode(QAbstractItemView::SingleSelection);
+ ui.connectTreeWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
connect(ui.connectTreeWidget, SIGNAL( customContextMenuRequested( QPoint ) ), this, SLOT( connectTreeWidgetCostumPopupMenu( QPoint ) ) );
connect(ui.connectTreeWidget, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(peerdetails()));
ui.onlyTrustedKeys->setMinimumWidth(20*f);
/* add filter actions */
- ui.filterLineEdit->addFilter(QIcon(), tr("Name"), COLUMN_PEERNAME, tr("Search name"));
- ui.filterLineEdit->addFilter(QIcon(), tr("Peer ID"), COLUMN_PEERID, tr("Search peer ID"));
- ui.filterLineEdit->setCurrentFilter(COLUMN_PEERNAME);
+ ui.filterLineEdit->addFilter(QIcon(), tr("Name"), pgpid_item_model::PGP_ITEM_MODEL_COLUMN_PEERNAME, tr("Search name"));
+ ui.filterLineEdit->addFilter(QIcon(), tr("Peer ID"), pgpid_item_model::PGP_ITEM_MODEL_COLUMN_PEERID, tr("Search peer ID"));
+ ui.filterLineEdit->setCurrentFilter(pgpid_item_model::PGP_ITEM_MODEL_COLUMN_PEERNAME);
connect(ui.filterLineEdit, SIGNAL(textChanged(QString)), PGPIdItemProxy, SLOT(setFilterWildcard(QString)));
}
@@ -117,24 +117,12 @@ void NetworkDialog::connectTreeWidgetCostumPopupMenu( QPoint /*point*/ )
{
return;
}
-
QMenu *contextMnu = new QMenu;
- RsPgpId peer_id(ui.connectTreeWidget->model()->data(ui.connectTreeWidget->model()->index(l.begin()->row(), COLUMN_PEERID)).toString().toStdString()) ;
-
- // That's what context menus are made for
- RsPeerDetails detail;
- if(!rsPeers->getGPGDetails(peer_id, detail)) // that is not suppose to fail.
- return ;
-
- if(peer_id == rsPeers->getGPGOwnId())
- 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(), tr("Remove unused keys..."), this, SLOT(removeUnusedKeys()));
contextMnu->addAction(QIcon(), tr("Remove this key"), this, SLOT(removeSelectedKeys()));
-
contextMnu->exec(QCursor::pos());
}
@@ -177,11 +165,34 @@ void NetworkDialog::removeSelectedKeys()
QModelIndexList l = ui.connectTreeWidget->selectionModel()->selection().indexes();
if(l.empty())
return;
-
std::set selected;
- selected.insert(RsPgpId(ui.connectTreeWidget->model()->data(ui.connectTreeWidget->model()->index(l.begin()->row(), COLUMN_PEERID)).toString().toStdString()));
- removeKeys(selected);
+ std::set friends;
+ for (int i = 0; i < l.size(); i++)
+ {
+ RsPgpId peer_id = RsPgpId(ui.connectTreeWidget->model()->data(ui.connectTreeWidget->model()->index(l[i].row(), pgpid_item_model::PGP_ITEM_MODEL_COLUMN_PEERID)).toString().toStdString());
+ RsPeerDetails details ;
+ if(rsPeers->getGPGDetails(peer_id,details))
+ {
+ if(details.accept_connection)
+ friends.insert(peer_id);
+ else
+ selected.insert(peer_id);
+ }
+ }
+ if(!friends.empty())
+ {
+ if ((QMessageBox::question(this, "RetroShare", tr("You have selected %1 accepted peers among others,\n Are you sure you want to un-friend them?").arg(friends.size()), QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes)) == QMessageBox::Yes)
+ {
+ for(std::set::const_iterator it(friends.begin());it!=friends.end();++it)
+ rsPeers->removeFriend(*it);
+ selected.insert(friends.begin(),friends.end());
+ }
+ }
+ if(!selected.empty())
+ removeKeys(selected);
+
+ updateDisplay();
}
void NetworkDialog::removeKeys(std::set selected)
@@ -228,7 +239,7 @@ void NetworkDialog::denyFriend()
if(l.empty())
return;
- RsPgpId peer_id(ui.connectTreeWidget->model()->data(ui.connectTreeWidget->model()->index(l.begin()->row(), COLUMN_PEERID)).toString().toStdString());
+ RsPgpId peer_id(ui.connectTreeWidget->model()->data(ui.connectTreeWidget->model()->index(l.begin()->row(), pgpid_item_model::PGP_ITEM_MODEL_COLUMN_PEERID)).toString().toStdString());
rsPeers->removeFriend(peer_id) ;
securedUpdateDisplay();
@@ -241,7 +252,7 @@ void NetworkDialog::makeFriend()
if(l.empty())
return;
- PGPKeyDialog::showIt(RsPgpId(ui.connectTreeWidget->model()->data(ui.connectTreeWidget->model()->index(l.begin()->row(), COLUMN_PEERID)).toString().toStdString()), PGPKeyDialog::PageDetails);
+ PGPKeyDialog::showIt(RsPgpId(ui.connectTreeWidget->model()->data(ui.connectTreeWidget->model()->index(l.begin()->row(), pgpid_item_model::PGP_ITEM_MODEL_COLUMN_PEERID)).toString().toStdString()), PGPKeyDialog::PageDetails);
}
/** Shows Peer Information/Auth Dialog */
@@ -251,7 +262,7 @@ void NetworkDialog::peerdetails()
if(l.empty())
return;
- PGPKeyDialog::showIt(RsPgpId(ui.connectTreeWidget->model()->data(ui.connectTreeWidget->model()->index(l.begin()->row(), COLUMN_PEERID)).toString().toStdString()), PGPKeyDialog::PageDetails);
+ PGPKeyDialog::showIt(RsPgpId(ui.connectTreeWidget->model()->data(ui.connectTreeWidget->model()->index(l.begin()->row(), pgpid_item_model::PGP_ITEM_MODEL_COLUMN_PEERID)).toString().toStdString()), PGPKeyDialog::PageDetails);
}
void NetworkDialog::copyLink()
@@ -261,7 +272,7 @@ void NetworkDialog::copyLink()
return;
- RsPgpId peer_id (ui.connectTreeWidget->model()->data(ui.connectTreeWidget->model()->index(l.begin()->row(), COLUMN_PEERID)).toString().toStdString()) ;
+ RsPgpId peer_id (ui.connectTreeWidget->model()->data(ui.connectTreeWidget->model()->index(l.begin()->row(), pgpid_item_model::PGP_ITEM_MODEL_COLUMN_PEERID)).toString().toStdString()) ;
QList urls;
RetroShareLink link = RetroShareLink::createPerson(peer_id);
diff --git a/retroshare-gui/src/gui/NetworkDialog/pgpid_item_model.cpp b/retroshare-gui/src/gui/NetworkDialog/pgpid_item_model.cpp
index 49bba065f..016692092 100644
--- a/retroshare-gui/src/gui/NetworkDialog/pgpid_item_model.cpp
+++ b/retroshare-gui/src/gui/NetworkDialog/pgpid_item_model.cpp
@@ -24,6 +24,9 @@
#include
#include
+#define IMAGE_AUTHED ":/images/accepted16.png"
+#define IMAGE_DENIED ":/images/denied16.png"
+#define IMAGE_TRUSTED ":/images/rs-2.png"
/*TODO:
* using list here for internal data storage is not best option
@@ -41,22 +44,22 @@ QVariant pgpid_item_model::headerData(int section, Qt::Orientation orientation,
{
switch(section)
{
- case COLUMN_CHECK:
+ case PGP_ITEM_MODEL_COLUMN_CHECK:
return QString(tr(" Do you accept connections signed by this profile?"));
break;
- case COLUMN_PEERNAME:
+ case PGP_ITEM_MODEL_COLUMN_PEERNAME:
return QString(tr("Name of the profile"));
break;
- case COLUMN_I_AUTH_PEER:
+ case PGP_ITEM_MODEL_COLUMN_I_AUTH_PEER:
return QString(tr("This column indicates the trust level you indicated and whether you signed the profile PGP key"));
break;
- case COLUMN_PEER_AUTH_ME:
+ case PGP_ITEM_MODEL_COLUMN_PEER_AUTH_ME:
return QString(tr("Did that peer sign your own profile PGP key"));
break;
- case COLUMN_PEERID:
+ case PGP_ITEM_MODEL_COLUMN_PEERID:
return QString(tr("PGP Key Id of that profile"));
break;
- case COLUMN_LAST_USED:
+ case PGP_ITEM_MODEL_COLUMN_LAST_USED:
return QString(tr("Last time this key was used (received time, or to check connection)"));
break;
}
@@ -65,22 +68,22 @@ QVariant pgpid_item_model::headerData(int section, Qt::Orientation orientation,
{
switch(section)
{
- case COLUMN_CHECK:
+ case PGP_ITEM_MODEL_COLUMN_CHECK:
return QString(tr("Connections"));
break;
- case COLUMN_PEERNAME:
+ case PGP_ITEM_MODEL_COLUMN_PEERNAME:
return QString(tr("Profile"));
break;
- case COLUMN_I_AUTH_PEER:
+ case PGP_ITEM_MODEL_COLUMN_I_AUTH_PEER:
return QString(tr("Trust level"));
break;
- case COLUMN_PEER_AUTH_ME:
+ case PGP_ITEM_MODEL_COLUMN_PEER_AUTH_ME:
return QString(tr("Has signed your key?"));
break;
- case COLUMN_PEERID:
+ case PGP_ITEM_MODEL_COLUMN_PEERID:
return QString(tr("Id"));
break;
- case COLUMN_LAST_USED:
+ case PGP_ITEM_MODEL_COLUMN_LAST_USED:
return QString(tr("Last used"));
break;
}
@@ -98,13 +101,13 @@ QVariant pgpid_item_model::headerData(int section, Qt::Orientation orientation,
{
switch(section)
{
- case COLUMN_CHECK:
+ case PGP_ITEM_MODEL_COLUMN_CHECK:
return 25*font_height;
break;
- case COLUMN_PEERNAME: case COLUMN_I_AUTH_PEER: case COLUMN_PEER_AUTH_ME:
+ case PGP_ITEM_MODEL_COLUMN_PEERNAME: case PGP_ITEM_MODEL_COLUMN_I_AUTH_PEER: case PGP_ITEM_MODEL_COLUMN_PEER_AUTH_ME:
return 200*font_height;
break;
- case COLUMN_LAST_USED:
+ case PGP_ITEM_MODEL_COLUMN_LAST_USED:
return 75*font_height;
break;
}
@@ -122,7 +125,7 @@ int pgpid_item_model::rowCount(const QModelIndex &/*parent*/) const
int pgpid_item_model::columnCount(const QModelIndex &/*parent*/) const
{
- return COLUMN_COUNT;
+ return PGP_ITEM_MODEL_COLUMN_COUNT;
}
@@ -148,20 +151,20 @@ QVariant pgpid_item_model::data(const QModelIndex &index, int role) const
{
switch(index.column())
{
- case COLUMN_LAST_USED:
+ case PGP_ITEM_MODEL_COLUMN_LAST_USED:
return detail.lastUsed;
break;
- case COLUMN_I_AUTH_PEER:
+ case PGP_ITEM_MODEL_COLUMN_I_AUTH_PEER:
{
if (detail.ownsign)
return RS_TRUST_LVL_ULTIMATE;
return detail.trustLvl;
}
break;
- case COLUMN_PEER_AUTH_ME:
+ case PGP_ITEM_MODEL_COLUMN_PEER_AUTH_ME:
return detail.hasSignedMe;
break;
- case COLUMN_CHECK:
+ case PGP_ITEM_MODEL_COLUMN_CHECK:
return detail.accept_connection;
break;
default:
@@ -174,13 +177,13 @@ QVariant pgpid_item_model::data(const QModelIndex &index, int role) const
{
switch(index.column())
{
- case COLUMN_PEERNAME:
+ case PGP_ITEM_MODEL_COLUMN_PEERNAME:
return QString::fromUtf8(detail.name.c_str());
break;
- case COLUMN_PEERID:
+ case PGP_ITEM_MODEL_COLUMN_PEERID:
return QString::fromStdString(detail.gpg_id.toStdString());
break;
- case COLUMN_I_AUTH_PEER:
+ case PGP_ITEM_MODEL_COLUMN_I_AUTH_PEER:
{
if (detail.ownsign)
return tr("Personal signature");
@@ -199,7 +202,7 @@ QVariant pgpid_item_model::data(const QModelIndex &index, int role) const
}
}
break;
- case COLUMN_PEER_AUTH_ME:
+ case PGP_ITEM_MODEL_COLUMN_PEER_AUTH_ME:
{
if (detail.hasSignedMe)
return tr("Yes");
@@ -207,7 +210,7 @@ QVariant pgpid_item_model::data(const QModelIndex &index, int role) const
return tr("No");
}
break;
- case COLUMN_LAST_USED:
+ case PGP_ITEM_MODEL_COLUMN_LAST_USED:
{
time_t now = time(NULL);
uint64_t last_time_used = now - detail.lastUsed ;
@@ -226,7 +229,7 @@ QVariant pgpid_item_model::data(const QModelIndex &index, int role) const
return lst_used_str;
}
break;
- case COLUMN_CHECK:
+ case PGP_ITEM_MODEL_COLUMN_CHECK:
{
if (detail.accept_connection || rsPeers->getGPGOwnId() == detail.gpg_id)
return tr("Accepted");
@@ -242,7 +245,7 @@ QVariant pgpid_item_model::data(const QModelIndex &index, int role) const
{
switch(index.column())
{
- case COLUMN_I_AUTH_PEER:
+ case PGP_ITEM_MODEL_COLUMN_I_AUTH_PEER:
{
if (detail.ownsign)
return tr("PGP key signed by you");
@@ -263,7 +266,7 @@ QVariant pgpid_item_model::data(const QModelIndex &index, int role) const
{
switch(index.column())
{
- case COLUMN_CHECK:
+ case PGP_ITEM_MODEL_COLUMN_CHECK:
{
if (detail.accept_connection)
return QIcon(IMAGE_AUTHED);
@@ -345,14 +348,14 @@ void pgpid_item_model::data_updated(std::list &new_neighs)
break;
if(*i1 != *i2)
{
- QModelIndex topLeft = createIndex(ii1,0), bottomRight = createIndex(ii1, COLUMN_COUNT-1);
+ QModelIndex topLeft = createIndex(ii1,0), bottomRight = createIndex(ii1, PGP_ITEM_MODEL_COLUMN_COUNT-1);
emit dataChanged(topLeft, bottomRight);
}
}
}
if(new_size > old_size)
{
- QModelIndex topLeft = createIndex(old_size ? old_size -1 : 0 ,0), bottomRight = createIndex(new_size -1, COLUMN_COUNT-1);
+ QModelIndex topLeft = createIndex(old_size ? old_size -1 : 0 ,0), bottomRight = createIndex(new_size -1, PGP_ITEM_MODEL_COLUMN_COUNT-1);
emit dataChanged(topLeft, bottomRight);
}
//dirty solution for initial data fetch
diff --git a/retroshare-gui/src/gui/NetworkDialog/pgpid_item_model.h b/retroshare-gui/src/gui/NetworkDialog/pgpid_item_model.h
index c4ae73400..7a17f48cb 100644
--- a/retroshare-gui/src/gui/NetworkDialog/pgpid_item_model.h
+++ b/retroshare-gui/src/gui/NetworkDialog/pgpid_item_model.h
@@ -25,20 +25,6 @@
#include
#include
-#define IMAGE_AUTHED ":/images/accepted16.png"
-#define IMAGE_DENIED ":/images/denied16.png"
-#define IMAGE_TRUSTED ":/images/rs-2.png"
-
-
-#define COLUMN_CHECK 0
-#define COLUMN_PEERNAME 1
-#define COLUMN_I_AUTH_PEER 2
-#define COLUMN_PEER_AUTH_ME 3
-#define COLUMN_PEERID 4
-#define COLUMN_LAST_USED 5
-#define COLUMN_COUNT 6
-
-
class pgpid_item_model : public QAbstractTableModel
{
Q_OBJECT
@@ -62,6 +48,13 @@ public:
void setBackgroundColorDenied(QColor color) { mBackgroundColorDenied = color; }
void setTextColor(QColor color) { mTextColor = color; }
+ static constexpr int PGP_ITEM_MODEL_COLUMN_CHECK = 0;
+ static constexpr int PGP_ITEM_MODEL_COLUMN_PEERNAME = 1;
+ static constexpr int PGP_ITEM_MODEL_COLUMN_I_AUTH_PEER = 2;
+ static constexpr int PGP_ITEM_MODEL_COLUMN_PEER_AUTH_ME = 3;
+ static constexpr int PGP_ITEM_MODEL_COLUMN_PEERID = 4;
+ static constexpr int PGP_ITEM_MODEL_COLUMN_LAST_USED = 5;
+ static constexpr int PGP_ITEM_MODEL_COLUMN_COUNT = 6;
public slots:
void data_updated(std::list &new_neighs);
diff --git a/retroshare-gui/src/gui/NetworkDialog/pgpid_item_proxy.cpp b/retroshare-gui/src/gui/NetworkDialog/pgpid_item_proxy.cpp
index 29acee273..e7b81ef2b 100644
--- a/retroshare-gui/src/gui/NetworkDialog/pgpid_item_proxy.cpp
+++ b/retroshare-gui/src/gui/NetworkDialog/pgpid_item_proxy.cpp
@@ -26,17 +26,13 @@
#include
#include
-
-//TODO: set this defines in one place
-// Defines for key list columns
-#define COLUMN_CHECK 0
-#define COLUMN_PEERNAME 1
-#define COLUMN_I_AUTH_PEER 2
-#define COLUMN_PEER_AUTH_ME 3
-#define COLUMN_PEERID 4
-#define COLUMN_LAST_USED 5
-#define COLUMN_COUNT 6
-
+bool pgpid_item_proxy::lessThan(const QModelIndex &left, const QModelIndex &right) const
+{
+ if(left.column() == pgpid_item_model::PGP_ITEM_MODEL_COLUMN_LAST_USED)
+ return left.data(Qt::EditRole).toUInt() < right.data(Qt::EditRole).toUInt();
+ else
+ return left.data(Qt::DisplayRole).toString().toUpper() < right.data(Qt::DisplayRole).toString().toUpper();
+}
pgpid_item_proxy::pgpid_item_proxy(QObject *parent) :
@@ -57,7 +53,7 @@ bool pgpid_item_proxy::filterAcceptsRow(int sourceRow, const QModelIndex &source
{
if(!rsPeers)
return false;
- RsPgpId peer_id (sourceModel()->data(sourceModel()->index(sourceRow, COLUMN_PEERID, sourceParent)).toString().toStdString());
+ RsPgpId peer_id (sourceModel()->data(sourceModel()->index(sourceRow, pgpid_item_model::PGP_ITEM_MODEL_COLUMN_PEERID, sourceParent)).toString().toStdString());
RsPeerDetails details;
if(!rsPeers->getGPGDetails(peer_id, details))
return false;
diff --git a/retroshare-gui/src/gui/NetworkDialog/pgpid_item_proxy.h b/retroshare-gui/src/gui/NetworkDialog/pgpid_item_proxy.h
index 0260b815d..5608862a2 100644
--- a/retroshare-gui/src/gui/NetworkDialog/pgpid_item_proxy.h
+++ b/retroshare-gui/src/gui/NetworkDialog/pgpid_item_proxy.h
@@ -34,14 +34,7 @@ class pgpid_item_proxy :
public:
pgpid_item_proxy(QObject *parent = nullptr);
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
-
- bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
- {
- if(left.column() == COLUMN_LAST_USED)
- return left.data(Qt::EditRole).toUInt() < right.data(Qt::EditRole).toUInt();
- else
- return left.data(Qt::DisplayRole).toString().toUpper() < right.data(Qt::DisplayRole).toString().toUpper();
- }
+ bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
public slots:
void use_only_trusted_keys(bool val);
diff --git a/retroshare-gui/src/gui/NewsFeed.cpp b/retroshare-gui/src/gui/NewsFeed.cpp
index d86ba2d7a..dc0834e71 100644
--- a/retroshare-gui/src/gui/NewsFeed.cpp
+++ b/retroshare-gui/src/gui/NewsFeed.cpp
@@ -32,6 +32,7 @@
#include
#include
+#include "util/misc.h"
#include "util/qtthreadsutils.h"
#include "feeds/BoardsCommentsItem.h"
#include "feeds/ChatMsgItem.h"
@@ -111,22 +112,23 @@ NewsFeed::NewsFeed(QWidget *parent) : MainPage(parent), ui(new Ui::NewsFeed),
connect(ui->feedOptionsButton, SIGNAL(clicked()), this, SLOT(feedoptions()));
ui->feedOptionsButton->hide(); // (csoler) Hidden until we repare the system to display a specific settings page.
-QString hlp_str = tr(
- "
Activity Feed
\
-
The Activity Feed displays the last events on your network, sorted by the time you received them. \
- This gives you a summary of the activity of your friends. \
- You can configure which events to show by pressing on Options.
\
-
The various events shown are: \
-
\
-
Connection attempts (useful to make friends with new people and control who's trying to reach you)
\
-
Channel, Forum and Board posts
\
-
Circle membership requests and invites
\
-
New Channels, Forums and Boards you can subscribe to
\
-
Channel and Board comments
\
-
New Mail messages
\
-
Private messages from your friends
\
-
\
- ") ;
+ int H = misc::getFontSizeFactor("HelpButton").height();
+ QString hlp_str = tr(
+ "
Activity Feed
"
+ "
The Activity Feed displays the last events on your network, sorted by the time you received them."
+ " This gives you a summary of the activity of your friends."
+ " You can configure which events to show by pressing on Options.
"
+ "
The various events shown are:"
+ "
"
+ "
Connection attempts (useful to make friends with new people and control who's trying to reach you)
"
+ "
Channel, Forum and Board posts
"
+ "
Circle membership requests and invites
"
+ "
New Channels, Forums and Boards you can subscribe to
Channels allow you to post data (e.g. movies, music) that will spread in the network
\
-
You can see the channels your friends are subscribed to, and you automatically forward subscribed channels to \
- your friends. This promotes good channels in the network.
\
-
Only the channel's creator can post on that channel. Other peers \
- in the network can only read from it, unless the channel is private. You can however share \
- the posting rights or the reading rights with friend Retroshare nodes.
\
-
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));
+ int H = misc::getFontSizeFactor("HelpButton").height();
+
+ QString hlp_str = tr(
+ "
Channels
"
+ "
Channels allow you to post data (e.g. movies, music) that will spread in the network
"
+ "
You can see the channels your friends are subscribed to, and you automatically forward subscribed channels to"
+ " your friends. This promotes good channels in the network.
"
+ "
Only the channel's creator can post on that channel. Other peers"
+ " in the network can only read from it, unless the channel is private. You can however share"
+ " the posting rights or the reading rights with friend Retroshare nodes.
"
+ "
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 %2 days, and sync-ed over the last %3 days, unless you change this.
"
+ "
UI Tip: use Control + mouse wheel to control image size in the thumbnail view.
"
+ ).arg( QString::number(2*H)
+ , QString::number(rsGxsChannels->getDefaultStoragePeriod()/86400)
+ , QString::number(rsGxsChannels->getDefaultSyncPeriod()/86400));
return hlp_str ;
}
@@ -260,11 +266,13 @@ void GxsChannelDialog::groupTreeCustomActions(RsGxsGroupId grpId, int subscribeF
if (isSubscribed)
{
- 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));
+ {
+ 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);
+ connect(action, SIGNAL(triggered()), this, SLOT(toggleAutoDownload()));
+ actions.append(action);
+ }
std::string dl_directory;
rsGxsChannels->getChannelDownloadDirectory(grpId,dl_directory) ;
@@ -282,20 +290,20 @@ void GxsChannelDialog::groupTreeCustomActions(RsGxsGroupId grpId, int subscribeF
for(std::list::const_iterator it(lst.begin());it!=lst.end();++it)
{
- QAction *action = NULL;
+ QAction *fileAction = NULL;
if(dl_directory == it->filename)
{
- action = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/start.png"),QString::fromUtf8(it->filename.c_str()),NULL) ;
+ fileAction = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/start.png"),QString::fromUtf8(it->filename.c_str()),NULL) ;
found = true ;
}
else
- action = new QAction(QString::fromUtf8(it->filename.c_str()),NULL) ;
+ fileAction = new QAction(QString::fromUtf8(it->filename.c_str()),NULL) ;
- connect(action,SIGNAL(triggered()),this,SLOT(setDownloadDirectory())) ;
- action->setData(QString::fromUtf8(it->filename.c_str())) ;
+ connect(fileAction,SIGNAL(triggered()),this,SLOT(setDownloadDirectory())) ;
+ fileAction->setData(QString::fromUtf8(it->filename.c_str())) ;
- mnu->addAction(action) ;
+ mnu->addAction(fileAction) ;
}
if(!found && !dl_directory.empty())
@@ -361,24 +369,42 @@ void GxsChannelDialog::toggleAutoDownload()
bool GxsChannelDialog::getGroupStatistics(const RsGxsGroupId& groupId,GxsGroupStatistic& stat)
{
- return rsGxsChannels->getChannelStatistics(groupId,stat);
+ // What follows is a hack to replace the GXS group statistics by the actual count of unread messages in channels,
+ // which should take into account old post versions, discard comments and votes, etc.
+
+ RsGxsChannelStatistics s;
+ bool res = rsGxsChannels->getChannelStatistics(groupId,s);
+
+ if(!res)
+ return false;
+
+ stat.mGrpId = groupId;
+ stat.mNumMsgs = s.mNumberOfPosts;
+
+ stat.mTotalSizeOfMsgs = 0; // hopefuly unused. Required the loading of the full channel data, so not very convenient.
+ stat.mNumThreadMsgsNew = s.mNumberOfNewPosts;
+ stat.mNumThreadMsgsUnread = s.mNumberOfUnreadPosts;
+ stat.mNumChildMsgsNew = 0;
+ stat.mNumChildMsgsUnread = 0;
+
+ return true;
}
bool GxsChannelDialog::getGroupData(std::list& groupInfo)
{
std::vector groups;
- // request all group infos at once
+ // request all group infos at once
- if(! rsGxsChannels->getChannelsInfo(std::list(),groups))
- return false;
+ if(! rsGxsChannels->getChannelsInfo(std::list(),groups))
+ return false;
- /* Save groups to fill icons and description */
+ /* Save groups to fill icons and description */
for (auto& group: groups)
- groupInfo.push_back(new RsGxsChannelGroup(group));
+ groupInfo.push_back(new RsGxsChannelGroup(group));
- return true;
+ return true;
}
void GxsChannelDialog::groupInfoToGroupItemInfo(const RsGxsGenericGroupData *groupData, GroupItemInfo &groupItemInfo)
diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.cpp
index 365dab3f3..23f5f5b93 100644
--- a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.cpp
+++ b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.cpp
@@ -33,8 +33,8 @@
#include "retroshare/rsfiles.h"
-GxsChannelFilesStatusWidget::GxsChannelFilesStatusWidget(const RsGxsFile &file, QWidget *parent) :
- QWidget(parent), mFile(file), ui(new Ui::GxsChannelFilesStatusWidget)
+GxsChannelFilesStatusWidget::GxsChannelFilesStatusWidget(const RsGxsFile &file, QWidget *parent,bool used_as_editor) :
+ QWidget(parent), mFile(file), mUsedAsEditor(used_as_editor),ui(new Ui::GxsChannelFilesStatusWidget)
{
ui->setupUi(this);
@@ -46,19 +46,16 @@ GxsChannelFilesStatusWidget::GxsChannelFilesStatusWidget(const RsGxsFile &file,
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->cancelToolButton, SIGNAL(clicked()), this, SLOT(cancel()));
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()));
+ ui->openFolderPushButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/folderopen.png"));
+ ui->openFolderPushButton->setToolTip(tr("Open folder"));
+
+ connect(ui->openFolderPushButton, SIGNAL(clicked()), this, SLOT(openFolder()));
- QMenu *menu = new QMenu();
- menu->addAction(openfolder);
- ui->openFolderToolButton->setMenu(menu);
-
check();
}
@@ -90,7 +87,9 @@ void GxsChannelFilesStatusWidget::setSize(uint64_t size)
void GxsChannelFilesStatusWidget::check()
{
FileInfo fileInfo;
- if (rsFiles->alreadyHaveFile(mFile.mHash, fileInfo)) {
+
+ if(haveFile(fileInfo))
+ {
mState = STATE_LOCAL;
setSize(fileInfo.size);
@@ -103,27 +102,25 @@ void GxsChannelFilesStatusWidget::check()
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);
-
- if (detailsOk) {
- switch (fileInfo.downloadStatus) {
+ }
+ else
+ {
+ switch (fileInfo.downloadStatus)
+ {
case FT_STATE_WAITING:
mState = STATE_WAITING;
break;
case FT_STATE_DOWNLOADING:
- if (fileInfo.avail == fileInfo.size) {
+ if (fileInfo.avail == fileInfo.size)
mState = STATE_LOCAL;
- } else {
+ else
mState = STATE_DOWNLOAD;
- }
+
setSize(fileInfo.size);
ui->progressBar->setValue(fileInfo.avail / mDivisor);
break;
- case FT_STATE_COMPLETE:
- mState = STATE_DOWNLOAD;
+ case FT_STATE_COMPLETE: // this should not happen, since the case is handled earlier
+ mState = STATE_ERROR;
break;
case FT_STATE_QUEUED:
mState = STATE_WAITING;
@@ -134,14 +131,11 @@ void GxsChannelFilesStatusWidget::check()
case FT_STATE_CHECKING_HASH:
mState = STATE_CHECKING;
break;
- case FT_STATE_FAILED:
- mState = STATE_ERROR;
- break;
- }
- } else {
- mState = STATE_REMOTE;
- }
- }
+ default:
+ mState = STATE_REMOTE;
+ break;
+ }
+ }
int repeat = 0;
QString statusText;
@@ -156,7 +150,7 @@ void GxsChannelFilesStatusWidget::check()
ui->cancelToolButton->hide();
ui->progressBar->hide();
ui->openFilePushButton->hide();
- ui->openFolderToolButton->hide();
+ ui->openFolderPushButton->hide();
statusText = tr("Error");
@@ -171,7 +165,7 @@ void GxsChannelFilesStatusWidget::check()
ui->cancelToolButton->hide();
ui->progressBar->hide();
ui->openFilePushButton->hide();
- ui->openFolderToolButton->hide();
+ ui->openFolderPushButton->hide();
break;
@@ -184,7 +178,7 @@ void GxsChannelFilesStatusWidget::check()
ui->cancelToolButton->show();
ui->progressBar->show();
ui->openFilePushButton->hide();
- ui->openFolderToolButton->hide();
+ ui->openFolderPushButton->hide();
break;
@@ -197,7 +191,7 @@ void GxsChannelFilesStatusWidget::check()
ui->cancelToolButton->show();
ui->progressBar->hide();
ui->openFilePushButton->hide();
- ui->openFolderToolButton->hide();
+ ui->openFolderPushButton->hide();
statusText = tr("Paused");
@@ -212,7 +206,7 @@ void GxsChannelFilesStatusWidget::check()
ui->cancelToolButton->show();
ui->progressBar->hide();
ui->openFilePushButton->hide();
- ui->openFolderToolButton->hide();
+ ui->openFolderPushButton->hide();
statusText = tr("Waiting");
@@ -227,7 +221,7 @@ void GxsChannelFilesStatusWidget::check()
ui->cancelToolButton->show();
ui->progressBar->hide();
ui->openFilePushButton->hide();
- ui->openFolderToolButton->hide();
+ ui->openFolderPushButton->hide();
statusText = tr("Checking");
@@ -242,7 +236,7 @@ void GxsChannelFilesStatusWidget::check()
ui->cancelToolButton->hide();
ui->progressBar->hide();
ui->openFilePushButton->show();
- ui->openFolderToolButton->show();
+ ui->openFolderPushButton->show();
break;
}
@@ -306,49 +300,87 @@ void GxsChannelFilesStatusWidget::resume()
void GxsChannelFilesStatusWidget::cancel()
{
- if ((QMessageBox::question(this, "", tr("Are you sure that you want to cancel and delete the file?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No)) == QMessageBox::No) {
- return;
- }
+ // When QMessgeBox asks for cancel confirmtion, this makes the widget lose focus => since it is an editor widget,
+ // it gets destroyed by the parent list widget => subsequent code after the QMessageBox runs over a deleted object => crash
+ // In summary: no QMessageBox here when the Status widget is used as an editor.
+
+ if(!mUsedAsEditor)
+ if ((QMessageBox::question(this, "", tr("Are you sure that you want to cancel and delete the file?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No)) == QMessageBox::No) {
+ return;
+ }
rsFiles->FileCancel(mFile.mHash);
emit onButtonClick();// Signals the parent widget to e.g. update the downloadable file count
- check();
+ check();
}
void GxsChannelFilesStatusWidget::openFolder()
{
FileInfo fileInfo;
- if (!rsFiles->alreadyHaveFile(mFile.mHash, fileInfo)) {
+ if (!haveFile(fileInfo))
return;
- }
- /* open folder with a suitable application */
- QDir dir = QFileInfo(QString::fromUtf8(fileInfo.path.c_str())).absoluteDir();
- if (dir.exists()) {
- if (!RsUrlHandler::openUrl(QUrl::fromLocalFile(dir.absolutePath()))) {
- QMessageBox::warning(this, "", QString("%1 %2").arg(tr("Can't open folder"), dir.absolutePath()));
- }
- }
+ QFileInfo finfo;
+ finfo.setFile(QString::fromUtf8(fileInfo.path.c_str()));
+
+ /* open folder with a suitable application */
+ if (!RsUrlHandler::openUrl(QUrl::fromLocalFile(finfo.absolutePath()))) {
+ if(!mUsedAsEditor)
+ QMessageBox::warning(this, "", QString("%1 %2").arg(tr("Can't open folder"), finfo.absolutePath()));
+ else
+ RsErr() << "Can't open folder " << finfo.absolutePath().toStdString() ;
+ }
+}
+
+bool GxsChannelFilesStatusWidget::haveFile(FileInfo& info)
+{
+ bool already_has_file = rsFiles->alreadyHaveFile(mFile.mHash, info);
+
+ if(!already_has_file)
+ if(!(rsFiles->FileDetails(mFile.mHash, RS_FILE_HINTS_DOWNLOAD | RS_FILE_HINTS_SPEC_ONLY, info) && info.downloadStatus==FT_STATE_COMPLETE))
+ return false;
+
+ // We need the code below because FileDetails() returns fileInfo.path as the directory when the file in COMPLETE and
+ // as a full path when the file is shared. The former is inconsistent with the documentation in rstypes.h, but I'm not
+ // sure what are the implications of changing the code in libretroshare so that the full path is always returned.
+
+ QFileInfo finfo;
+
+ if(QDir(QString::fromUtf8(info.path.c_str())).exists())
+ finfo.setFile(QString::fromUtf8(info.path.c_str()),QString::fromUtf8(info.fname.c_str()));
+ else if(QFile(QString::fromUtf8(info.path.c_str())).exists())
+ finfo.setFile(QString::fromUtf8(info.path.c_str()));
+ else
+ {
+ RsErr() << "Cannot find file!" << std::endl;
+ return false;
+ }
+
+ info.path = finfo.absoluteFilePath().toStdString();
+ return true;
}
void GxsChannelFilesStatusWidget::openFile()
{
FileInfo fileInfo;
- if (!rsFiles->alreadyHaveFile(mFile.mHash, fileInfo)) {
+ if(!haveFile(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()))) {
+ QFileInfo finfo;
+ finfo.setFile(QString::fromUtf8(fileInfo.path.c_str()));
+
+ if (finfo.exists()) {
+ if (!RsUrlHandler::openUrl(QUrl::fromLocalFile(finfo.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;
+ if(!mUsedAsEditor)
+ QMessageBox::information(this, tr("Play File"),
+ tr("File %1 does not exist at location.").arg(fileInfo.path.c_str()));
+ else
+ RsErr() << "File " << fileInfo.path << " does not exist at location." ;
+
+ return;
}
}
diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.h b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.h
index 8f2f7f8e4..0dbb5c072 100644
--- a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.h
+++ b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.h
@@ -34,9 +34,10 @@ class GxsChannelFilesStatusWidget : public QWidget
Q_OBJECT
public:
- explicit GxsChannelFilesStatusWidget(const RsGxsFile &file, QWidget *parent = 0);
+ explicit GxsChannelFilesStatusWidget(const RsGxsFile &file, QWidget *parent = 0,bool used_as_editor=false);
~GxsChannelFilesStatusWidget();
+ bool usedAsEditor() const { return mUsedAsEditor; }
signals:
void onButtonClick();
@@ -52,6 +53,7 @@ private slots:
private:
void setSize(uint64_t size);
+ bool haveFile(FileInfo& info);
private:
enum State
@@ -73,6 +75,8 @@ private:
uint64_t mSize;
uint64_t mDivisor;
+ bool mUsedAsEditor;
+
Ui::GxsChannelFilesStatusWidget *ui;
};
diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.ui
index 463b05423..e8ee2d512 100644
--- a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.ui
+++ b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.ui
@@ -6,8 +6,8 @@
00
- 421
- 29
+ 473
+ 36
@@ -120,8 +120,8 @@
Qt::NoFocus
-
- :/images/cancel.png:/images/cancel.png
+
+ :/icons/cancel.svg:/icons/cancel.svg
@@ -139,7 +139,7 @@
-
+ 0
diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelGroupDialog.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelGroupDialog.cpp
index a98e9016a..0af2631c5 100644
--- a/retroshare-gui/src/gui/gxschannels/GxsChannelGroupDialog.cpp
+++ b/retroshare-gui/src/gui/gxschannels/GxsChannelGroupDialog.cpp
@@ -21,10 +21,11 @@
#include
#include "gui/gxs/GxsIdDetails.h"
+#include "util/imageutil.h"
#include "gui/common/FilesDefs.h"
#include "GxsChannelGroupDialog.h"
-#include
+#include "retroshare/rsgxschannels.h"
#include
// To start with we only have open forums - with distribution controls.
@@ -119,8 +120,10 @@ void GxsChannelGroupDialog::prepareChannelGroup(RsGxsChannelGroup &group, const
QByteArray ba;
QBuffer buffer(&ba);
- buffer.open(QIODevice::WriteOnly);
- pixmap.save(&buffer, "PNG"); // writes image into ba in PNG format
+ bool has_transparency = ImageUtil::hasAlphaContent(pixmap.toImage());
+
+ buffer.open(QIODevice::WriteOnly);
+ pixmap.save(&buffer, has_transparency?"PNG":"JPG"); // writes image into ba in PNG format
group.mImage.copy((uint8_t *) ba.data(), ba.size());
} else {
diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp
index be2f288ac..806fcaa69 100644
--- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp
+++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp
@@ -33,11 +33,11 @@
#include "GxsChannelPostFilesModel.h"
-//#define DEBUG_CHANNEL_MODEL
+//#define DEBUG_CHANNEL_FILES_MODEL
Q_DECLARE_METATYPE(ChannelPostFileInfo)
-#ifdef DEBUG_CHANNEL_MODEL
+#ifdef DEBUG_CHANNEL_FILES_MODEL
static std::ostream& operator<<(std::ostream& o, const QModelIndex& i);// defined elsewhere
#endif
@@ -161,8 +161,8 @@ QModelIndex RsGxsChannelPostFilesModel::index(int row, int column, const QModelI
quintptr ref = getChildRef(parent.internalId(),row);
-#ifdef DEBUG_CHANNEL_MODEL
- std::cerr << "index-3(" << row << "," << column << " parent=" << parent << ") : " << createIndex(row,column,ref) << std::endl;
+#ifdef DEBUG_CHANNEL_FILES_MODEL
+ RsDbg() << "index-3(" << row << "," << column << " parent=" << parent << ") : " << createIndex(row,column,ref) ;
#endif
return createIndex(row,column,ref) ;
}
@@ -242,7 +242,7 @@ QVariant RsGxsChannelPostFilesModel::headerData(int section, Qt::Orientation /*o
QVariant RsGxsChannelPostFilesModel::data(const QModelIndex &index, int role) const
{
-#ifdef DEBUG_CHANNEL_MODEL
+#ifdef DEBUG_CHANNEL_FILES_MODEL
std::cerr << "calling data(" << index << ") role=" << role << std::endl;
#endif
@@ -259,13 +259,13 @@ QVariant RsGxsChannelPostFilesModel::data(const QModelIndex &index, int role) co
quintptr ref = (index.isValid())?index.internalId():0 ;
uint32_t entry = 0;
-#ifdef DEBUG_CHANNEL_MODEL
+#ifdef DEBUG_CHANNEL_FILES_MODEL
std::cerr << "data(" << index << ")" ;
#endif
if(!ref)
{
-#ifdef DEBUG_CHANNEL_MODEL
+#ifdef DEBUG_CHANNEL_FILES_MODEL
std::cerr << " [empty]" << std::endl;
#endif
return QVariant() ;
@@ -273,7 +273,7 @@ QVariant RsGxsChannelPostFilesModel::data(const QModelIndex &index, int role) co
if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFilteredFiles.size())
{
-#ifdef DEBUG_CHANNEL_MODEL
+#ifdef DEBUG_CHANNEL_FILES_MODEL
std::cerr << "Bad pointer: " << (void*)ref << std::endl;
#endif
return QVariant() ;
@@ -295,7 +295,7 @@ void RsGxsChannelPostFilesModel::setFilter(const QStringList& strings, uint32_t&
{
preMods();
- initEmptyHierarchy();
+ mFilteredFiles.clear();
if(strings.empty())
{
@@ -317,8 +317,6 @@ void RsGxsChannelPostFilesModel::setFilter(const QStringList& strings, uint32_t&
}
count = mFilteredFiles.size();
- std::cerr << "After filtering: " << count << " posts remain." << std::endl;
-
if (rowCount()>0)
{
beginInsertRows(QModelIndex(),0,rowCount()-1);
@@ -344,10 +342,20 @@ public:
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);
+ bool r1 = rsFiles->FileDetails(f1.mHash,RS_FILE_HINTS_DOWNLOAD,fi1);
+ bool r2 = rsFiles->FileDetails(f2.mHash,RS_FILE_HINTS_DOWNLOAD,fi2);
- return (ord==Qt::AscendingOrder)?(fi1.transferedfi2.transfered);
+ if(r1 && r2)
+ return (ord==Qt::AscendingOrder)?(fi1.transferedfi2.transfered);
+ else
+ {
+ FileInfo fitmp;
+
+ if(!r1 && rsFiles->alreadyHaveFile(f1.mHash,fitmp)) fi1.downloadStatus = FT_STATE_COMPLETE;
+ if(!r2 && rsFiles->alreadyHaveFile(f2.mHash,fitmp)) fi2.downloadStatus = FT_STATE_COMPLETE;
+
+ return (ord==Qt::AscendingOrder)?(fi1.downloadStatusfi2.downloadStatus);
+ }
}
}
@@ -428,7 +436,6 @@ QVariant RsGxsChannelPostFilesModel::userRole(const ChannelPostFileInfo& fmpe,in
void RsGxsChannelPostFilesModel::clear()
{
-
initEmptyHierarchy();
emit channelLoaded();
@@ -446,7 +453,7 @@ void RsGxsChannelPostFilesModel::setFiles(const std::list &
for(uint32_t i=0;i &
postMods();
}
+
+void RsGxsChannelPostFilesModel::update_files(std::set& added_files,std::set& removed_files)
+{
+ // 1 - remove common files from both lists
+
+#ifdef DEBUG_CHANNEL_FILES_MODEL
+ RsDbg() << "RsGxsChannelPostsFilesModel:: updating files." ;
+#endif
+
+ for(auto afit=added_files.begin();afit!=added_files.end();)
+ {
+ auto rfit = removed_files.find(*afit);
+
+ if(rfit != removed_files.end())
+ {
+#ifdef DEBUG_CHANNEL_FILES_MODEL
+ RsDbg() << " Eliminating common file " << rfit->mName ;
+#endif
+ removed_files.erase(rfit);
+ auto tmp = afit;
+ ++tmp;
+ added_files.erase(afit);
+ afit = tmp;
+ }
+ else
+ ++afit;
+ }
+#ifdef DEBUG_CHANNEL_FILES_MODEL
+ RsDbg() << " Remains: " << added_files.size() << " added files and " << removed_files.size() << " removed files" ;
+#endif
+
+ // 2 - add whatever file remains,
+
+ for(const auto& f:removed_files)
+ {
+#ifdef DEBUG_CHANNEL_FILES_MODEL
+ RsDbg() << " Removing deleted file " << f.mName ;
+#endif
+
+ for(uint32_t i=0;i& files);
+ void setFiles(const std::list& files);
void setFilter(const QStringList &strings, uint32_t &count) ;
+ // This method adds/removes the given lists of files. Useful when a single post is updated
+ void update_files(std::set &added_files, std::set &removed_files);
+
#ifdef TODO
- QModelIndex getIndexOfFile(const RsFileHash& hash) const;
+ 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);
+ 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
@@ -112,25 +115,25 @@ public:
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;
+ 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;
+ 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;
+ 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
/*!
@@ -151,20 +154,20 @@ private:
bool mFilteringEnabled;
SortMode mSortMode;
#endif
- void preMods() ;
- void postMods() ;
+ 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 bool convertRefPointerToTabEntry(quintptr ref,uint32_t& entry);
#ifdef TODO
- static void generateMissingItem(const RsGxsMessageId &msgId,ChannelPostsModelPostEntry& entry);
+ static void generateMissingItem(const RsGxsMessageId &msgId,ChannelPostsModelPostEntry& entry);
#endif
- void initEmptyHierarchy();
+ void initEmptyHierarchy();
std::vector mFilteredFiles ; // store the list of files for the post
std::vector mFiles ; // store the list of files for the post
diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.cpp
index ee40e1465..16cf96195 100644
--- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.cpp
+++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.cpp
@@ -350,7 +350,7 @@ void ZoomableLabel::updateView()
std::cerr << " Image size: " << mFullImage.width() << " x " << mFullImage.height() << ", window size: " << width() << " x " << height() << std::endl;
std::cerr << " cropped image: " << rect.left() << "," << rect.top() << "+" << rect.width() << "+" << rect.height() << std::endl;
std::cerr << " saving crop to pix2.png" << std::endl;
- mFullImage.copy(rect).save("pix2.png","PNG");
+ mFullImage.copy(rect).save("pix2.png","JPG");
#endif
QLabel::setPixmap(mFullImage.copy(rect));
}
diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp
index 31eef8242..8b5b7e707 100644
--- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp
+++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp
@@ -27,6 +27,8 @@
#include "retroshare/rsgxschannels.h"
#include "retroshare/rsexpr.h"
+#include "gui/MainWindow.h"
+#include "gui/mainpagestack.h"
#include "gui/common/FilesDefs.h"
#include "util/qtthreadsutils.h"
#include "util/HandleRichText.h"
@@ -35,10 +37,10 @@
#include "GxsChannelPostsModel.h"
#include "GxsChannelPostFilesModel.h"
+//#define DEBUG_CHANNEL_MODEL_DATA
//#define DEBUG_CHANNEL_MODEL
Q_DECLARE_METATYPE(RsMsgMetaData)
-
Q_DECLARE_METATYPE(RsGxsChannelPost)
std::ostream& operator<<(std::ostream& o, const QModelIndex& i);// defined elsewhere
@@ -47,14 +49,6 @@ 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()
@@ -69,137 +63,10 @@ void RsGxsChannelPostsModel::setMode(TreeMode mode)
if(mode == TREE_MODE_LIST)
setNumColumns(2);
- triggerViewUpdate();
-}
-
-void updateCommentCounts( std::vector& posts, std::vector& comments)
-{
- // Store posts IDs in a std::map to avoid a quadratic cost
-
- std::map post_indices;
-
- for(uint32_t i=0;isecond>=posts.size() is impossible by construction, since post_indices
- // is previously filled using posts ids.
-
- if(it == post_indices.end())
- continue;
-
- ++posts[it->second].mCommentCount;
-
- if(IS_MSG_NEW(comments[i].mMeta.mMsgStatus))
- ++posts[it->second].mUnreadCommentCount;
- }
+ triggerViewUpdate(true,true);
}
-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!
-
- // make a copy of e, so as to avoid destruction of the shared pointer during async thread execution, since [e] doesn't actually tell
- // the original shared_ptr that it is copied! So no counter is updated in event, which will be destroyed (as e will be) during or even before
- // the execution of the lambda.
-
- RsGxsChannelEvent E(*e);
-
- if(E.mChannelGroupId == mChannelGroup.mMeta.mGroupId)
- RsThread::async([this, E]()
- {
- // 1 - get message data from p3GxsChannels. No need for pointers here, because we send only a single post to postToObject()
- // At this point we dont know what kind of msg id we have. It can be a vote, a comment or an actual message.
-
- std::vector posts;
- std::vector comments;
- std::vector votes;
- std::set msg_ids{ E.mChannelMsgId };
-
- if(!rsGxsChannels->getChannelContent(E.mChannelGroupId,msg_ids, posts,comments,votes))
- {
- std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve channel message data for channel/msg " << E.mChannelGroupId << "/" << E.mChannelMsgId << std::endl;
- return;
- }
-
- // Check if what we have actually is a comment or a vote. If so we need to update the actual message they refer to
-
- if(posts.empty()) // means we have a comment or a vote
- {
- msg_ids.clear();
-
- for(auto c:comments) msg_ids.insert(c.mMeta.mThreadId);
- for(auto v:votes ) msg_ids.insert(v.mMeta.mThreadId);
-
- comments.clear();
- votes.clear();
-
- if(!rsGxsChannels->getChannelContent(E.mChannelGroupId,msg_ids,posts,comments,votes))
- {
- std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve channel message data for channel/msg " << E.mChannelGroupId << "/" << E.mChannelMsgId << std::endl;
- return;
- }
- }
-
- // Need to call this in order to get the actuall comment count. The previous call only retrieves the message, since we supplied the message ID.
- // another way to go would be to save the comment ids of the existing message and re-insert them before calling getChannelContent.
-
- if(!rsGxsChannels->getChannelComments(E.mChannelGroupId,msg_ids,comments))
- {
- std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve message comment data for channel/msg " << E.mChannelGroupId << "/" << E.mChannelMsgId << std::endl;
- return;
- }
-
- updateCommentCounts(posts,comments);
-
- // 2 - update the model in the UI thread.
-
- RsQThreadUtils::postToObject( [posts,this]()
- {
- for(uint32_t i=0;i& files)
@@ -241,29 +111,39 @@ void RsGxsChannelPostsModel::getFilesList(std::list& files)
files.push_back(it.second);
}
+bool RsGxsChannelPostsModel::postPassesFilter(const RsGxsChannelPost& post,const QStringList& strings,bool only_unread) const
+{
+ bool passes_strings = true;
+
+ for(auto& s:strings)
+ passes_strings = passes_strings && QString::fromStdString(post.mMeta.mMsgName).contains(s,Qt::CaseInsensitive);
+
+ if(strings.empty())
+ passes_strings = true;
+
+ if(passes_strings && (!only_unread || (IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus))))
+ return true;
+
+ return false;
+}
+
void RsGxsChannelPostsModel::setFilter(const QStringList& strings,bool only_unread, uint32_t& count)
+{
+ mFilteredStrings = strings;
+ mFilterUnread = only_unread;
+
+ updateFilter(count);
+}
+
+void RsGxsChannelPostsModel::updateFilter(uint32_t& count)
{
preMods();
- beginResetModel();
-
mFilteredPosts.clear();
- //mFilteredPosts.push_back(0);
- endResetModel();
for(size_t i=0;i 0)
+ return r;
+ else
+ return columnCount();
+ }
+ else
+ return columnCount();
+ }
+ else
+ return 2;
+}
int RsGxsChannelPostsModel::columnCount(const QModelIndex &/*parent*/) const
{
if(mTreeMode == TREE_MODE_GRID)
@@ -365,12 +264,12 @@ bool RsGxsChannelPostsModel::convertRefPointerToTabEntry(quintptr ref, uint32_t&
QModelIndex RsGxsChannelPostsModel::index(int row, int column, const QModelIndex & parent) const
{
- if(row < 0 || column < 0 || column >= (int)mColumns)
+ if(row < 0 || column < 0 || row >= rowCount() || column >= columnCount(row))
return QModelIndex();
quintptr ref = getChildRef(parent.internalId(),(mTreeMode == TREE_MODE_GRID)?(column + row*mColumns):row);
-#ifdef DEBUG_CHANNEL_MODEL
+#ifdef DEBUG_CHANNEL_MODEL_DATA
std::cerr << "index-3(" << row << "," << column << " parent=" << parent << ") : " << createIndex(row,column,ref) << std::endl;
#endif
return createIndex(row,column,ref) ;
@@ -401,17 +300,8 @@ bool RsGxsChannelPostsModel::setNumColumns(int n)
preMods();
- beginResetModel();
- endResetModel();
-
mColumns = n;
- if (rowCount()>0)
- {
- beginInsertRows(QModelIndex(),0,rowCount()-1);
- endInsertRows();
- }
-
postMods();
return true;
@@ -460,7 +350,7 @@ int RsGxsChannelPostsModel::getChildrenCount(quintptr ref) const
QVariant RsGxsChannelPostsModel::data(const QModelIndex &index, int role) const
{
-#ifdef DEBUG_CHANNEL_MODEL
+#ifdef DEBUG_CHANNEL_MODEL_DATA
std::cerr << "calling data(" << index << ") role=" << role << std::endl;
#endif
@@ -477,13 +367,13 @@ QVariant RsGxsChannelPostsModel::data(const QModelIndex &index, int role) const
quintptr ref = (index.isValid())?index.internalId():0 ;
uint32_t entry = 0;
-#ifdef DEBUG_CHANNEL_MODEL
+#ifdef DEBUG_CHANNEL_MODEL_DATA
std::cerr << "data(" << index << ")" ;
#endif
if(!ref)
{
-#ifdef DEBUG_CHANNEL_MODEL
+#ifdef DEBUG_CHANNEL_MODEL_DATA
std::cerr << " [empty]" << std::endl;
#endif
return QVariant() ;
@@ -491,7 +381,7 @@ QVariant RsGxsChannelPostsModel::data(const QModelIndex &index, int role) const
if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFilteredPosts.size())
{
-#ifdef DEBUG_CHANNEL_MODEL
+#ifdef DEBUG_CHANNEL_MODEL_DATA
std::cerr << "Bad pointer: " << (void*)ref << std::endl;
#endif
return QVariant() ;
@@ -540,7 +430,6 @@ const RsGxsGroupId& RsGxsChannelPostsModel::currentGroupId() const
{
return mChannelGroup.mMeta.mGroupId;
}
-
void RsGxsChannelPostsModel::updateChannel(const RsGxsGroupId& channel_group_id)
{
if(channel_group_id.isNull())
@@ -564,6 +453,79 @@ bool operator<(const RsGxsChannelPost& p1,const RsGxsChannelPost& p2)
return p1.mMeta.mPublishTs > p2.mMeta.mPublishTs;
}
+void RsGxsChannelPostsModel::updateSinglePost(const RsGxsChannelPost& post,std::set& added_files,std::set& removed_files)
+{
+#ifdef DEBUG_CHANNEL_MODEL
+ RsDbg() << "updating single post for group id=" << currentGroupId() << " and msg id=" << post.mMeta.mMsgId ;
+#endif
+ added_files.clear();
+ removed_files.clear();
+
+ emit layoutAboutToBeChanged();
+
+ // linear search. Not good at all, but normally this is just for a single post.
+
+ bool found = false;
+ const auto& new_post_meta(post.mMeta);
+
+ for(uint32_t j=0;j& posts)
{
preMods();
@@ -571,8 +533,9 @@ void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vecto
initEmptyHierarchy();
mChannelGroup = group;
- createPostsArray(posts);
+// createPostsArray(posts);
+ mPosts = posts;
std::sort(mPosts.begin(),mPosts.end());
for(uint32_t i=0;isetCursor(Qt::WaitCursor) ; // Maybe we should pass that widget when calling update_posts
+
+ RsThread::async([this, group_id]()
{
// 1 - get message data from p3GxsChannels
@@ -616,30 +583,25 @@ void RsGxsChannelPostsModel::update_posts(const RsGxsGroupId& group_id)
RsGxsChannelGroup group = groups[0];
- // We use the heap because the arrays need to be stored accross async
+ std::vector *posts = new std::vector(); // We use the heap because the arrays need to be stored accross async
+ std::vector comments ;
+ std::vector votes ;
- 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))
+ if(!rsGxsChannels->getChannelAllContent(group_id, *posts,comments,votes))
{
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve channel messages for channel " << group_id << std::endl;
return;
}
+#ifdef DEBUG_CHANNEL_MODEL
std::cerr << "Got channel all content for channel " << group_id << std::endl;
std::cerr << " posts : " << posts->size() << std::endl;
- std::cerr << " comments: " << comments->size() << std::endl;
- std::cerr << " votes : " << votes->size() << std::endl;
-
- // This shouldn't be needed normally. We need it until a background process computes the number of comments per
- // post and stores it in the service string. Since we request all data, this process isn't costing much anyway.
-
- updateCommentCounts(*posts,*comments);
+ std::cerr << " comments: " << comments.size() << std::endl;
+ std::cerr << " votes : " << votes.size() << std::endl;
+#endif
// 2 - update the model in the UI thread.
- RsQThreadUtils::postToObject( [group,posts,comments,votes,this]()
+ RsQThreadUtils::postToObject( [group,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
@@ -650,132 +612,14 @@ void RsGxsChannelPostsModel::update_posts(const RsGxsGroupId& group_id)
setPosts(group,*posts) ;
delete posts;
- delete comments;
- delete votes;
- }, this );
+ MainWindow::getPage(MainWindow::Channels)->setCursor(Qt::ArrowCursor) ;
+
+ }, 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
@@ -798,11 +642,31 @@ void RsGxsChannelPostsModel::setAllMsgReadStatus(bool read_status)
for(uint32_t i=0;imarkRead(p,read_status))
+ if(!rsGxsChannels->setMessageReadStatus(p,read_status))
RsErr() << "setAllMsgReadStatus: failed to change status of msg " << p.first << " in group " << p.second << " to status " << read_status << std::endl;
});
+
+ // 3 - update the local model data, since we don't catch the READ_STATUS_CHANGED event later, to avoid re-loading the msg.
+
+ for(uint32_t i=0;i= mFilteredPosts.size())
return ;
- rsGxsChannels->markRead(RsGxsGrpMsgIdPair(mPosts[mFilteredPosts[entry]].mMeta.mGroupId,mPosts[mFilteredPosts[entry]].mMeta.mMsgId),read_status);
+ rsGxsChannels->setMessageReadStatus(RsGxsGrpMsgIdPair(mPosts[mFilteredPosts[entry]].mMeta.mGroupId,mPosts[mFilteredPosts[entry]].mMeta.mMsgId),read_status);
+
+ // Quick update to the msg itself. Normally setMsgReadStatus will launch an event,
+ // that we can catch to update the msg, but all the information is already here.
+
+ if(read_status)
+ mPosts[mFilteredPosts[entry]].mMeta.mMsgStatus &= ~(GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD | GXS_SERV::GXS_MSG_STATUS_GUI_NEW);
+ else
+ mPosts[mFilteredPosts[entry]].mMeta.mMsgStatus |= GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD;
+
+ mPosts[mFilteredPosts[entry]].mUnreadCommentCount = 0;
+
+ emit dataChanged(i,i);
}
QModelIndex RsGxsChannelPostsModel::getIndexOfMessage(const RsGxsMessageId& mid) const
diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h
index d8385d24b..c447f11a3 100644
--- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h
+++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h
@@ -101,9 +101,11 @@ public:
SORT_MODE_CHILDREN_PUBLISH_TS = 0x01,
};
#endif
+ static void computeCommentCounts( std::vector& posts, std::vector& comments);
QModelIndex root() const{ return createIndex(0,0,(void*)NULL) ;}
QModelIndex getIndexOfMessage(const RsGxsMessageId& mid) const;
+ int columnCount(int row) const; // columns in the row of this particular index.
std::vector > getPostVersions(const RsGxsMessageId& mid) const;
@@ -114,7 +116,7 @@ public:
void updateChannel(const RsGxsGroupId& channel_group_id);
const RsGxsGroupId& currentGroupId() const;
- void triggerViewUpdate();
+ void triggerViewUpdate(bool data_changed,bool layout_changed);
// sets the number of columns. Returns 0 if nothing changes.
bool setNumColumns(int n);
@@ -138,8 +140,11 @@ public:
void setMsgReadStatus(const QModelIndex &i, bool read_status);
void setAllMsgReadStatus(bool read_status);
+ void updatePostWithNewComment(const RsGxsMessageId& msg_id);
void setFilter(const QStringList &strings, bool only_unread,uint32_t &count) ;
+ bool postPassesFilter(const RsGxsChannelPost &post, const QStringList &strings, bool only_unread) const;
+ void updateFilter(uint32_t& count);
#ifdef TODO
void setAuthorOpinion(const QModelIndex& indx,RsOpinion op);
@@ -215,6 +220,8 @@ private:
void update_posts(const RsGxsGroupId& group_id);
+private:
+
#ifdef TODO
void setForumMessageSummary(const std::vector& messages);
#endif
@@ -229,10 +236,16 @@ private:
//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);
+ void old_createPostsArray(std::vector &posts);
+ void createPostsArray(std::vector& posts);
+ void setPosts(const RsGxsChannelGroup& group, std::vector &posts);
+public:
+ void updateSinglePost(const RsGxsChannelPost& post, std::set& added_files, std::set& removed_files);
+private:
+ void initEmptyHierarchy();
+
+ QStringList mFilteredStrings;
+ bool mFilterUnread;
std::vector mFilteredPosts; // stores the list of displayes indices due to filtering.
std::vector mPosts ; // store the list of posts updated from rsForums.
diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp
index d3c4070b0..18aa2ad17 100644
--- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp
+++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp
@@ -56,8 +56,6 @@
* #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;
@@ -75,12 +73,13 @@ QColor SelectedColor = QRgb(0xff308dc7);
#define COLUMN_SIZE_FONT_FACTOR_W 6
#define COLUMN_SIZE_FONT_FACTOR_H 10
-#define STAR_OVERLAY_IMAGE ":icons/star_overlay_128.png"
-#define COMMENT_OVERLAY_IMAGE ":images/white-bubble-64.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"
+#define STAR_OVERLAY_IMAGE ":icons/star_overlay_128.png"
+#define COMMENT_OVERLAY_IMAGE ":images/white-bubble-64.png"
+#define UNREAD_COMMENT_OVERLAY_IMAGE ":images/orange-bubble-64.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)
@@ -93,17 +92,17 @@ Q_DECLARE_METATYPE(ChannelPostFileInfo)
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();
+ return (int)floor(mZoom*COLUMN_SIZE_FONT_FACTOR_W*QFontMetricsF(font).height());
else
- return 0.8*parent_width - mZoom*COLUMN_SIZE_FONT_FACTOR_W*QFontMetricsF(font).height();
+ return (int)floor(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;
+ mZoom *= 1.02;
else
- mZoom /= 1.02;
+ mZoom /= 1.02;
if(mZoom < 0.5)
mZoom = 0.5;
@@ -123,11 +122,7 @@ void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem &
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->fillRect( option.rect, option.palette.base().color());
painter->restore();
if(mUseGrid || index.column()==0)
@@ -146,14 +141,7 @@ void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem &
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());
- }
+ pixmap.fill(option.palette.base().color());
w.render(&pixmap,QPoint(),QRegion(),QWidget::DrawChildren );// draw the widgets, not the background
@@ -166,37 +154,43 @@ void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem &
// {
// QFile file("yourFile.png");
// file.open(QIODevice::WriteOnly);
-// pixmap.save(&file, "PNG");
+// pixmap.save(&file, "JPG");
// 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))
{
- if(mZoom != 1.0)
- pixmap = pixmap.scaled(mZoom*pixmap.size(),Qt::KeepAspectRatio,Qt::SmoothTransformation);
+ QPainter p(&pixmap);
+ QFontMetricsF fm(option.font);
- 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.4*fm.height()),FilesDefs::getPixmapFromQtResourcePath(STAR_OVERLAY_IMAGE).scaled(mZoom*6*fm.height(),mZoom*6*fm.height(),Qt::KeepAspectRatio,Qt::SmoothTransformation));
+ }
- p.drawPixmap(mZoom*QPoint(0.1*fm.height(),-3.4*fm.height()),FilesDefs::getPixmapFromQtResourcePath(STAR_OVERLAY_IMAGE).scaled(mZoom*6*fm.height(),mZoom*6*fm.height(),Qt::KeepAspectRatio,Qt::SmoothTransformation));
- }
+ if(post.mUnreadCommentCount > 0)
+ {
+ QPainter p(&pixmap);
+ QFontMetricsF fm(option.font);
- if(post.mUnreadCommentCount)
- {
- QPainter p(&pixmap);
- QFontMetricsF fm(option.font);
-
- p.drawPixmap(QPoint(pixmap.width(),0.0)+mZoom*QPoint(-2.9*fm.height(),0.4*fm.height()),
- FilesDefs::getPixmapFromQtResourcePath(COMMENT_OVERLAY_IMAGE).scaled(mZoom*3*fm.height(),mZoom*3*fm.height(),
- Qt::KeepAspectRatio,Qt::SmoothTransformation));
- }
+ p.drawPixmap(QPoint(pixmap.width(),0.0)+mZoom*QPoint(-2.9*fm.height(),0.4*fm.height()),
+ FilesDefs::getPixmapFromQtResourcePath(UNREAD_COMMENT_OVERLAY_IMAGE).scaled(mZoom*3*fm.height(),mZoom*3*fm.height(),
+ Qt::KeepAspectRatio,Qt::SmoothTransformation));
+ }
+ else if(post.mCommentCount > 0)
+ {
+ QPainter p(&pixmap);
+ QFontMetricsF fm(option.font);
+ p.drawPixmap(QPoint(pixmap.width(),0.0)+mZoom*QPoint(-2.9*fm.height(),0.4*fm.height()),
+ FilesDefs::getPixmapFromQtResourcePath(COMMENT_OVERLAY_IMAGE).scaled(mZoom*3*fm.height(),mZoom*3*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));
+ pixmap.scaled(option.rect.width(),option.rect.width()*pixmap.height()/(float)pixmap.width(),
+ Qt::IgnoreAspectRatio,Qt::SmoothTransformation));
}
else
{
@@ -264,6 +258,9 @@ QSize ChannelPostDelegate::sizeHint(const QStyleOptionViewItem& option, const QM
float cell_width = mZoom*COLUMN_SIZE_FONT_FACTOR_W*fm.height();
float cell_height = mZoom*COLUMN_SIZE_FONT_FACTOR_W*fm.height()*aspect_ratio;
+#ifdef DEBUG_CHANNEL_POSTS_WIDGET
+ RsDbg() << "SizeHint: mUseGrid=" << mUseGrid << " cell_width=" << cell_width << " cell_height=" << cell_height << " mZoom=" << mZoom ;
+#endif
if(mUseGrid || index.column()==0)
return QSize(cell_width,cell_height);
@@ -282,52 +279,54 @@ void ChannelPostDelegate::setWidgetGrid(bool use_grid)
QWidget *ChannelPostFilesDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/*option*/, const QModelIndex& index) const
{
- ChannelPostFileInfo file = index.data(Qt::UserRole).value() ;
+ ChannelPostFileInfo file = index.data(Qt::UserRole).value() ;
- if(index.column() == RsGxsChannelPostFilesModel::COLUMN_FILES_FILE)
- {
- GxsChannelFilesStatusWidget* w = new GxsChannelFilesStatusWidget(file,parent);
- connect(w,SIGNAL(onButtonClick()),this->parent(),SLOT(updateDAll_PB()));
- return w;
- }
- else
- return NULL;
+ if(index.column() == RsGxsChannelPostFilesModel::COLUMN_FILES_FILE)
+ {
+ GxsChannelFilesStatusWidget* w = new GxsChannelFilesStatusWidget(file,parent,true);
+ w->setFocusPolicy(Qt::StrongFocus);
+ connect(w,SIGNAL(onButtonClick()),this->parent(),SLOT(updateDAll_PB()));
+
+ return w;
+ }
+ else
+ return NULL;
}
void ChannelPostFilesDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const
{
- editor->setGeometry(option.rect);
+ editor->setGeometry(option.rect);
}
void ChannelPostFilesDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
- ChannelPostFileInfo file = index.data(Qt::UserRole).value() ;
+ ChannelPostFileInfo file = index.data(Qt::UserRole).value() ;
- // prepare
- painter->save();
- painter->setClipRect(option.rect);
+ // 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);
+ //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;
+ break;
case RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE: painter->drawText(option.rect,Qt::AlignRight | Qt::AlignVCenter,misc::friendlyUnit(qulonglong(file.mSize)));
- break;
+ 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;
+ break;
case RsGxsChannelPostFilesModel::COLUMN_FILES_FILE: {
- GxsChannelFilesStatusWidget w(file);
+ GxsChannelFilesStatusWidget w(file);
w.setFixedWidth(option.rect.width());
- w.setFixedHeight(option.rect.height());
+ w.setFixedHeight(option.rect.height());
- QPixmap pixmap(w.size());
+ QPixmap pixmap(w.size());
// apparently we need to do the alternate colors manually
//if(index.row() & 0x01)
@@ -343,13 +342,13 @@ void ChannelPostFilesDelegate::paint(QPainter * painter, const QStyleOptionViewI
default:
painter->drawText(option.rect,Qt::AlignLeft,QString("[No data]"));
- break;
+ break;
}
}
QSize ChannelPostFilesDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
{
- ChannelPostFileInfo file = index.data(Qt::UserRole).value() ;
+ ChannelPostFileInfo file = index.data(Qt::UserRole).value() ;
QFontMetricsF fm(option.font);
@@ -369,18 +368,18 @@ QSize ChannelPostFilesDelegate::sizeHint(const QStyleOptionViewItem& option, con
/** Constructor */
GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupId &channelId, QWidget *parent) :
- GxsMessageFrameWidget(rsGxsChannels, parent),
- ui(new Ui::GxsChannelPostsWidgetWithModel)
+ GxsMessageFrameWidget(rsGxsChannels, parent),
+ ui(new Ui::GxsChannelPostsWidgetWithModel)
{
- /* Invoke the Qt Designer generated object setup routine */
- ui->setupUi(this);
+ /* 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);
+ whileBlocking(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)));
@@ -393,7 +392,7 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI
mChannelPostsDelegate->setAspectRatio(ChannelPostThumbnailView::ASPECT_RATIO_16_9);
- connect(ui->postsTree,SIGNAL(zoomRequested(bool)),this,SLOT(updateZoomFactor(bool)));
+ connect(ui->postsTree,SIGNAL(zoomRequested(bool)),this,SLOT(onUpdateZoomFactor(bool)));
connect(ui->commentsDialog,SIGNAL(commentsLoaded(int)),this,SLOT(updateCommentsCount(int)));
ui->channelPostFiles_TV->setModel(mChannelPostFilesModel = new RsGxsChannelPostFilesModel(this));
@@ -412,12 +411,14 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI
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->channelPostFiles_TV,SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showChannelFilesContextMenu(QPoint)));
+ connect(ui->channelPostFiles_TV,SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showChannelPostFilesContextMenu(QPoint)));
connect(ui->channelFiles_TV,SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showChannelFilesContextMenu(QPoint)));
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(ui->channel_TW,SIGNAL(currentChanged(int)),this,SLOT(currentTabChanged(int)));
+
connect(mChannelPostsModel,SIGNAL(channelPostsLoaded()),this,SLOT(postChannelPostLoad()));
ui->postName_LB->hide();
@@ -429,94 +430,149 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI
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);
+ // Set initial size of the splitter
+ ui->splitter->setStretchFactor(0, 1);
+ ui->splitter->setStretchFactor(1, 0);
- QFontMetricsF fm(font());
+ 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()));
+ 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 */
+ /* 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()));
+ /* 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->postButton->setText(tr("Add new post"));
+
+ /* add filter actions */
ui->filterLineEdit->setPlaceholderText(tr("Search..."));
- connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString)));
+ connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString)));
ui->postsTree->setPlaceholderText(tr("No posts available in this channel"));
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 () ;
+ /* 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"));
+ 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);
+ /* Initialize feed widget */
+ //ui->feedWidget->setSortRole(ROLE_PUBLISH, Qt::DescendingOrder);
+ //ui->feedWidget->setFilterCallback(filterItem);
- /* load settings */
- processSettings(true);
+ /* load settings */
+ processSettings(true);
ui->channelPostFiles_TV->setColumnHidden(RsGxsChannelPostFilesModel::COLUMN_FILES_DATE, true); // no need to show this here.
- /* Initialize subscribe button */
- QIcon icon;
+ /* 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);
#ifdef TO_REMOVE
- mAutoDownloadAction = new QAction(icon, "", this);
- mAutoDownloadAction->setCheckable(true);
- connect(mAutoDownloadAction, SIGNAL(triggered()), this, SLOT(toggleAutoDownload()));
+ mAutoDownloadAction = new QAction(icon, "", this);
+ mAutoDownloadAction->setCheckable(true);
+ connect(mAutoDownloadAction, SIGNAL(triggered()), this, SLOT(toggleAutoDownload()));
- ui->subscribeToolButton->addSubscribedAction(mAutoDownloadAction);
+ ui->subscribeToolButton->addSubscribedAction(mAutoDownloadAction);
setAutoDownload(false);
#endif
- ui->commentsDialog->setTokenService(rsGxsChannels->getTokenService(),rsGxsChannels);
+ ui->commentsDialog->setGxsService(rsGxsChannels);
- /* Initialize GUI */
- settingsChanged();
+ /* Initialize GUI */
+ settingsChanged();
setGroupId(channelId);
- mChannelPostsModel->updateChannel(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)
+ 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)
+void GxsChannelPostsWidgetWithModel::keyPressEvent(QKeyEvent *e)
{
- mChannelPostsDelegate->zoom(zoom_or_unzoom);
+ QModelIndex index = ui->postsTree->selectionModel()->currentIndex();
+
+ if(index.isValid() && mChannelPostsModel->getMode() == RsGxsChannelPostsModel::TREE_MODE_GRID)
+ {
+ int n = mChannelPostsModel->columnCount(index.row())-1;
+
+ if(e->key() == Qt::Key_Left && index.column()==0)
+ {
+ ui->postsTree->setCurrentIndex(index.sibling(index.row(),n));
+ e->accept();
+ return;
+ }
+ if(e->key() == Qt::Key_Right && index.column()==n)
+ {
+ ui->postsTree->setCurrentIndex(index.sibling(index.row(),0));
+ e->accept();
+ return;
+ }
+ }
+
+ GxsMessageFrameWidget::keyPressEvent(e);
+}
+void GxsChannelPostsWidgetWithModel::resizeEvent(QResizeEvent *e)
+{
+ GxsMessageFrameWidget::resizeEvent(e);
+ updateZoomFactor(0);
+}
+void GxsChannelPostsWidgetWithModel::currentTabChanged(int t)
+{
+ switch(t)
+ {
+ case CHANNEL_TABS_DETAILS:
+ case CHANNEL_TABS_FILES:
+ ui->showUnread_TB->setHidden(true);
+ ui->viewType_TB->setHidden(true);
+ break;
+
+ case CHANNEL_TABS_POSTS:
+ ui->showUnread_TB->setHidden(false);
+ ui->viewType_TB->setHidden(false);
+ updateZoomFactor(0); // fixes a bug due to the widget now knowing its size when not displayed.
+ break;
+ }
+}
+void GxsChannelPostsWidgetWithModel::onUpdateZoomFactor(bool zoom_or_unzoom)
+{
+ if(zoom_or_unzoom)
+ updateZoomFactor(1);
+ else
+ updateZoomFactor(-1);
+}
+void GxsChannelPostsWidgetWithModel::updateZoomFactor(int what_to_do)
+{
+ if(what_to_do != 0)
+ mChannelPostsDelegate->zoom(what_to_do > 0);
+
+ QSize s = ui->postsTree->size();
+ int n_columns = std::max(1,(int)floor(s.width() / (float)(mChannelPostsDelegate->cellSize(0,font(),s.width()))));
+ mChannelPostsModel->setNumColumns(n_columns); // forces the update
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());
+ if(what_to_do)
+ mChannelPostsModel->triggerViewUpdate(true,false);
+ else
+ mChannelPostsModel->triggerViewUpdate(false,true);
}
void GxsChannelPostsWidgetWithModel::sortColumnPostFiles(int col,Qt::SortOrder so)
@@ -557,10 +613,10 @@ void GxsChannelPostsWidgetWithModel::postContextMenu(const QPoint&)
}
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()));
+ 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());
+ menu.exec(QCursor::pos());
}
void GxsChannelPostsWidgetWithModel::markMessageUnread()
@@ -622,7 +678,7 @@ void GxsChannelPostsWidgetWithModel::switchView()
selectItem(msg_id);
ui->postsTree->setFocus();
- mChannelPostsModel->triggerViewUpdate(); // This is already called by setMode(), but the model cannot know how many
+ mChannelPostsModel->triggerViewUpdate(false,true); // 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.
}
@@ -630,29 +686,29 @@ void GxsChannelPostsWidgetWithModel::switchView()
void GxsChannelPostsWidgetWithModel::copyMessageLink()
{
try
- {
- if (groupId().isNull())
- throw std::runtime_error("No channel currently selected!");
+ {
+ if (groupId().isNull())
+ throw std::runtime_error("No channel currently selected!");
- QModelIndex index = ui->postsTree->selectionModel()->currentIndex();
+ QModelIndex index = ui->postsTree->selectionModel()->currentIndex();
- if(!index.isValid())
- throw std::runtime_error("No post under mouse!");
+ if(!index.isValid())
+ throw std::runtime_error("No post under mouse!");
- RsGxsChannelPost post = index.data(Qt::UserRole).value() ;
+ RsGxsChannelPost post = index.data(Qt::UserRole).value() ;
- if(post.mMeta.mMsgId.isNull())
- throw std::runtime_error("Post has empty MsgId!");
+ 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()));
+ 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");
+ if (!link.valid())
+ throw std::runtime_error("Link is not valid");
- QList urls;
- urls.push_back(link);
- RSLinkClipboard::copyLinks(urls);
- }
+ 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());
@@ -681,41 +737,41 @@ void GxsChannelPostsWidgetWithModel::download()
rsFiles->FileRequest(file.mName, file.mHash, file.mSize, destination, RS_FILE_REQ_ANONYMOUS_ROUTING, sources);
}
- ui->postDAll_PB->hide();
+ ui->postDAll_PB->hide();
}
void GxsChannelPostsWidgetWithModel::updateDAll_PB()
{
- QModelIndex index = ui->postsTree->selectionModel()->currentIndex();
- RsGxsChannelPost post = index.data(Qt::UserRole).value() ;
+ QModelIndex index = ui->postsTree->selectionModel()->currentIndex();
+ RsGxsChannelPost post = index.data(Qt::UserRole).value() ;
- size_t newFileToDl = 0;
- uint64_t newFileTotalSize = 0;
- QString newFilesDetails;
+ size_t newFileToDl = 0;
+ uint64_t newFileTotalSize = 0;
+ QString newFilesDetails;
- for(auto& file:post.mFiles)
- {
- FileInfo fileInfo;
- if (!rsFiles->FileDetails(file.mHash, RS_FILE_HINTS_DOWNLOAD | RS_FILE_HINTS_SPEC_ONLY, fileInfo)) {
- ++newFileToDl;
- newFileTotalSize += file.mSize;
- newFilesDetails += QString::fromUtf8(file.mName.c_str()) + " " + misc::friendlyUnit(file.mSize) + "\n";
- }
- }
+ for(auto& file:post.mFiles)
+ {
+ FileInfo fileInfo;
+ if (!rsFiles->FileDetails(file.mHash, RS_FILE_HINTS_DOWNLOAD | RS_FILE_HINTS_SPEC_ONLY, fileInfo)) {
+ ++newFileToDl;
+ newFileTotalSize += file.mSize;
+ newFilesDetails += QString::fromUtf8(file.mName.c_str()) + " " + misc::friendlyUnit(file.mSize) + "\n";
+ }
+ }
- ui->postDAll_PB->setHidden(newFileToDl == 0);
- ui->postDAll_PB->setToolTip((newFileToDl == 1 ? tr("Download this file:") : tr("Download All these %1 files:").arg(newFileToDl) ) + "\n"
- + newFilesDetails
- + tr("Totaling: %1").arg(misc::friendlyUnit(newFileTotalSize)));
+ ui->postDAll_PB->setHidden(newFileToDl == 0);
+ ui->postDAll_PB->setToolTip((newFileToDl == 1 ? tr("Download this file:") : tr("Download All these %1 files:").arg(newFileToDl) ) + "\n"
+ + newFilesDetails
+ + tr("Totaling: %1").arg(misc::friendlyUnit(newFileTotalSize)));
}
void GxsChannelPostsWidgetWithModel::editPost()
{
QModelIndex index = ui->postsTree->selectionModel()->currentIndex();
- RsGxsChannelPost post = index.data(Qt::UserRole).value() ;
+ RsGxsChannelPost post = index.data(Qt::UserRole).value() ;
- CreateGxsChannelMsg *msgDialog = new CreateGxsChannelMsg(post.mMeta.mGroupId,post.mMeta.mMsgId);
+ CreateGxsChannelMsg *msgDialog = new CreateGxsChannelMsg(post.mMeta.mGroupId,post.mMeta.mMsgId);
msgDialog->show();
}
@@ -725,7 +781,9 @@ void GxsChannelPostsWidgetWithModel::handlePostsTreeSizeChange(QSize s,bool forc
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;
+#ifdef DEBUG_CHANNEL_POSTS_WIDGET
+ RsDbg() << "nb columns: " << n_columns << " current count=" << mChannelPostsModel->columnCount() ;
+#endif
// save current post. The setNumColumns() indeed loses selection
@@ -744,69 +802,128 @@ void GxsChannelPostsWidgetWithModel::handlePostsTreeSizeChange(QSize s,bool forc
void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptr event)
{
- const RsGxsChannelEvent *e = dynamic_cast(event.get());
+ const RsGxsChannelEvent *e = dynamic_cast(event.get());
- if(!e)
- return;
+ if(!e)
+ return;
- switch(e->mChannelEventCode)
- {
- case RsChannelEventCode::NEW_CHANNEL: // [[fallthrough]];
+ switch(e->mChannelEventCode)
+ {
+ case RsChannelEventCode::NEW_CHANNEL: // [[fallthrough]];
case RsChannelEventCode::DELETED_CHANNEL: // [[fallthrough]];
- case RsChannelEventCode::NEW_COMMENT: // [[fallthrough]];
- case RsChannelEventCode::NEW_VOTE: // [[fallthrough]];
case RsChannelEventCode::UPDATED_CHANNEL: // [[fallthrough]];
- case RsChannelEventCode::NEW_MESSAGE: // [[fallthrough]];
- case RsChannelEventCode::UPDATED_MESSAGE:
case RsChannelEventCode::RECEIVED_PUBLISH_KEY:
case RsChannelEventCode::SYNC_PARAMETERS_UPDATED:
{
- if(e->mChannelGroupId == groupId())
- updateDisplay(true);
- }
+ if(e->mChannelGroupId == groupId())
+ updateDisplay(true,false);
+ }
+ break;
- default:
- break;
- }
+ case RsChannelEventCode::READ_STATUS_CHANGED: // This is already handled by setMsgReadStatus() that has been called and issued this event.
+ break;
+
+ case RsChannelEventCode::NEW_MESSAGE:
+ {
+ if(e->mChannelGroupId == groupId())
+ {
+#ifdef DEBUG_CHANNEL_POSTS_WIDGET
+ RsDbg() << "Received new message in current channel, msgId=" << e->mChannelMsgId ;
+#endif
+
+ RsThread::async([this,E=*e]() // dereferencing to make a copy that will survive while e is deleted by the parent thread.
+ {
+ // 1 - get message data from p3GxsChannels. No need for pointers here, because we send only a single post to postToObject()
+ // At this point we dont know what kind of msg id we have. It can be a vote, a comment or an actual message.
+
+ std::vector posts;
+ std::vector comments;
+ std::vector votes;
+
+ const auto& msg_id(E.mChannelMsgId);
+ const auto& grp_id(E.mChannelGroupId);
+
+ if(!rsGxsChannels->getChannelContent(grp_id, { msg_id }, posts,comments,votes) || posts.size() != 1)
+ {
+ RsErr() << " failed to retrieve channel message data for channel/msg " << grp_id << "/" << msg_id;
+ return;
+ }
+
+ // 2 - update the model in the UI thread.
+
+ RsQThreadUtils::postToObject( [post=posts[0],this]()
+ {
+ std::set added_files,removed_files;
+
+ mChannelPostsModel->updateSinglePost(post,added_files,removed_files);
+
+ std::set added_filesi,removed_filesi;
+
+ for(auto f:added_files) added_filesi.insert(ChannelPostFileInfo(f,post.mMeta.mPublishTs));
+ for(auto f:removed_files) removed_filesi.insert(ChannelPostFileInfo(f,post.mMeta.mPublishTs));
+
+ mChannelFilesModel->update_files(added_filesi,removed_filesi);
+
+ },this);
+ });
+ }
+ }
+ break;
+
+ case RsChannelEventCode::NEW_COMMENT:
+
+ if(e->mChannelGroupId == groupId() && e->mChannelThreadId != ui->commentsDialog->messageId())
+ mChannelPostsModel->updatePostWithNewComment(e->mChannelThreadId); [[fallthrough]];
+
+ case RsChannelEventCode::NEW_VOTE:
+
+ if(e->mChannelGroupId == groupId() && e->mChannelThreadId == ui->commentsDialog->messageId())
+ ui->commentsDialog->refresh();
+ break;
+
+ default:
+ break;
+
+ }
}
void GxsChannelPostsWidgetWithModel::showPostDetails()
{
QModelIndex index = ui->postsTree->selectionModel()->currentIndex();
- RsGxsChannelPost post = index.data(Qt::UserRole).value() ;
+ RsGxsChannelPost post = index.data(Qt::UserRole).value() ;
#ifdef DEBUG_CHANNEL_POSTS_WIDGET
- std::cerr << "showPostDetails: current index is " << index.row() << "," << index.column() << std::endl;
+ RsDbg() << "showPostDetails: current index is " << index.row() << "," << index.column() ;
#endif
- //QTextDocument doc;
- //doc.setHtml(post.mMsg.c_str());
+ //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);
- //mLastSelectedPosts[groupId()].clear();
+ 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);
+ //mLastSelectedPosts[groupId()].clear();
- return;
- }
- ui->details_TW->setEnabled(true);
+ return;
+ }
+ ui->details_TW->setEnabled(true);
- ui->postLogo_LB->show();
- ui->postName_LB->show();
- ui->postTime_LB->show();
+ ui->postLogo_LB->show();
+ ui->postName_LB->show();
+ ui->postTime_LB->show();
#ifdef DEBUG_CHANNEL_POSTS_WIDGET
- std::cerr << "showPostDetails: setting mLastSelectedPosts[groupId()] to current post Id " << post.mMeta.mMsgId << ". Previous value: " << mLastSelectedPosts[groupId()] << std::endl;
+ RsDbg() << "showPostDetails: setting mLastSelectedPosts[groupId()] to current post Id " << post.mMeta.mMsgId << ". Previous value: " << mLastSelectedPosts[groupId()] ;
#endif
- mLastSelectedPosts[groupId()] = post.mMeta.mMsgId;
+ mLastSelectedPosts[groupId()] = post.mMeta.mMsgId;
- std::list files;
- for(auto& file:post.mFiles)
- files.push_back(ChannelPostFileInfo(file,post.mMeta.mPublishTs));
+ std::list files;
+ for(auto& file:post.mFiles)
+ files.push_back(ChannelPostFileInfo(file,post.mMeta.mPublishTs));
mChannelPostFilesModel->setFiles(files);
@@ -816,46 +933,51 @@ void GxsChannelPostsWidgetWithModel::showPostDetails()
ui->commentsDialog->commentLoad(post.mMeta.mGroupId, all_msgs_versions, post.mMeta.mMsgId,true);
#ifdef DEBUG_CHANNEL_POSTS_WIDGET
- std::cerr << "Showing details about selected index : "<< index.row() << "," << index.column() << std::endl;
+ RsDbg() << "Showing details about selected index : "<< index.row() << "," << index.column() ;
#endif
ui->postDetails_TE->setText(RsHtml().formatText(NULL, QString::fromUtf8(post.mMsg.c_str()), /* RSHTML_FORMATTEXT_EMBED_SMILEYS |*/ RSHTML_FORMATTEXT_EMBED_LINKS));
- QPixmap postImage;
+ 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);
+ 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->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->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->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
+ // 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;
+ if(IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus) || post.mUnreadCommentCount > 0)
+ {
+ mChannelPostsModel->setMsgReadStatus(index,true);
- RsThread::async([postId]() { rsGxsChannels->markRead(postId, true) ; } );
- }
+ //RsGxsGrpMsgIdPair postId;
+ //postId.second = post.mMeta.mMsgId;
+ //postId.first = post.mMeta.mGroupId;
- updateDAll_PB();
+ //RsThread::async([postId]()
+ //{
+ //rsGxsChannels->setMessageReadStatus(postId, true) ;
+ //} );
+ }
+
+ updateDAll_PB();
}
void GxsChannelPostsWidgetWithModel::updateCommentsCount(int n)
@@ -865,30 +987,30 @@ void GxsChannelPostsWidgetWithModel::updateCommentsCount(int n)
else
ui->details_TW->setTabText(2,tr("Comments"));
}
-void GxsChannelPostsWidgetWithModel::updateGroupData()
+void GxsChannelPostsWidgetWithModel::updateData(bool update_group_data, bool update_posts)
{
- if(groupId().isNull())
+ if(groupId().isNull())
{
// clear post, files and comment widgets
showPostDetails();
- return;
+ return;
}
- RsThread::async([this]()
- {
- RsGxsChannelGroup group;
- std::vector groups;
+ RsThread::async([this,update_group_data,update_posts]()
+ {
+ std::vector groups;
+ RsGxsChannelGroup group;
- if(rsGxsChannels->getChannelsInfo(std::list{ groupId() }, groups) && groups.size()==1)
+ 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;
- }
+ {
+ std::cerr << __PRETTY_FUNCTION__ << " failed to get group data for channel: " << groupId() << std::endl;
+ return;
+ }
- RsQThreadUtils::postToObject( [this,group]()
+ RsQThreadUtils::postToObject( [this,update_group_data,update_posts,group]()
{
if(mGroup.mMeta.mGroupId != group.mMeta.mGroupId) // this prevents any attempt to display the wrong index. Navigate() if needed will use mNavigatePendingMsgId
{
@@ -900,49 +1022,63 @@ void GxsChannelPostsWidgetWithModel::updateGroupData()
updateCommentsCount(0);
}
- mGroup = group;
- mChannelPostsModel->updateChannel(groupId());
- whileBlocking(ui->filterLineEdit)->clear();
- whileBlocking(ui->showUnread_TB)->setChecked(false);
+ if(update_group_data)
+ {
+ mGroup = group;
+ insertChannelDetails(mGroup);
+ }
- insertChannelDetails(mGroup);
+ if(update_posts)
+ {
+ ui->postsTree->setPlaceholderText(tr("Loading..."));
+
+ mChannelPostsModel->updateChannel(groupId());
+
+ whileBlocking(ui->filterLineEdit)->clear();
+ whileBlocking(ui->showUnread_TB)->setChecked(false);
+ }
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;
+#ifdef DEBUG_CHANNEL_POSTS_WIDGET
+ std::cerr << "Post channel load..." << std::endl;
+#endif
- if (!mNavigatePendingMsgId.isNull())
- navigate(mNavigatePendingMsgId);
+ if (!mNavigatePendingMsgId.isNull())
+ navigate(mNavigatePendingMsgId);
- else if( (mLastSelectedPosts.count(groupId()) > 0)
+ else if( (mLastSelectedPosts.count(groupId()) > 0)
&& !mLastSelectedPosts[groupId()].isNull())
- {
- QModelIndex index = mChannelPostsModel->getIndexOfMessage(mLastSelectedPosts[groupId()]);
+ {
+ QModelIndex index = mChannelPostsModel->getIndexOfMessage(mLastSelectedPosts[groupId()]);
- std::cerr << "Setting current index to " << index.row() << ","<< index.column() << " for current post "
- << mLastSelectedPosts[groupId()].toStdString() << std::endl;
+ std::cerr << "Setting current index to " << index.row() << ","<< index.column() << " for current post "
+ << mLastSelectedPosts[groupId()].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;
+ 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;
+ std::list files;
mChannelPostsModel->getFilesList(files);
mChannelFilesModel->setFiles(files);
- ui->channelFiles_TV->setAutoSelect(true);
- ui->channelFiles_TV->sortByColumn(ui->channelFiles_TV->header()->sortIndicatorSection()
- ,ui->channelFiles_TV->header()->sortIndicatorOrder());
+ ui->channelFiles_TV->setAutoSelect(true);
+ ui->channelFiles_TV->sortByColumn(ui->channelFiles_TV->header()->sortIndicatorSection()
+ ,ui->channelFiles_TV->header()->sortIndicatorOrder());
+
+ // if there's no posts, this is what's going to be displayed.
+ ui->postsTree->setPlaceholderText(tr("No posts available in this channel."));
ui->infoPosts->setText(QString::number(mChannelPostsModel->getNumberOfPosts()) + " / " + QString::number(mGroup.mMeta.mVisibleMsgCount));
@@ -963,88 +1099,80 @@ void GxsChannelPostsWidgetWithModel::postChannelPostLoad()
best = i;
mChannelPostsDelegate->setAspectRatio(static_cast(best));
- mChannelPostsModel->triggerViewUpdate();
handlePostsTreeSizeChange(ui->postsTree->size(),true); // force the update
+
+ updateZoomFactor(0);
}
-void GxsChannelPostsWidgetWithModel::updateDisplay(bool complete)
+void GxsChannelPostsWidgetWithModel::updateDisplay(bool update_group_data,bool update_posts)
{
- // First, clear all widget
- blank();
+ // First, clear all widget
+
#ifdef DEBUG_CHANNEL
std::cerr << "udateDisplay: groupId()=" << groupId()<< std::endl;
#endif
- if(groupId().isNull())
+ if(groupId().isNull())
{
#ifdef DEBUG_CHANNEL
std::cerr << " group_id=0. Return!"<< std::endl;
#endif
- return;
+ return;
}
- if(mGroup.mMeta.mGroupId.isNull() && !groupId().isNull())
+ if(mGroup.mMeta.mGroupId != groupId())
{
#ifdef DEBUG_FORUMS
std::cerr << " inconsistent group data. Reloading!"<< std::endl;
#endif
- complete = true;
+ update_group_data = true;
+ update_posts = true;
}
- if(complete) // need to update the group data, reload the messages etc.
- {
-#warning csoler 2020-06-02 : todo
- //saveExpandedItems(mSavedExpandedMessages);
- //if(mGroupId != mChannelPostsModel->currentGroupId())
- // mThreadId.clear();
-
- updateGroupData();
-
- return;
- }
+ updateData(update_group_data,update_posts);
}
GxsChannelPostsWidgetWithModel::~GxsChannelPostsWidgetWithModel()
{
- rsEvents->unregisterEventsHandler(mEventHandlerId);
- // save settings
- processSettings(false);
+ rsEvents->unregisterEventsHandler(mEventHandlerId);
+ // save settings
+ processSettings(false);
//delete(mAutoDownloadAction);
delete mFilesDelegate;
- delete ui;
+ delete ui;
}
void GxsChannelPostsWidgetWithModel::processSettings(bool load)
{
- QHeaderView *channelpostfilesheader = ui->channelPostFiles_TV->header () ;
- QHeaderView *channelfilesheader = ui->channelFiles_TV->header () ;
-
- Settings->beginGroup(QString("ChannelPostsWidget"));
+ QHeaderView *channelpostfilesheader = ui->channelPostFiles_TV->header () ;
+ QHeaderView *channelfilesheader = ui->channelFiles_TV->header () ;
- if (load) {
+ Settings->beginGroup(QString("ChannelPostsWidget"));
- // state of files tree
- channelpostfilesheader->restoreState(Settings->value("PostFilesTree").toByteArray());
- channelfilesheader->restoreState(Settings->value("FilesTree").toByteArray());
+ if (load) {
- // 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 files tree
+ channelpostfilesheader->restoreState(Settings->value("PostFilesTree").toByteArray());
+ channelfilesheader->restoreState(Settings->value("FilesTree").toByteArray());
- // state of splitter
- Settings->setValue("SplitterChannelPosts", ui->splitter->saveState());
- }
+ // 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());
- Settings->endGroup();
+ // state of splitter
+ Settings->setValue("SplitterChannelPosts", ui->splitter->saveState());
+ }
+
+ Settings->endGroup();
}
void GxsChannelPostsWidgetWithModel::settingsChanged()
{
- mUseThread = Settings->getChannelLoadThread();
+ mUseThread = Settings->getChannelLoadThread();
- //mStateHelper->setWidgetVisible(ui->progressBar, mUseThread);
+ //mStateHelper->setWidgetVisible(ui->progressBar, mUseThread);
}
QString GxsChannelPostsWidgetWithModel::groupName(bool)
@@ -1064,15 +1192,15 @@ void GxsChannelPostsWidgetWithModel::groupNameChanged(const QString &/*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);
- }
+ /* 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);
+ return QIcon(chanImage);
}
/*************************************************************************************/
@@ -1082,50 +1210,50 @@ QIcon GxsChannelPostsWidgetWithModel::groupIcon()
// Callback from Widget->FeedHolder->ServiceDialog->CommentContainer->CommentDialog,
void GxsChannelPostsWidgetWithModel::openComments(uint32_t /*type*/, const RsGxsGroupId &groupId, const QVector