diff --git a/README-Android.asciidoc b/README-Android.asciidoc index 085d50ade..7c781dda9 100644 --- a/README-Android.asciidoc +++ b/README-Android.asciidoc @@ -1,15 +1,13 @@ Compile Retroshare for Android ============================== -== Introduction - Compiling an application for Android is not as easy as one would imagine, expecially one like RetroShare that has a big codebase and is not well -documented. -This document is aimed to empower the reader so she can hopefully succed or at -least have a significant help in compiling her own RetroShare APK package +documented. This document is aimed to empower the reader so she can hopefully +succed or at least have a significant help in compiling her own RetroShare APK installable on Android. + == Preparing The Environement First of all setup your Qt for Android development environement following the @@ -21,7 +19,7 @@ on your Android phone Qt for Android examples. But RetroShare is not as simple to compile as those examples. The good news is that Android NDK ships all the necessary to build a custom toolchain that is suitable to build RetroShare. -In order to build the toolchain with needed library RetroShare provides the +In order to build the toolchain with needed libraries RetroShare provides the +android-prepare-toolchain.sh+ script; before you execute it you should define some variables the script cannot determine in an easy and reliable manner by itself in your terminal. @@ -33,7 +31,7 @@ export ANDROID_NDK_PATH="/opt/android-ndk/" ## The path where your fresh compiled toolchain will be installed, make sure ## the parent exists -export NDK_TOOLCHAIN_PATH="/home/$(whoami)/Development/android-toolchains/retroshare-android/" +export NDK_TOOLCHAIN_PATH="${HOME}/Builds/android-toolchains/retroshare-android/" ## The CPU architecture of the Android device you want to target export ANDROID_NDK_ARCH="arm" diff --git a/android-prepare-toolchain.sh b/android-prepare-toolchain.sh index a0e37a054..d89e071e2 100755 --- a/android-prepare-toolchain.sh +++ b/android-prepare-toolchain.sh @@ -5,7 +5,7 @@ [ -z ${ANDROID_NDK_ARCH+x} ] && export ANDROID_NDK_ARCH="arm" [ -z ${ANDROID_NDK_ABI_VER+x} ] && export ANDROID_NDK_ABI_VER="4.9" [ -z ${ANDROID_PLATFORM_VER+x} ] && export ANDROID_PLATFORM_VER="18" -[ -z ${NDK_TOOLCHAIN_PATH+x} ] && export NDK_TOOLCHAIN_PATH="/home/$(whoami)/Development/android-toolchains/retroshare-android-${ANDROID_PLATFORM_VER}-${ANDROID_NDK_ARCH}-abi${ANDROID_NDK_ABI_VER}/" +[ -z ${NDK_TOOLCHAIN_PATH+x} ] && export NDK_TOOLCHAIN_PATH="${HOME}/Builds/android-toolchains/retroshare-android-${ANDROID_PLATFORM_VER}-${ANDROID_NDK_ARCH}-abi${ANDROID_NDK_ABI_VER}/" [ -z ${HOST_NUM_CPU+x} ] && export HOST_NUM_CPU=4 runDir="$(pwd)" diff --git a/build_scripts/Debian+Ubuntu/changelog b/build_scripts/Debian+Ubuntu/changelog index fa7a08c16..72dfeb83b 100644 --- a/build_scripts/Debian+Ubuntu/changelog +++ b/build_scripts/Debian+Ubuntu/changelog @@ -1,5 +1,46 @@ retroshare (0.6.3-1.XXXXXX~YYYYYY) YYYYYY; urgency=low + d61a5cd csoler Mon, 2 Oct 2017 22:23:26 +0200 attempt at fixing the re-hash bug. Now only using canonicalized filenames in hash cache + 22942dc csoler Sun, 1 Oct 2017 20:20:26 +0200 fixed bug causing suffix/prefix lists to contain an empty string + bc05aaa csoler Sat, 30 Sep 2017 18:58:31 +0200 switched tokenQueue speed to 10 calls/sec, thus improving loading speeds. Still need to test for CPU load + 65977c0 csoler Sat, 30 Sep 2017 18:29:29 +0200 Merge pull request #1054 from PhenomRetroShare/Fix_GccWarningd + 06ebaa5 csoler Sat, 30 Sep 2017 18:28:24 +0200 Merge pull request #1055 from felisucoibi/master + 06063c5 felisu Sat, 30 Sep 2017 11:34:18 +0200 removed extra space + b84c898 felisu Sat, 30 Sep 2017 00:29:07 +0200 Removed ... from text + ef6fd38 felisu Thu, 28 Sep 2017 22:42:30 +0200 Changed prefixes and sifixes for startign with and ending with to be understandable. + 7f851f6 Phenom Thu, 28 Sep 2017 19:24:26 +0200 Fix Gcc Warning in p3banlist + e66c0d1 csoler Wed, 27 Sep 2017 22:26:14 +0200 removed updateTotals() method. Replaced it with costless on-the-fly update of totals + a00d2c9 csoler Tue, 26 Sep 2017 21:12:17 +0200 changed lobby to char room in chat system msgs + 8eae374 csoler Mon, 25 Sep 2017 22:14:34 +0200 Merge pull request #1053 from csoler/v0.6-FT + 0d9b6e7 csoler Mon, 25 Sep 2017 22:09:35 +0200 fixd broken layout + cbeefda csoler Mon, 25 Sep 2017 21:35:59 +0200 Merge pull request #744 from RetroPooh/tunnel-names + f98f16f Pooh Mon, 25 Sep 2017 22:31:49 +0300 Update TransfersDialog.cpp + 5970ff2 csoler Mon, 25 Sep 2017 21:14:30 +0200 Merge pull request #1052 from csoler/v0.6-FT + 40cda11 csoler Mon, 25 Sep 2017 21:05:42 +0200 removed delay when calling forceUpdate() on directories + b969f31 csoler Mon, 25 Sep 2017 20:59:32 +0200 Merge pull request #1050 from csoler/v0.6-FT + d8cb3fe csoler Mon, 25 Sep 2017 20:56:35 +0200 fixed not rehashing files that already exist in a different directory pointed by a symlink + 72dc1c7 Pooh Mon, 25 Sep 2017 21:50:12 +0300 Update TransfersDialog.cpp + c47e6f6 Pooh Mon, 25 Sep 2017 21:43:41 +0300 Merge branch 'master' into tunnel-names + 4766a89 csoler Sun, 24 Sep 2017 23:51:47 +0200 added an additional hard limit to directory depth, just in case + f497905 csoler Sun, 24 Sep 2017 23:43:52 +0200 fixed limitation of directory depth when indexing files + abd7e25 csoler Sun, 24 Sep 2017 18:14:45 +0200 added checks for mMaxShareDepth and mIgnoreDuplicates + 2a99df4 csoler Sun, 24 Sep 2017 17:53:06 +0200 added UI and parameters for two new options in shared files: max share depth and ignore duplicates + 932eb49 csoler Sat, 23 Sep 2017 13:49:53 +0200 Merge pull request #1049 from Steve-V/minor-spelling-fix + 82c9c95 Steve- Fri, 22 Sep 2017 19:58:13 -0500 spelling fix ommitted -> omitted + 73cbf6c csoler Thu, 21 Sep 2017 23:47:11 +0200 Merge pull request #1047 from csoler/v0.6-FT + a2ccf97 csoler Wed, 20 Sep 2017 22:57:32 +0200 removed the 4M files limit on 64bits systems + 308f8ca defnax Tue, 19 Sep 2017 18:34:58 +0200 update qrc file + 58ccf0b csoler Mon, 18 Sep 2017 22:50:35 +0200 Merge pull request #1045 from csoler/v0.6-FT + bc2c9d5 csoler Mon, 18 Sep 2017 22:49:51 +0200 adding pending transfers 100 at a time instead of 1 at a time when loading + 52ca6d0 defnax Mon, 18 Sep 2017 20:49:40 +0200 Added Crystall Ball to emote file + bf7d346 csoler Sun, 17 Sep 2017 20:27:44 +0200 Merge pull request #1043 from csoler/v0.6-FT + e21dec4 csoler Sun, 17 Sep 2017 20:26:46 +0200 Merge pull request #1038 from PhenomRetroShare/Add_UploadUserColumn + d8f621e csoler Sun, 17 Sep 2017 20:14:45 +0200 make forceDirectoryCheck() to desactivate the pause, in case it was left activated + + -- Retroshare Dev Team Sat, 14 Oct 2017 15:00:00 +0100 + +retroshare (0.6.3-1.20170917.c2f321d7~trusty) trusty; urgency=low + 46d26bf csoler Tue, 12 Sep 2017 21:05:59 +0200 updated ubuntu packagign script f3c627d csoler Sat, 16 Sep 2017 22:13:53 +0200 Merge pull request #1042 from csoler/v0.6-FT 8189d66 csoler Sat, 16 Sep 2017 22:13:15 +0200 fixed bug causing anon FT to not resume when restarting RS when the server enforces encryption diff --git a/build_scripts/Debian+Ubuntu/control.artful b/build_scripts/Debian+Ubuntu/control.artful new file mode 100644 index 000000000..80b452523 --- /dev/null +++ b/build_scripts/Debian+Ubuntu/control.artful @@ -0,0 +1,46 @@ +Source: retroshare +Section: devel +Priority: standard +Maintainer: Cyril Soler +Build-Depends: debhelper (>= 7), libglib2.0-dev, libupnp-dev, libssl-dev, libxss-dev, libgnome-keyring-dev, libbz2-dev, libspeex-dev, libspeexdsp-dev, libxslt1-dev, cmake, libcurl4-openssl-dev, libopencv-dev, tcl8.6, libsqlcipher-dev, libmicrohttpd-dev, libavcodec-dev, qtmultimedia5-dev, qttools5-dev, libqt5x11extras5-dev, qt5-default +Standards-Version: 3.9.6 +Homepage: http://retroshare.sourceforge.net + +Package: retroshare-voip-plugin +Architecture: any +Conflicts: retroshare06-voip-plugin +Depends: ${shlibs:Depends}, ${misc:Depends}, retroshare, libspeex1, libspeexdsp1, libqt5multimedia5 +Description: RetroShare VOIP plugin + This package provides a plugin for RetroShare, a secured Friend-to-Friend communication + plateform. The plugin adds voice-over-IP functionality to the private chat window. Both + friends chatting together need the plugin installed to be able to talk together. + +Package: retroshare-feedreader-plugin +Architecture: any +Conflicts: retroshare06-feedreader-plugin +Depends: ${shlibs:Depends}, ${misc:Depends}, retroshare +Description: RetroShare FeedReader plugin + This package provides a plugin for RetroShare, a secured Friend-to-Friend communication + plateform. The plugin adds a RSS feed reader tab to retroshare. + +Package: retroshare-nogui +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, gnome-keyring +Conflicts: retroshare,retroshare06-nogui +Description: Secure communication with friends + This is the command-line client for RetroShare network. This client + can be contacted and talked-to using SSL. Clients exist for portable + devices running e.g. Android. + +Package: retroshare +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, gnome-keyring +Conflicts: retroshare-nogui,retroshare06 +Description: Secure communication with friends + RetroShare is a Open Source cross-platform, private and secure decentralised + commmunication platform. It lets you to securely chat and share files with your + friends and family, using a web-of-trust to authenticate peers and OpenSSL to + encrypt all communication. RetroShare provides filesharing, chat, messages, + forums and channels. + + diff --git a/build_scripts/Debian+Ubuntu/makeSourcePackage.sh b/build_scripts/Debian+Ubuntu/makeSourcePackage.sh index e2cf2f79c..9e3bd4fff 100755 --- a/build_scripts/Debian+Ubuntu/makeSourcePackage.sh +++ b/build_scripts/Debian+Ubuntu/makeSourcePackage.sh @@ -55,7 +55,7 @@ while [ ${#} -gt 0 ]; do done if test "${dist}" = "" ; then - dist="precise trusty vivid xenial zesty" + dist="precise trusty xenial zesty artful" fi echo Attempting to get revision number... @@ -121,23 +121,11 @@ for i in ${dist}; do echo copying changelog for ${i} sed -e s/XXXXXX/"${rev}"/g -e s/YYYYYY/"${i}"/g ../changelog > debian/changelog - if test "${i}" = "lucid" ; then - cp ../control.ubuntu_lucid debian/control - elif test "${i}" = "zesty" ; then - cp ../control.zesty debian/control - elif test "${i}" = "squeeze" ; then - cp ../control.squeeze_bubba3 debian/control - elif test "${i}" = "precise" ; then - cp ../control.precise debian/control - elif test "${i}" = "xenial" ; then - cp ../control.xenial debian/control - elif test "${i}" = "yakkety" ; then - cp ../control.yakkety debian/control - elif test "${i}" = "stretch" ; then - cp ../control.${i} debian/control - elif test "${i}" = "jessie" ; then - cp ../control.${i} debian/control + if test -f ../control."${i}" ; then + echo \/\!\\ Using specific control file for distribution "${i}" + cp ../control."${i}" debian/control else + echo Using standard control file control."${i}" for distribution "${i}" cp ../debian/control debian/control fi diff --git a/libresapi/src/webui-src/README.md b/libresapi/src/webui-src/README.md index 148068918..8174097e3 100644 --- a/libresapi/src/webui-src/README.md +++ b/libresapi/src/webui-src/README.md @@ -31,32 +31,32 @@ CONTRIBUTE TODO ---- -[ ] make stylesheets or find reusable sass/css components + - [ ] make stylesheets or find reusable sass/css components google material design has nice rules for color, spacing and everything: https://www.google.de/design/spec/material-design/introduction.html -[ ] find icons, maybe use google material design iconfont -[X] use urls/mithril routing for the menu. urls could replace state stored in rs.content -[X] drag and drop private key upload and import -[X] link from peer location to chat (use urls and mithril routing) -[X] add/remove friend, own cert -[X] downloads, search -[ ] make reusable infinite list controller, the js part to load data from Pagination.h (tweak Pagination.h to make everything work) + - [ ] find icons, maybe use google material design iconfont + - [X] use urls/mithril routing for the menu. urls could replace state stored in rs.content + - [X] drag and drop private key upload and import + - [X] link from peer location to chat (use urls and mithril routing) + - [X] add/remove friend, own cert + - [X] downloads, search + - [ ] make reusable infinite list controller, the js part to load data from Pagination.h (tweak Pagination.h to make everything work) should provide forward, backward and follow-list-end -[ ] backend: view/create identities -[ ] backend: chat lobby participants list -[X] chat: send_message -[ ] backend: chat typing notifications -[ ] make routines to handle retroshare links -[ ] backend: edit shared folders -[ ] backend: view shared files -[ ] redirect if a url is not usable in the current runstate (e.g. redirect from login page to home page, after login) -[X] sort friendslist + - [ ] backend: view/create identities + - [ ] backend: chat lobby participants list + - [X] chat: send_message + - [ ] backend: chat typing notifications + - [ ] make routines to handle retroshare links + - [ ] backend: edit shared folders + - [ ] backend: view shared files + - [ ] redirect if a url is not usable in the current runstate (e.g. redirect from login page to home page, after login) + - [X] sort friendslist need 4 master ------------- -[X] unsubscribe lobby -[X] unread chat message counter in menu -[X] list chat-lobby participants -[X] creating app.js on build (no need for npm on regulary build) + - [X] unsubscribe lobby + - [X] unread chat message counter in menu + - [X] list chat-lobby participants + - [X] creating app.js on build (no need for npm on regulary build) url-handling (brainstorming) ---------------------------- diff --git a/libretroshare/src/file_sharing/dir_hierarchy.cc b/libretroshare/src/file_sharing/dir_hierarchy.cc index bc8faf0e9..9ec6d2088 100644 --- a/libretroshare/src/file_sharing/dir_hierarchy.cc +++ b/libretroshare/src/file_sharing/dir_hierarchy.cc @@ -35,6 +35,8 @@ //#define DEBUG_DIRECTORY_STORAGE 1 +typedef FileListIO::read_error read_error; + /******************************************************************************************************************/ /* Internal File Hierarchy Storage */ /******************************************************************************************************************/ @@ -1041,22 +1043,6 @@ bool InternalFileHierarchyStorage::save(const std::string& fname) } } -class read_error -{ -public: - read_error(unsigned char *sec,uint32_t size,uint32_t offset,uint8_t expected_tag) - { - std::ostringstream s ; - s << "At offset " << offset << "/" << size << ": expected section tag " << std::hex << (int)expected_tag << std::dec << " but got " << RsUtil::BinToHex(&sec[offset],std::min((int)size-(int)offset, 15)) << "..." << std::endl; - err_string = s.str(); - } - read_error(const std::string& s) : err_string(s) {} - - const std::string& what() const { return err_string ; } -private: - std::string err_string ; -}; - bool InternalFileHierarchyStorage::load(const std::string& fname) { unsigned char *buffer = NULL ; diff --git a/libretroshare/src/file_sharing/file_tree.cc b/libretroshare/src/file_sharing/file_tree.cc new file mode 100644 index 000000000..92ee6a362 --- /dev/null +++ b/libretroshare/src/file_sharing/file_tree.cc @@ -0,0 +1,298 @@ +#include +#include +#include + +#include "file_sharing_defaults.h" +#include "filelist_io.h" +#include "file_tree.h" + +std::string FileTreeImpl::toRadix64() const +{ + unsigned char *buff = NULL ; + uint32_t size = 0 ; + + serialise(buff,size) ; + + std::string res ; + + Radix64::encode(buff,size,res) ; + + free(buff) ; + return res ; +} + +FileTree *FileTree::create(const std::string& radix64_string) +{ + FileTreeImpl *ft = new FileTreeImpl ; + + std::vector mem = Radix64::decode(radix64_string); + ft->deserialise(mem.data(),mem.size()) ; + + return ft ; +} + +void FileTreeImpl::recurs_buildFileTree(FileTreeImpl& ft,uint32_t index,const DirDetails& dd,bool remote,bool remove_top_dirs) +{ + if(ft.mDirs.size() <= index) + ft.mDirs.resize(index+1) ; + + if(remove_top_dirs) + ft.mDirs[index].name = RsDirUtil::getTopDir(dd.name) ; + else + ft.mDirs[index].name = dd.name ; + + ft.mDirs[index].subfiles.clear(); + ft.mDirs[index].subdirs.clear(); + + DirDetails dd2 ; + + FileSearchFlags flags = remote?RS_FILE_HINTS_REMOTE:RS_FILE_HINTS_LOCAL ; + + for(uint32_t i=0;iRequestDirDetails(dd.children[i].ref,dd2,flags)) + { + if(dd.children[i].type == DIR_TYPE_FILE) + { + FileTree::FileData f ; + f.name = dd2.name ; + f.size = dd2.count ; + f.hash = dd2.hash ; + + ft.mDirs[index].subfiles.push_back(ft.mFiles.size()) ; + ft.mFiles.push_back(f) ; + + ft.mTotalFiles++ ; + ft.mTotalSize += f.size ; + } + else if(dd.children[i].type == DIR_TYPE_DIR) + { + ft.mDirs[index].subdirs.push_back(ft.mDirs.size()); + recurs_buildFileTree(ft,ft.mDirs.size(),dd2,remote,remove_top_dirs) ; + } + else + std::cerr << "(EE) Unsupported DirDetails type." << std::endl; + } + else + std::cerr << "(EE) Cannot request dir details for pointer " << dd.children[i].ref << std::endl; +} + +bool FileTreeImpl::getDirectoryContent(uint32_t index,std::string& name,std::vector& subdirs,std::vector& subfiles) const +{ + if(index >= mDirs.size()) + return false ; + + name = mDirs[index].name; + subdirs = mDirs[index].subdirs ; + + subfiles.clear() ; + for(uint32_t i=0;i= mDirs.size()) + { + std::cerr << "(EE) inconsistent FileTree structure" << std::endl; + return; + } + std::cerr << indent << mDirs[index].name << std::endl; + + for(uint32_t i=0;i& subdirs,std::vector& subfiles) const ; + virtual void print() const ; + + bool serialise(unsigned char *& data,uint32_t& data_size) const ; + bool deserialise(unsigned char* data, uint32_t data_size) ; + +protected: + void recurs_print(uint32_t index,const std::string& indent) const; + + static void recurs_buildFileTree(FileTreeImpl& ft, uint32_t index, const DirDetails& dd, bool remote, bool remove_top_dirs); + + struct DirData { + std::string name; + std::vector subdirs ; + std::vector subfiles ; + }; + std::vector mFiles ; + std::vector mDirs ; + + friend class FileTree ; +}; diff --git a/libretroshare/src/file_sharing/filelist_io.cc b/libretroshare/src/file_sharing/filelist_io.cc index 618d6c6bb..15d4d1b53 100644 --- a/libretroshare/src/file_sharing/filelist_io.cc +++ b/libretroshare/src/file_sharing/filelist_io.cc @@ -22,12 +22,22 @@ * Please report all bugs and problems to "retroshare.project@gmail.com". * */ + +#include #include "retroshare/rsids.h" #include "pqi/authssl.h" #include "util/rsdir.h" +#include "util/rsprint.h" #include "serialiser/rsbaseserial.h" #include "filelist_io.h" +FileListIO::read_error::read_error(unsigned char *sec,uint32_t size,uint32_t offset,uint8_t expected_tag) +{ + std::ostringstream s ; + s << "At offset " << offset << "/" << size << ": expected section tag " << std::hex << (int)expected_tag << std::dec << " but got " << RsUtil::BinToHex(&sec[offset],std::min((int)size-(int)offset, 15)) << "..." << std::endl; + err_string = s.str(); +} + template<> bool FileListIO::serialise(unsigned char *buff,uint32_t size,uint32_t& offset,const uint32_t & val) { return setRawUInt32(buff,size,&offset,val) ; } template<> bool FileListIO::serialise(unsigned char *buff,uint32_t size,uint32_t& offset,const uint64_t & val) { return setRawUInt64(buff,size,&offset,val) ; } template<> bool FileListIO::serialise(unsigned char *buff,uint32_t size,uint32_t& offset,const std::string & val) { return setRawString(buff,size,&offset,val) ; } diff --git a/libretroshare/src/file_sharing/filelist_io.h b/libretroshare/src/file_sharing/filelist_io.h index 0fcdb00fb..8eccd6b27 100644 --- a/libretroshare/src/file_sharing/filelist_io.h +++ b/libretroshare/src/file_sharing/filelist_io.h @@ -36,6 +36,7 @@ // WARNING: the encoding is system-dependent, so this should *not* be used to exchange data between computers. static const uint32_t FILE_LIST_IO_LOCAL_DIRECTORY_STORAGE_VERSION_0001 = 0x00000001 ; +static const uint32_t FILE_LIST_IO_LOCAL_DIRECTORY_TREE_VERSION_0001 = 0x00010001 ; static const uint8_t FILE_LIST_IO_TAG_UNKNOWN = 0x00 ; static const uint8_t FILE_LIST_IO_TAG_LOCAL_DIRECTORY_VERSION = 0x01 ; @@ -65,6 +66,7 @@ static const uint8_t FILE_LIST_IO_TAG_RAW_NUMBER = 0x62 ; static const uint32_t SECTION_HEADER_MAX_SIZE = 6 ; // section tag (1 byte) + size (max = 5 bytes) + class FileListIO { public: @@ -93,7 +95,19 @@ public: return deserialise(buff,buff_size,offset,val); } - static bool writeField( unsigned char*&buff,uint32_t& buff_size,uint32_t& offset,uint8_t section_tag,const unsigned char * val,uint32_t size) ; + class read_error + { + public: + read_error(unsigned char *sec,uint32_t size,uint32_t offset,uint8_t expected_tag); + read_error(const std::string& s) : err_string(s) {} + + const std::string& what() const { return err_string ; } + private: + std::string err_string ; + }; + + + static bool writeField( unsigned char*&buff,uint32_t& buff_size,uint32_t& offset,uint8_t section_tag,const unsigned char * val,uint32_t size) ; static bool readField (const unsigned char *buff,uint32_t buff_size,uint32_t& offset,uint8_t check_section_tag, unsigned char *& val,uint32_t& size) ; template static bool serialise(unsigned char *buff,uint32_t size,uint32_t& offset,const T& val) ; diff --git a/libretroshare/src/libretroshare.pro b/libretroshare/src/libretroshare.pro index d96e7dc7f..1521f5574 100644 --- a/libretroshare/src/libretroshare.pro +++ b/libretroshare/src/libretroshare.pro @@ -47,6 +47,7 @@ file_lists { file_sharing/directory_updater.h \ file_sharing/rsfilelistitems.h \ file_sharing/dir_hierarchy.h \ + file_sharing/file_tree.h \ file_sharing/file_sharing_defaults.h SOURCES *= file_sharing/p3filelists.cc \ @@ -55,6 +56,7 @@ file_lists { file_sharing/directory_storage.cc \ file_sharing/directory_updater.cc \ file_sharing/dir_hierarchy.cc \ + file_sharing/file_tree.cc \ file_sharing/rsfilelistitems.cc } @@ -916,7 +918,7 @@ test_bitdht { ################################# Android ##################################### android-g++ { -## ifaddrs is missing on Android add them don't use the one from +## ifaddrs is missing on Android to add them don't use the one from ## https://github.com/morristech/android-ifaddrs ## because they crash, use QNetworkInterface from Qt instead CONFIG *= qt diff --git a/libretroshare/src/retroshare/rsfiles.h b/libretroshare/src/retroshare/rsfiles.h index 313ca26eb..d7efb2937 100644 --- a/libretroshare/src/retroshare/rsfiles.h +++ b/libretroshare/src/retroshare/rsfiles.h @@ -134,6 +134,38 @@ struct SharedDirStats uint64_t total_shared_size ; }; +// This class represents a tree of directories and files, only with their names size and hash. It is used to create collection links in the GUI +// and to transmit directory information between services. This class is independent from the existing FileHierarchy classes used in storage because +// we need a very copact serialization and storage size since we create links with it. Besides, we cannot afford to risk the leak of other local information +// by using the orignal classes. + +class FileTree +{ +public: + virtual ~FileTree() {} + + static FileTree *create(const DirDetails& dd, bool remote, bool remove_top_dirs = true) ; + static FileTree *create(const std::string& radix64_string) ; + + virtual std::string toRadix64() const =0 ; + + // These methods allow the user to browse the hierarchy + + struct FileData { + std::string name ; + uint64_t size ; + RsFileHash hash ; + }; + + virtual uint32_t root() const { return 0;} + virtual bool getDirectoryContent(uint32_t index,std::string& name,std::vector& subdirs,std::vector& subfiles) const = 0; + + virtual void print() const=0; + + uint32_t mTotalFiles ; + uint64_t mTotalSize ; +}; + class RsFiles { public: diff --git a/libretroshare/src/rsserver/rsaccounts.cc b/libretroshare/src/rsserver/rsaccounts.cc index 0f0a6ac94..6bd36b04d 100644 --- a/libretroshare/src/rsserver/rsaccounts.cc +++ b/libretroshare/src/rsserver/rsaccounts.cc @@ -803,7 +803,8 @@ static bool checkAccount(std::string accountdir, AccountDetails &account,std::ma /* Use RetroShare's exe dir */ dataDirectory = "."; - +#elif defined(ANDROID) + dataDirectory = defaultBaseDirectory()+"/usr/share/retroshare"; #elif defined(DATA_DIR) dataDirectory = DATA_DIR; // For all other OS the data directory must be set in libretroshare.pro diff --git a/retroshare-gui/src/gui/FileTransfer/SearchDialog.cpp b/retroshare-gui/src/gui/FileTransfer/SearchDialog.cpp index 4607a2541..facb8b60d 100644 --- a/retroshare-gui/src/gui/FileTransfer/SearchDialog.cpp +++ b/retroshare-gui/src/gui/FileTransfer/SearchDialog.cpp @@ -30,7 +30,7 @@ #include "gui/RetroShareLink.h" #include "retroshare-gui/RsAutoUpdatePage.h" #include "gui/msgs/MessageComposer.h" -#include "gui/common/RsCollectionFile.h" +#include "gui/common/RsCollection.h" #include "gui/common/FilesDefs.h" #include "gui/common/RsUrlHandler.h" #include "gui/settings/rsharesettings.h" @@ -345,7 +345,7 @@ void SearchDialog::searchResultWidgetCustomPopupMenu( QPoint /*point*/ ) QList item =ui.searchResultWidget->selectedItems() ; if (item.at(0)->data(SR_DATA_COL, SR_ROLE_LOCAL).toBool()) { contextMnu.addAction(QIcon(IMAGE_OPENFOLDER), tr("Open Folder"), this, SLOT(openFolderSearch())) ; - if (item.at(0)->text(SR_NAME_COL).endsWith(RsCollectionFile::ExtensionString)) { + if (item.at(0)->text(SR_NAME_COL).endsWith(RsCollection::ExtensionString)) { add_CollActions = true ; }//if (item.at(0)->text(SR_NAME_COL).endsWith(RsCollectionFile::ExtensionString)) }//if (item.at(0)->data(SR_DATA_COL, SR_ROLE_LOCAL).toBool()) @@ -448,7 +448,7 @@ void SearchDialog::collCreate() }//if (!item->text(SR_HASH_COL).isEmpty()) }//for (int i = 0; i < numdls; ++i) - RsCollectionFile(dirVec).openNewColl(this); + RsCollection(dirVec).openNewColl(this); } void SearchDialog::collModif() @@ -476,8 +476,8 @@ void SearchDialog::collModif() QFileInfo qinfo; qinfo.setFile(QString::fromUtf8(path.c_str())); if (qinfo.exists()) { - if (qinfo.absoluteFilePath().endsWith(RsCollectionFile::ExtensionString)) { - RsCollectionFile collection; + if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) { + RsCollection collection; collection.openColl(qinfo.absoluteFilePath()); }//if (qinfo.absoluteFilePath().endsWith(RsCollectionFile::ExtensionString)) }//if (qinfo.exists()) @@ -508,8 +508,8 @@ void SearchDialog::collView() QFileInfo qinfo; qinfo.setFile(QString::fromUtf8(path.c_str())); if (qinfo.exists()) { - if (qinfo.absoluteFilePath().endsWith(RsCollectionFile::ExtensionString)) { - RsCollectionFile collection; + if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) { + RsCollection collection; collection.openColl(qinfo.absoluteFilePath(), true); }//if (qinfo.absoluteFilePath().endsWith(RsCollectionFile::ExtensionString)) }//if (qinfo.exists()) @@ -540,8 +540,8 @@ void SearchDialog::collOpen() QFileInfo qinfo; qinfo.setFile(QString::fromUtf8(path.c_str())); if (qinfo.exists()) { - if (qinfo.absoluteFilePath().endsWith(RsCollectionFile::ExtensionString)) { - RsCollectionFile collection; + if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) { + RsCollection collection; if (collection.load(qinfo.absoluteFilePath())) { collection.downloadFiles(); return; @@ -552,7 +552,7 @@ void SearchDialog::collOpen() } } - RsCollectionFile collection; + RsCollection collection; if (collection.load(this)) { collection.downloadFiles(); }//if (collection.load(this)) diff --git a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp index 9b1dac00d..afba50f7b 100644 --- a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp +++ b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp @@ -33,13 +33,15 @@ #include "SharedFilesDialog.h" #include "gui/notifyqt.h" +#include "gui/MainWindow.h" #include "gui/RemoteDirModel.h" #include "gui/RetroShareLink.h" #include "gui/ShareManager.h" #include "gui/common/PeerDefs.h" -#include "gui/common/RsCollectionFile.h" +#include "gui/common/RsCollection.h" #include "gui/msgs/MessageComposer.h" #include "gui/settings/AddFileAssociationDialog.h" +#include "gui/gxschannels/GxsChannelDialog.h" #include "gui/settings/rsharesettings.h" #include "util/QtVersion.h" #include "util/RsAction.h" @@ -61,6 +63,7 @@ #define IMAGE_OPENFOLDER ":/images/folderopen.png" #define IMAGE_OPENFILE ":/images/fileopen.png" #define IMAGE_LIBRARY ":/images/library.png" +#define IMAGE_CHANNEL ":/images/channels32.png" #define IMAGE_COLLCREATE ":/images/library_add.png" #define IMAGE_COLLMODIF ":/images/library_edit.png" #define IMAGE_COLLVIEW ":/images/library_view.png" @@ -154,9 +157,6 @@ SharedFilesDialog::SharedFilesDialog(RetroshareDirModel *_tree_model,RetroshareD connect(notify, SIGNAL(filesPreModChanged(bool)), this, SLOT(preModDirectories(bool))); connect(notify, SIGNAL(filesPostModChanged(bool)), this, SLOT(postModDirectories(bool))); -//== connect(ui.localButton, SIGNAL(toggled(bool)), this, SLOT(showFrame(bool))); -//== connect(ui.remoteButton, SIGNAL(toggled(bool)), this, SLOT(showFrameRemote(bool))); -//== connect(ui.splittedButton, SIGNAL(toggled(bool)), this, SLOT(showFrameSplitted(bool))); connect(ui.viewType_CB, SIGNAL(currentIndexChanged(int)), this, SLOT(changeCurrentViewModel(int))); connect( ui.dirTreeView, SIGNAL( customContextMenuRequested( QPoint ) ), this, SLOT( spawnCustomPopupMenu( QPoint ) ) ); @@ -499,36 +499,24 @@ void RemoteSharedFilesDialog::spawnCustomPopupMenu( QPoint point ) QMenu contextMnu( this ) ; - //bool bIsRsColl = currentFile.endsWith(RsCollectionFile::ExtensionString); collCreateAct->setEnabled(true); - //collModifAct->setEnabled(bIsRsColl); - //collViewAct->setEnabled(bIsRsColl); collOpenAct->setEnabled(true); QMenu collectionMenu(tr("Collection"), this); collectionMenu.setIcon(QIcon(IMAGE_LIBRARY)); collectionMenu.addAction(collCreateAct); - //collectionMenu.addAction(collModifAct); - //collectionMenu.addAction(collViewAct); collectionMenu.addAction(collOpenAct); QAction *downloadAct = new QAction(QIcon(IMAGE_DOWNLOAD), tr( "Download" ), &contextMnu ) ; connect( downloadAct , SIGNAL( triggered() ), this, SLOT( downloadRemoteSelected() ) ) ; contextMnu.addAction( downloadAct) ; - if ( type == DIR_TYPE_FILE ) { - //QAction *copyremotelinkAct = new QAction(QIcon(IMAGE_COPYLINK), tr( "Copy retroshare Link" ), &contextMnu ) ; - //connect( copyremotelinkAct , SIGNAL( triggered() ), this, SLOT( copyLink() ) ) ; + contextMnu.addSeparator() ;//------------------------------------ + contextMnu.addAction( copylinkAct) ; + contextMnu.addAction( sendlinkAct) ; + contextMnu.addSeparator() ;//------------------------------------ + contextMnu.addAction(QIcon(IMAGE_MSG), tr("Recommend in a message to..."), this, SLOT(recommendFilesToMsg())) ; - //QAction *sendremotelinkAct = new QAction(QIcon(IMAGE_COPYLINK), tr( "Send retroshare Link" ), &contextMnu ) ; - //connect( sendremotelinkAct , SIGNAL( triggered() ), this, SLOT( sendremoteLinkTo( ) ) ) ; - - contextMnu.addSeparator() ;//------------------------------------ - contextMnu.addAction( copylinkAct) ; - contextMnu.addAction( sendlinkAct) ; - contextMnu.addSeparator() ;//------------------------------------ - contextMnu.addAction(QIcon(IMAGE_MSG), tr("Recommend in a message to"), this, SLOT(recommendFilesToMsg())) ; - }//if (type == DIR_TYPE_FILE) contextMnu.addSeparator() ;//------------------------------------ contextMnu.addMenu(&collectionMenu) ; @@ -542,7 +530,7 @@ QModelIndexList SharedFilesDialog::getSelected() QModelIndexList proxyList ; for (QModelIndexList::iterator index = list.begin(); index != list.end(); ++index ) { proxyList.append(proxyModel->mapToSource(*index)) ; - }//for (QModelIndexList::iterator index + } return proxyList ; } @@ -567,62 +555,59 @@ void RemoteSharedFilesDialog::downloadRemoteSelected() model -> downloadSelected(lst) ; } -void SharedFilesDialog::copyLink (const QModelIndexList& lst, bool remote) +void SharedFilesDialog::copyLinks(const QModelIndexList& lst, bool remote,QList& urls,bool& has_unhashed_files) { - std::vector dirVec; + std::vector dirVec; - model->getDirDetailsFromSelect(lst, dirVec); + model->getDirDetailsFromSelect(lst, dirVec); - QList urls ; + has_unhashed_files = false; - bool has_unhashed_files = false; + for (int i = 0, n = dirVec.size(); i < n; ++i) + { + const DirDetails& details = dirVec[i]; - for (int i = 0, n = dirVec.size(); i < n; ++i) - { - const DirDetails& details = dirVec[i]; + if (details.type == DIR_TYPE_DIR) + { + FileTree *ft = FileTree::create(details,remote) ; - if (details.type == DIR_TYPE_DIR) - { - for(uint32_t j=0;jprint(); - DirDetails details; - FileSearchFlags flags = remote?RS_FILE_HINTS_REMOTE:RS_FILE_HINTS_LOCAL ; + QString dir_name = QDir(QString::fromUtf8(details.name.c_str())).dirName(); - // do not recursive copy sub dirs. - if (!rsFiles->RequestDirDetails(dirStub.ref, details, flags) || details.type != DIR_TYPE_FILE) - continue; + RetroShareLink link = RetroShareLink::createCollection(dir_name,ft->mTotalSize,ft->mTotalFiles,QString::fromStdString(ft->toRadix64())) ; - if(details.hash.isNull()) - { - has_unhashed_files = true; - continue; - } + if(link.valid()) + urls.push_back(link) ; - RetroShareLink link = RetroShareLink::createFile(QString::fromUtf8(details.name.c_str()), details.count, details.hash.toStdString().c_str()); - if (link.valid()) { - urls.push_back(link) ; - } - } - } - else - { - if(details.hash.isNull()) + delete ft ; + } + else + { + if(details.hash.isNull()) { has_unhashed_files = true; continue; } - RetroShareLink link = RetroShareLink::createFile(QString::fromUtf8(details.name.c_str()), details.count, details.hash.toStdString().c_str()); - if (link.valid()) { - urls.push_back(link) ; - } - } - } + RetroShareLink link = RetroShareLink::createFile(QString::fromUtf8(details.name.c_str()), details.count, details.hash.toStdString().c_str()); + if (link.valid()) { + urls.push_back(link) ; + } + } + } +} + +void SharedFilesDialog::copyLink (const QModelIndexList& lst, bool remote) +{ + QList urls ; + bool has_unhashed_files = false; + + copyLinks(lst,remote,urls,has_unhashed_files) ; RSLinkClipboard::copyLinks(urls) ; if(has_unhashed_files) - QMessageBox::warning(NULL,tr("Some files have been omitted"),tr("Some files have been omitted because their hash is not available yet.")) ; + QMessageBox::warning(NULL,tr("Some files have been omitted"),tr("Some files have been omitted because they have not been indexed yet.")) ; } void SharedFilesDialog::copyLink() @@ -692,11 +677,11 @@ void SharedFilesDialog::collModif() QFileInfo qinfo; qinfo.setFile(QString::fromUtf8(path.c_str())); if (qinfo.exists()) { - if (qinfo.absoluteFilePath().endsWith(RsCollectionFile::ExtensionString)) { - RsCollectionFile collection; + if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) { + RsCollection collection; collection.openColl(qinfo.absoluteFilePath()); - }//if (qinfo.absoluteFilePath().endsWith(RsCollectionFile::ExtensionString)) - }//if (qinfo.exists()) + } + } } void SharedFilesDialog::collView() @@ -722,11 +707,11 @@ void SharedFilesDialog::collView() QFileInfo qinfo; qinfo.setFile(QString::fromUtf8(path.c_str())); if (qinfo.exists()) { - if (qinfo.absoluteFilePath().endsWith(RsCollectionFile::ExtensionString)) { - RsCollectionFile collection; + if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) { + RsCollection collection; collection.openColl(qinfo.absoluteFilePath(), true); - }//if (qinfo.absoluteFilePath().endsWith(RsCollectionFile::ExtensionString)) - }//if (qinfo.exists()) + } + } } void SharedFilesDialog::collOpen() @@ -752,8 +737,8 @@ void SharedFilesDialog::collOpen() QFileInfo qinfo; qinfo.setFile(QString::fromUtf8(path.c_str())); if (qinfo.exists()) { - if (qinfo.absoluteFilePath().endsWith(RsCollectionFile::ExtensionString)) { - RsCollectionFile collection; + if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) { + RsCollection collection; if (collection.load(qinfo.absoluteFilePath())) { collection.downloadFiles(); return; @@ -763,10 +748,10 @@ void SharedFilesDialog::collOpen() } } - RsCollectionFile collection; + RsCollection collection; if (collection.load(this)) { collection.downloadFiles(); - }//if (collection.load(this)) + } } void LocalSharedFilesDialog::playselectedfiles() @@ -995,7 +980,7 @@ void LocalSharedFilesDialog::spawnCustomPopupMenu( QPoint point ) QMenu contextMnu(this) ; - bool bIsRsColl = currentFile.endsWith(RsCollectionFile::ExtensionString); + bool bIsRsColl = currentFile.endsWith(RsCollection::ExtensionString); collCreateAct->setEnabled(true); collModifAct->setEnabled(bIsRsColl); collViewAct->setEnabled(bIsRsColl); @@ -1011,6 +996,7 @@ void LocalSharedFilesDialog::spawnCustomPopupMenu( QPoint point ) switch (type) { case DIR_TYPE_DIR : contextMnu.addAction(openfolderAct) ; + contextMnu.addAction(copylinkAct) ; contextMnu.addSeparator() ;//------------------------------------ contextMnu.addMenu(&collectionMenu) ; break ; @@ -1023,14 +1009,51 @@ void LocalSharedFilesDialog::spawnCustomPopupMenu( QPoint point ) contextMnu.addSeparator() ;//------------------------------------ contextMnu.addMenu(&collectionMenu) ; contextMnu.addSeparator() ;//------------------------------------ - contextMnu.addAction(QIcon(IMAGE_MSG), tr("Recommend in a message to"), this, SLOT(recommendFilesToMsg())) ; + contextMnu.addAction(QIcon(IMAGE_MSG), tr("Recommend in a message to..."), this, SLOT(recommendFilesToMsg())) ; break ; default : return ; - }//switch (type) + } - contextMnu.exec(QCursor::pos()) ; + GxsChannelDialog *channelDialog = dynamic_cast(MainWindow::getPage(MainWindow::Channels)); + + if(channelDialog != NULL) + { + QMenu shareChannelMenu(tr("Share on channel...")) ; + shareChannelMenu.setIcon(QIcon(IMAGE_CHANNEL)); + + std::list grp_metas ; + channelDialog->getGroupList(grp_metas) ; + + for(auto it(grp_metas.begin());it!=grp_metas.end();++it) + if(IS_GROUP_PUBLISHER((*it).mSubscribeFlags)) + shareChannelMenu.addAction(QString::fromUtf8((*it).mGroupName.c_str()), this, SLOT(shareOnChannel()))->setData(QString::fromStdString((*it).mGroupId.toStdString())) ; + + contextMnu.addMenu(&shareChannelMenu) ; + contextMnu.exec(QCursor::pos()) ; // added here because the shareChannelMenu QMenu object is deleted afterwards + } + else + contextMnu.exec(QCursor::pos()) ; +} + +void LocalSharedFilesDialog::shareOnChannel() +{ + RsGxsGroupId groupId(qobject_cast(sender())->data().toString().toStdString()); + + GxsChannelDialog *channelDialog = dynamic_cast(MainWindow::getPage(MainWindow::Channels)); + + if(channelDialog == NULL) + return ; + + std::list files_info ; + + QList file_links_list ; + bool has_unhashed_files ; + + copyLinks(getSelected(),false,file_links_list,has_unhashed_files) ; + + channelDialog->shareOnChannel(groupId,file_links_list) ; } //============================================================================ diff --git a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h index e7ec1e2b0..885bab91a 100644 --- a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h +++ b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h @@ -24,6 +24,7 @@ #include #include "RsAutoUpdatePage.h" +#include "gui/RetroShareLink.h" #include "ui_SharedFilesDialog.h" class RetroshareDirModel; @@ -68,10 +69,6 @@ private slots: void collView(); void collOpen(); -//== void showFrame(bool show); -//== void showFrameRemote(bool show); -//== void showFrameSplitted(bool show); - void recommendFilesToMsg(); void indicatorChanged(int index); @@ -103,6 +100,7 @@ protected: //QMenu* contextMnu2; + void copyLinks(const QModelIndexList& lst, bool remote, QList& urls, bool& has_unhashed_files); void copyLink (const QModelIndexList& lst, bool remote); void FilterItems(); @@ -161,6 +159,7 @@ class LocalSharedFilesDialog : public SharedFilesDialog void runCommandForFile(); void tryToAddNewAssotiation(); void forceCheck(); + void shareOnChannel(); QAction* fileAssotiationAction(const QString fileName); diff --git a/retroshare-gui/src/gui/FileTransfer/TransfersDialog.cpp b/retroshare-gui/src/gui/FileTransfer/TransfersDialog.cpp index aecc53bef..b9e961457 100644 --- a/retroshare-gui/src/gui/FileTransfer/TransfersDialog.cpp +++ b/retroshare-gui/src/gui/FileTransfer/TransfersDialog.cpp @@ -31,7 +31,7 @@ #include #include -#include +#include #include #include @@ -50,7 +50,7 @@ #include "xprogressbar.h" #include #include "util/misc.h" -#include +#include #include "TransferUserNotify.h" #include "util/QtVersion.h" #include "util/RsFile.h" @@ -652,7 +652,8 @@ void TransfersDialog::downloadListCustomPopupMenu( QPoint /*point*/ ) QMenu contextMnu( this ); - if(!RSLinkClipboard::empty(RetroShareLink::TYPE_FILE)) add_PasteLink=true; + if(!RSLinkClipboard::empty(RetroShareLink::TYPE_FILE)) add_PasteLink=true; + if(!RSLinkClipboard::empty(RetroShareLink::TYPE_FILE_TREE)) add_PasteLink=true; if(!items.empty()) { @@ -666,8 +667,7 @@ void TransfersDialog::downloadListCustomPopupMenu( QPoint /*point*/ ) //Look only for first column == File List //Get Info for current item - if (rsFiles->FileDetails(fileHash - , RS_FILE_HINTS_DOWNLOAD, info)) { + if (rsFiles->FileDetails(fileHash, RS_FILE_HINTS_DOWNLOAD, info)) { /*const uint32_t FT_STATE_FAILED = 0x0000; *const uint32_t FT_STATE_OKAY = 0x0001; *const uint32_t FT_STATE_WAITING = 0x0002; @@ -677,22 +677,21 @@ void TransfersDialog::downloadListCustomPopupMenu( QPoint /*point*/ ) *const uint32_t FT_STATE_PAUSED = 0x0006; *const uint32_t FT_STATE_CHECKING_HASH = 0x0007; */ - if (info.downloadStatus == FT_STATE_WAITING) { + if (info.downloadStatus == FT_STATE_WAITING) atLeastOne_Waiting = true ; - }//if (info.downloadStatus == FT_STATE_WAITING) - if (info.downloadStatus == FT_STATE_DOWNLOADING) { + + if (info.downloadStatus == FT_STATE_DOWNLOADING) atLeastOne_Downloading=true ; - }//if (info.downloadStatus == FT_STATE_DOWNLOADING) + if (info.downloadStatus == FT_STATE_COMPLETE) { atLeastOne_Complete = true ; add_OpenFileOption = single ; - }//if (info.downloadStatus == FT_STATE_COMPLETE) - if (info.downloadStatus == FT_STATE_QUEUED) { + } + if (info.downloadStatus == FT_STATE_QUEUED) atLeastOne_Queued = true ; - }//if(info.downloadStatus == FT_STATE_QUEUED) - if (info.downloadStatus == FT_STATE_PAUSED) { + + if (info.downloadStatus == FT_STATE_PAUSED) atLeastOne_Paused = true ; - }//if (info.downloadStatus == FT_STATE_PAUSED) size_t pos = info.fname.find_last_of('.') ; if (pos != std::string::npos) { @@ -700,23 +699,23 @@ void TransfersDialog::downloadListCustomPopupMenu( QPoint /*point*/ ) if (misc::isPreviewable(info.fname.substr(pos + 1).c_str())) { add_PreviewOption = (info.downloadStatus != FT_STATE_COMPLETE) ; add_PlayOption = !add_PreviewOption ; - }// if (misc::isPreviewable(info.fname.substr(pos + 1).c_str())) + } // Check if the file is a collection - if (RsCollectionFile::ExtensionString == info.fname.substr(pos + 1).c_str()) { + if (RsCollection::ExtensionString == info.fname.substr(pos + 1).c_str()) { add_CollActions = (info.downloadStatus == FT_STATE_COMPLETE); - }//if (RsCollectionFile::ExtensionString == info - }// if(pos != std::string::npos) + } + } - }// if FileDetails - }// for items iterate - }// if (!items.empty()) + } + } + } if (atLeastOne_Waiting || atLeastOne_Downloading || atLeastOne_Queued || atLeastOne_Paused) { contextMnu.addMenu( &prioritySpeedMenu) ; } if (atLeastOne_Queued) { contextMnu.addMenu( &priorityQueueMenu) ; - }//if (atLeastOne_Queued) + } if ( (!items.empty()) && (atLeastOne_Downloading || atLeastOne_Queued || atLeastOne_Waiting || atLeastOne_Paused)) { @@ -724,7 +723,7 @@ void TransfersDialog::downloadListCustomPopupMenu( QPoint /*point*/ ) if (single) { contextMnu.addAction( renameFileAct) ; - }//if (single) + } QMenu *directoryMenu = contextMnu.addMenu(QIcon(IMAGE_OPENFOLDER), tr("Set destination directory")) ; directoryMenu->addAction(specifyDestinationDirectoryAct) ; @@ -745,27 +744,24 @@ void TransfersDialog::downloadListCustomPopupMenu( QPoint /*point*/ ) act->setData(QString::fromUtf8( (*it).filename.c_str() ) ) ; connect(act, SIGNAL(triggered()), this, SLOT(setDestinationDirectory())) ; directoryMenu->addAction( act) ; - }//for (std::list::const_iterator it - }//if ( (!items.empty()) && + } + } - if (atLeastOne_Paused) { + if (atLeastOne_Paused) contextMnu.addAction(resumeAct) ; - }//if (atLeastOne_Paused) - if (atLeastOne_Downloading || atLeastOne_Queued || atLeastOne_Waiting) { + + if (atLeastOne_Downloading || atLeastOne_Queued || atLeastOne_Waiting) contextMnu.addAction(pauseAct) ; - }//if (atLeastOne_Downloading || atLeastOne_Queued || atLeastOne_Waiting) if (!atLeastOne_Complete && !items.empty()) { contextMnu.addAction(forceCheckAct) ; contextMnu.addAction(cancelAct) ; - }//if (!atLeastOne_Complete && !items.empty()) - if (add_PlayOption) { + } + if (add_PlayOption) contextMnu.addAction(playAct) ; - }//if (add_PlayOption) - if (atLeastOne_Paused || atLeastOne_Downloading || atLeastOne_Complete || add_PlayOption) { - contextMnu.addSeparator() ;//------------------------------------------------ - }//if (atLeastOne_Paused || + if (atLeastOne_Paused || atLeastOne_Downloading || atLeastOne_Complete || add_PlayOption) + contextMnu.addSeparator() ; if (single) { if (add_OpenFileOption) contextMnu.addAction( openFileAct) ; @@ -773,20 +769,20 @@ void TransfersDialog::downloadListCustomPopupMenu( QPoint /*point*/ ) contextMnu.addAction( openFolderAct) ; contextMnu.addAction( detailsFileAct) ; contextMnu.addSeparator() ;//-------------------------------------------- - }//if (single) + } contextMnu.addAction( clearCompletedAct) ; - contextMnu.addSeparator() ;//------------------------------------------------ + contextMnu.addSeparator() ; if (add_CopyLink) { contextMnu.addAction( copyLinkAct) ; - }//if (add_CopyLink) + } if (add_PasteLink) { contextMnu.addAction( pasteLinkAct) ; - }//if (add_PasteLink) + } if (add_CopyLink || add_PasteLink) { - contextMnu.addSeparator() ;//-------------------------------------------- - }//if (add_CopyLink || add_PasteLink) + contextMnu.addSeparator() ; + } if (DLLFilterModel->rowCount()>0 ) { contextMnu.addAction( expandAllDLAct ) ; @@ -1644,7 +1640,27 @@ void TransfersDialog::updateDetailsDialog() void TransfersDialog::pasteLink() { - RSLinkClipboard::process(RetroShareLink::TYPE_FILE); + QList links ; + + // We want to capture and process all links at once here, because we're possibly pasting a large collection of files. So we first + // merge all links into a single RsCollection and then process it. + + RsCollection col ; + RSLinkClipboard::pasteLinks(links,RetroShareLink::TYPE_FILE_TREE); + + for(QList::const_iterator it(links.begin());it!=links.end();++it) + { + FileTree *ft = FileTree::create((*it).radix().toStdString()) ; + + col.merge_in(*ft) ; + } + links.clear(); + RSLinkClipboard::pasteLinks(links,RetroShareLink::TYPE_FILE); + + for(QList::const_iterator it(links.begin());it!=links.end();++it) + col.merge_in((*it).name(),(*it).size(),RsFileHash((*it).hash().toStdString())) ; + + col.downloadFiles(); } void TransfersDialog::getDLSelectedItems(std::set *ids, std::set *rows) @@ -2155,7 +2171,7 @@ void TransfersDialog::collCreate() dirVec.push_back(details); }//for (it = items.begin(); - RsCollectionFile(dirVec).openNewColl(this); + RsCollection(dirVec).openNewColl(this); } void TransfersDialog::collModif() @@ -2180,12 +2196,12 @@ void TransfersDialog::collModif() QFileInfo qinfo; qinfo.setFile(QString::fromUtf8(path.c_str())); if (qinfo.exists()) { - if (qinfo.absoluteFilePath().endsWith(RsCollectionFile::ExtensionString)) { - RsCollectionFile collection; + if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) { + RsCollection collection; collection.openColl(qinfo.absoluteFilePath()); - }//if (qinfo.absoluteFilePath().endsWith(RsCollectionFile::ExtensionString)) - }//if (qinfo.exists()) - }//if (info.downloadStatus == FT_STATE_COMPLETE) + } + } + } } void TransfersDialog::collView() @@ -2210,12 +2226,12 @@ void TransfersDialog::collView() QFileInfo qinfo; qinfo.setFile(QString::fromUtf8(path.c_str())); if (qinfo.exists()) { - if (qinfo.absoluteFilePath().endsWith(RsCollectionFile::ExtensionString)) { - RsCollectionFile collection; + if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) { + RsCollection collection; collection.openColl(qinfo.absoluteFilePath(), true); - }//if (qinfo.absoluteFilePath().endsWith(RsCollectionFile::ExtensionString)) - }//if (qinfo.exists()) - }//if (info.downloadStatus == FT_STATE_COMPLETE) + } + } + } } void TransfersDialog::collOpen() @@ -2240,8 +2256,8 @@ void TransfersDialog::collOpen() QFileInfo qinfo; qinfo.setFile(QString::fromUtf8(path.c_str())); if (qinfo.exists()) { - if (qinfo.absoluteFilePath().endsWith(RsCollectionFile::ExtensionString)) { - RsCollectionFile collection; + if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) { + RsCollection collection; if (collection.load(qinfo.absoluteFilePath())) { collection.downloadFiles(); return; @@ -2252,95 +2268,24 @@ void TransfersDialog::collOpen() } } - RsCollectionFile collection; + RsCollection collection; if (collection.load(this)) { collection.downloadFiles(); - }//if (collection.load(this)) + } } -void TransfersDialog::setShowDLSizeColumn(bool show) -{ - if ( (!ui.downloadList->isColumnHidden(COLUMN_SIZE)) != show) { - ui.downloadList->setColumnHidden(COLUMN_SIZE, !show); - } -} - -void TransfersDialog::setShowDLCompleteColumn(bool show) -{ - if ( (!ui.downloadList->isColumnHidden(COLUMN_COMPLETED)) != show) { - ui.downloadList->setColumnHidden(COLUMN_COMPLETED, !show); - } -} - -void TransfersDialog::setShowDLDLSpeedColumn(bool show) -{ - if ( (!ui.downloadList->isColumnHidden(COLUMN_DLSPEED)) != show) { - ui.downloadList->setColumnHidden(COLUMN_DLSPEED, !show); - } -} - -void TransfersDialog::setShowDLProgressColumn(bool show) -{ - if ( (!ui.downloadList->isColumnHidden(COLUMN_PROGRESS)) != show) { - ui.downloadList->setColumnHidden(COLUMN_PROGRESS, !show); - } -} - -void TransfersDialog::setShowDLSourcesColumn(bool show) -{ - if ( (!ui.downloadList->isColumnHidden(COLUMN_SOURCES)) != show) { - ui.downloadList->setColumnHidden(COLUMN_SOURCES, !show); - } -} - -void TransfersDialog::setShowDLStatusColumn(bool show) -{ - if ( (!ui.downloadList->isColumnHidden(COLUMN_STATUS)) != show) { - ui.downloadList->setColumnHidden(COLUMN_STATUS, !show); - } -} - -void TransfersDialog::setShowDLPriorityColumn(bool show) -{ - if ( (!ui.downloadList->isColumnHidden(COLUMN_PRIORITY)) != show) { - ui.downloadList->setColumnHidden(COLUMN_PRIORITY, !show); - } -} - -void TransfersDialog::setShowDLRemainingColumn(bool show) -{ - if ( (!ui.downloadList->isColumnHidden(COLUMN_REMAINING)) != show) { - ui.downloadList->setColumnHidden(COLUMN_REMAINING, !show); - } -} - -void TransfersDialog::setShowDLDownloadTimeColumn(bool show) -{ - if ( (!ui.downloadList->isColumnHidden(COLUMN_DOWNLOADTIME)) != show) { - ui.downloadList->setColumnHidden(COLUMN_DOWNLOADTIME, !show); - } -} - -void TransfersDialog::setShowDLIDColumn(bool show) -{ - if ( (!ui.downloadList->isColumnHidden(COLUMN_ID)) != show) { - ui.downloadList->setColumnHidden(COLUMN_ID, !show); - } -} - -void TransfersDialog::setShowDLLastDLColumn(bool show) -{ - if ( (!ui.downloadList->isColumnHidden(COLUMN_LASTDL)) != show) { - ui.downloadList->setColumnHidden(COLUMN_LASTDL, !show); - } -} - -void TransfersDialog::setShowDLPath(bool show) -{ - if ( (!ui.downloadList->isColumnHidden(COLUMN_PATH)) != show) { - ui.downloadList->setColumnHidden(COLUMN_PATH, !show); - } -} +void TransfersDialog::setShowDLSizeColumn (bool show) { ui.downloadList->setColumnHidden(COLUMN_SIZE, !show); } +void TransfersDialog::setShowDLCompleteColumn (bool show) { ui.downloadList->setColumnHidden(COLUMN_COMPLETED, !show); } +void TransfersDialog::setShowDLDLSpeedColumn (bool show) { ui.downloadList->setColumnHidden(COLUMN_DLSPEED, !show); } +void TransfersDialog::setShowDLProgressColumn (bool show) { ui.downloadList->setColumnHidden(COLUMN_PROGRESS, !show); } +void TransfersDialog::setShowDLSourcesColumn (bool show) { ui.downloadList->setColumnHidden(COLUMN_SOURCES, !show); } +void TransfersDialog::setShowDLStatusColumn (bool show) { ui.downloadList->setColumnHidden(COLUMN_STATUS, !show); } +void TransfersDialog::setShowDLPriorityColumn (bool show) { ui.downloadList->setColumnHidden(COLUMN_PRIORITY, !show); } +void TransfersDialog::setShowDLRemainingColumn (bool show) { ui.downloadList->setColumnHidden(COLUMN_REMAINING, !show); } +void TransfersDialog::setShowDLDownloadTimeColumn(bool show) { ui.downloadList->setColumnHidden(COLUMN_DOWNLOADTIME, !show); } +void TransfersDialog::setShowDLIDColumn (bool show) { ui.downloadList->setColumnHidden(COLUMN_ID, !show); } +void TransfersDialog::setShowDLLastDLColumn (bool show) { ui.downloadList->setColumnHidden(COLUMN_LASTDL, !show); } +void TransfersDialog::setShowDLPath (bool show) { ui.downloadList->setColumnHidden(COLUMN_PATH, !show); } void TransfersDialog::expandAllDL() { diff --git a/retroshare-gui/src/gui/MainWindow.cpp b/retroshare-gui/src/gui/MainWindow.cpp index 0933f8e7b..e5d9430ce 100644 --- a/retroshare-gui/src/gui/MainWindow.cpp +++ b/retroshare-gui/src/gui/MainWindow.cpp @@ -103,7 +103,7 @@ #include "gui/statistics/StatisticsWindow.h" #include "gui/connect/ConnectFriendWizard.h" -#include "gui/common/RsCollectionFile.h" +#include "gui/common/RsCollection.h" #include "settings/rsettingswin.h" #include "settings/rsharesettings.h" #include "settings/WebuiPage.h" @@ -141,6 +141,7 @@ #define IMAGE_BLOGS ":/images/kblogger.png" #define IMAGE_DHT ":/images/dht16.png" +/*static*/ bool MainWindow::hiddenmode = false; /*static*/ MainWindow *MainWindow::_instance = NULL; @@ -182,6 +183,8 @@ MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) RsPeerDetails pd; if (rsPeers->getPeerDetails(rsPeers->getOwnId(), pd)) { nameAndLocation = QString("%1 (%2)").arg(QString::fromUtf8(pd.name.c_str())).arg(QString::fromUtf8(pd.location.c_str())); + if(pd.netMode == RS_NETMODE_HIDDEN) + hiddenmode = true; } setWindowTitle(tr("RetroShare %1 a secure decentralized communication platform").arg(Rshare::retroshareVersion(true)) + " - " + nameAndLocation); @@ -207,8 +210,9 @@ MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) connect(ui->listWidget, SIGNAL(currentRowChanged(int)), this, SLOT(setNewPage(int))); connect(ui->stackPages, SIGNAL(currentChanged(int)), this, SLOT(setNewPage(int))); - //ui->stackPages->setCurrentIndex(Settings->getLastPageInMainWindow()); - setNewPage(Settings->getLastPageInMainWindow()); + int lastpageindex = Settings->getLastPageInMainWindow(); + if(lastpageindex < ui->stackPages->count()) //Do not crash when a page was removed after last run + setNewPage(lastpageindex); ui->splitter->setStretchFactor(0, 0); ui->splitter->setStretchFactor(1, 1); @@ -241,13 +245,17 @@ MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) statusBar()->addWidget(peerstatus); natstatus = new NATStatus(); - natstatus->setVisible(Settings->valueFromGroup("StatusBar", "ShowNAT", QVariant(true)).toBool()); + if(hiddenmode) natstatus->setVisible(false); + else natstatus->setVisible(Settings->valueFromGroup("StatusBar", "ShowNAT", QVariant(true)).toBool()); statusBar()->addWidget(natstatus); - + natstatus->getNATStatus(); + dhtstatus = new DHTStatus(); - dhtstatus->setVisible(Settings->valueFromGroup("StatusBar", "ShowDHT", QVariant(true)).toBool()); + if(hiddenmode) dhtstatus->setVisible(false); + else dhtstatus->setVisible(Settings->valueFromGroup("StatusBar", "ShowDHT", QVariant(true)).toBool()); statusBar()->addWidget(dhtstatus); - + dhtstatus->getDHTStatus(); + hashingstatus = new HashingStatus(); hashingstatus->setVisible(Settings->valueFromGroup("StatusBar", "ShowHashing", QVariant(true)).toBool()); statusBar()->addPermanentWidget(hashingstatus, 1); @@ -702,11 +710,14 @@ void MainWindow::updateStatus() if (ratesstatus) ratesstatus->getRatesStatus(downKb, upKb); + if(!hiddenmode) + { if (natstatus) natstatus->getNATStatus(); if (dhtstatus) dhtstatus->getDHTStatus(); + } if (discstatus) { discstatus->update(); @@ -1020,7 +1031,7 @@ void MainWindow::newRsCollection() { std::vector dirVec; - RsCollectionFile(dirVec).openNewColl(this); + RsCollection(dirVec).openNewColl(this); } /** Shows Share Manager */ @@ -1448,8 +1459,8 @@ void MainWindow::openRsCollection(const QString &filename) { QFileInfo qinfo(filename); if (qinfo.exists()) { - if (qinfo.absoluteFilePath().endsWith(RsCollectionFile::ExtensionString)) { - RsCollectionFile collection; + if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) { + RsCollection collection; collection.openColl(qinfo.absoluteFilePath()); } } @@ -1576,10 +1587,13 @@ void MainWindow::setCompactStatusMode(bool compact) //statusComboBox: TODO Show only icon peerstatus->setCompactMode(compact); updateFriends(); + if(!hiddenmode) + { natstatus->setCompactMode(compact); natstatus->getNATStatus(); dhtstatus->setCompactMode(compact); dhtstatus->getDHTStatus(); + } hashingstatus->setCompactMode(compact); ratesstatus->setCompactMode(compact); //opModeStatus: TODO Show only ??? diff --git a/retroshare-gui/src/gui/MainWindow.h b/retroshare-gui/src/gui/MainWindow.h index 0297690ae..b3fc246dc 100644 --- a/retroshare-gui/src/gui/MainWindow.h +++ b/retroshare-gui/src/gui/MainWindow.h @@ -184,6 +184,8 @@ public: ToasterDisable *toasterDisableInstance(); SysTrayStatus *sysTrayStatusInstance(); + static bool hiddenmode; + public slots: void receiveNewArgs(QStringList args); void displayErrorMessage(int,int,const QString&) ; diff --git a/retroshare-gui/src/gui/NewsFeed.cpp b/retroshare-gui/src/gui/NewsFeed.cpp index 5da89940a..93ba2fc13 100644 --- a/retroshare-gui/src/gui/NewsFeed.cpp +++ b/retroshare-gui/src/gui/NewsFeed.cpp @@ -1116,7 +1116,7 @@ static bool addFeedItemIfUniqueCallback(FeedItem *feedItem, void *data) if (findData->mSecurityIpItem) { SecurityIpItem *securityIpItem = dynamic_cast(feedItem); - if (securityIpItem && securityIpItem->isSame(findData->mId1, findData->mId2, findData->mType)) { + if (securityIpItem && securityIpItem->isSame(RsPeerId(findData->mId1), findData->mId2, findData->mId3, findData->mType)) { return true; } return false; diff --git a/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.cpp b/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.cpp index 369bb4f3d..6a47ecc77 100644 --- a/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.cpp +++ b/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.cpp @@ -27,6 +27,8 @@ #include "util/TokenQueue.h" +#include "gui/settings/rsharesettings.h" + #include PostedCreatePostDialog::PostedCreatePostDialog(TokenQueue* tokenQ, RsPosted *posted, const RsGxsGroupId& grpId, QWidget *parent): @@ -35,7 +37,7 @@ PostedCreatePostDialog::PostedCreatePostDialog(TokenQueue* tokenQ, RsPosted *pos ui(new Ui::PostedCreatePostDialog) { ui->setupUi(this); - + Settings->loadWidgetInformation(this); connect(ui->submitButton, SIGNAL(clicked()), this, SLOT(createPost())); connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(close())); @@ -50,6 +52,7 @@ PostedCreatePostDialog::PostedCreatePostDialog(TokenQueue* tokenQ, RsPosted *pos PostedCreatePostDialog::~PostedCreatePostDialog() { + Settings->saveWidgetInformation(this); delete ui; } diff --git a/retroshare-gui/src/gui/Posted/PostedItem.cpp b/retroshare-gui/src/gui/Posted/PostedItem.cpp index d26b1e5c8..327608ff8 100644 --- a/retroshare-gui/src/gui/Posted/PostedItem.cpp +++ b/retroshare-gui/src/gui/Posted/PostedItem.cpp @@ -104,8 +104,6 @@ void PostedItem::setup() ui->clearButton->hide(); ui->readAndClearButton->hide(); - - ui->frame_notes->hide(); } bool PostedItem::setGroup(const RsPostedGroup &group, bool doFill) @@ -278,11 +276,12 @@ void PostedItem::fill() // FIX THIS UP LATER. ui->notes->setText(QString::fromUtf8(mPost.mNotes.c_str())); + if(ui->notes->text().isEmpty()) + ui->frame_notes->hide(); // differences between Feed or Top of Comment. if (mFeedHolder) { // feed. - ui->frame_notes->hide(); //frame_comment->show(); ui->commentButton->show(); @@ -303,14 +302,6 @@ void PostedItem::fill() else { // no feed. - if(ui->notes->text().isEmpty()) - { - ui->frame_notes->hide(); - } - else - { - ui->frame_notes->show(); - } //frame_comment->hide(); ui->commentButton->hide(); diff --git a/retroshare-gui/src/gui/Posted/PostedItem.ui b/retroshare-gui/src/gui/Posted/PostedItem.ui index ac9ea2a72..bebf874c2 100644 --- a/retroshare-gui/src/gui/Posted/PostedItem.ui +++ b/retroshare-gui/src/gui/Posted/PostedItem.ui @@ -60,7 +60,7 @@ - + 0 @@ -327,30 +327,29 @@ - QFrame::Box + QFrame::NoFrame QFrame::Sunken - - - - 2 - 2 - 16 - 17 - + + + 1 - - + + 1 - - - - - true - - + + + + + + + true + + + + diff --git a/retroshare-gui/src/gui/RemoteDirModel.cpp b/retroshare-gui/src/gui/RemoteDirModel.cpp index 15ac3168e..7ffbcf5bd 100644 --- a/retroshare-gui/src/gui/RemoteDirModel.cpp +++ b/retroshare-gui/src/gui/RemoteDirModel.cpp @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include #include @@ -1065,7 +1065,7 @@ void RetroshareDirModel::createCollectionFile(QWidget *parent, const QModelIndex std::vector dirVec; getDirDetailsFromSelect(list, dirVec); - RsCollectionFile(dirVec).openNewColl(parent); + RsCollection(dirVec).openNewColl(parent); } void RetroshareDirModel::downloadSelected(const QModelIndexList &list) diff --git a/retroshare-gui/src/gui/RetroShareLink.cpp b/retroshare-gui/src/gui/RetroShareLink.cpp index b58e7186b..a42d9c104 100644 --- a/retroshare-gui/src/gui/RetroShareLink.cpp +++ b/retroshare-gui/src/gui/RetroShareLink.cpp @@ -43,7 +43,7 @@ #include "msgs/MessageComposer.h" #include "util/misc.h" #include "common/PeerDefs.h" -#include "common/RsCollectionFile.h" +#include "common/RsCollection.h" #include #include "gui/connect/ConnectFriendWizard.h" #include "gui/connect/ConfCertDialog.h" @@ -58,6 +58,7 @@ //#define DEBUG_RSLINK 1 #define HOST_FILE "file" +#define HOST_COLLECTION "collection" #define HOST_EXTRAFILE "extra" #define HOST_PERSON "person" #define HOST_FORUM "forum" @@ -68,13 +69,18 @@ #define HOST_CERTIFICATE "certificate" #define HOST_PUBLIC_MSG "public_msg" #define HOST_IDENTITY "identity" -#define HOST_REGEXP "file|extra|person|forum|channel|posted|search|message|certificate|private_chat|public_msg|identity" +#define HOST_REGEXP "file|collection|extra|person|forum|channel|posted|search|message|certificate|private_chat|public_msg|identity" #define FILE_NAME "name" #define FILE_SIZE "size" #define FILE_HASH "hash" #define FILE_SOURCE "src" +#define COLLECTION_NAME "name" +#define COLLECTION_SIZE "size" +#define COLLECTION_DATA "radix" +#define COLLECTION_COUNT "files" + #define PERSON_NAME "name" #define PERSON_HASH "hash" @@ -328,6 +334,20 @@ void RetroShareLink::fromUrl(const QUrl& url) return; } + if (url.host() == HOST_COLLECTION) { + bool ok; + _type = TYPE_FILE_TREE; + _radix = decodedQueryItemValue(urlQuery, COLLECTION_DATA); + _name = decodedQueryItemValue(urlQuery, COLLECTION_NAME); + _size = urlQuery.queryItemValue(COLLECTION_SIZE).toULongLong(&ok); + _count = urlQuery.queryItemValue(COLLECTION_COUNT).toULongLong(&ok); + +#ifdef DEBUG_RSLINK + std::cerr << "Got a certificate link!!" << std::endl; +#endif + check() ; + return; + } if (url.host() == HOST_CERTIFICATE) { _type = TYPE_CERTIFICATE; _radix = decodedQueryItemValue(urlQuery, CERTIFICATE_RADIX); @@ -400,6 +420,21 @@ RetroShareLink RetroShareLink::createFile(const QString& name, uint64_t size, co return link; } +RetroShareLink RetroShareLink::createCollection(const QString& name, uint64_t size, uint32_t count, const QString& radix_data) +{ + RetroShareLink link; + link.clear(); + + link._name = name; + link._count = count; + link._size = size; + link._radix = radix_data ; + link._type = TYPE_FILE_TREE; + + link.check(); + + return link; +} RetroShareLink RetroShareLink::createPublicMsgInvite(time_t time_stamp,const QString& issuer_pgp_id,const QString& hash) { RetroShareLink link; @@ -608,6 +643,8 @@ void RetroShareLink::check() if(!checkSSLId(_SSLid)) _valid = false; // no break! We also test file stuff below. /* fallthrough */ + case TYPE_FILE_TREE: + case TYPE_FILE: if(_size > (((uint64_t)1)<<40)) // 1TB. Who has such large files? _valid = false; @@ -615,7 +652,7 @@ void RetroShareLink::check() if(!checkName(_name)) _valid = false; - if(!checkHash(_hash)) + if(!checkRadix64(_radix)) _valid = false; break; @@ -717,6 +754,8 @@ QString RetroShareLink::title() const rsPeers->getGPGDetails(RsPgpId(_GPGid.toStdString()), detail) ; return QObject::tr("Click to send a private message to %1 (%2).").arg(QString::fromUtf8(detail.name.c_str())).arg(_GPGid) ; } + case TYPE_FILE_TREE: + return QObject::tr("Click to browse/download this file collection"); case TYPE_EXTRAFILE: return QObject::tr("%1 (%2, Extra - Source included)").arg(hash()).arg(misc::friendlyUnit(size())); case TYPE_FILE: @@ -860,6 +899,15 @@ QString RetroShareLink::toString() const break; + case TYPE_FILE_TREE: + url.setScheme(RSLINK_SCHEME); + url.setHost(HOST_COLLECTION) ; + urlQuery.addQueryItem(COLLECTION_NAME, encodeItem(_name)); + urlQuery.addQueryItem(COLLECTION_SIZE, QString::number(_size)); + urlQuery.addQueryItem(COLLECTION_DATA, encodeItem(_radix)); + urlQuery.addQueryItem(COLLECTION_COUNT, QString::number(_count)); + break; + case TYPE_CERTIFICATE: url.setScheme(RSLINK_SCHEME); url.setHost(HOST_CERTIFICATE) ; @@ -882,9 +930,15 @@ QString RetroShareLink::niceName() const if (type() == TYPE_PERSON) return PeerDefs::rsid(name().toUtf8().constData(), RsPgpId(hash().toStdString())); + if(type() == TYPE_FILE_TREE) + return QObject::tr("%1 (%2 files, %3)").arg(_name).arg(_count).arg(misc::friendlyUnit(_size)); + if(type() == TYPE_IDENTITY) return QObject::tr("Identity link (name=%1, ID=%2)").arg(_name).arg(_hash) ; + if(type() == TYPE_FILE_TREE) + return QObject::tr("File directory (Total %s) Click to browse/download this file collection").arg(misc::friendlyUnit(_size)); + if(type() == TYPE_PUBLIC_MSG) { RsPeerDetails detail; rsPeers->getGPGDetails(RsPgpId(_GPGid.toStdString()), detail) ; @@ -926,10 +980,10 @@ QString RetroShareLink::toHtmlSize() const { QString size = QString("(%1)").arg(misc::friendlyUnit(_size)); - if (type() == TYPE_FILE && RsCollectionFile::isCollectionFile(name())) { + if (type() == TYPE_FILE && RsCollection::isCollectionFile(name())) { FileInfo finfo; if (rsFiles->FileDetails(RsFileHash(hash().toStdString()), RS_FILE_HINTS_EXTRA | RS_FILE_HINTS_LOCAL, finfo)) { - RsCollectionFile collection; + RsCollection collection; if (collection.load(QString::fromUtf8(finfo.path.c_str()), false)) { size += QString(" [%1]").arg(misc::friendlyUnit(collection.size())); } @@ -1095,6 +1149,7 @@ static void processList(const QStringList &list, const QString &textSingular, co case TYPE_SEARCH: case TYPE_MESSAGE: case TYPE_IDENTITY: + case TYPE_FILE_TREE: case TYPE_CERTIFICATE: case TYPE_PUBLIC_MSG: case TYPE_PRIVATE_CHAT: @@ -1336,6 +1391,16 @@ static void processList(const QStringList &list, const QString &textSingular, co } break; + case TYPE_FILE_TREE: + { + FileTree *ft = FileTree::create(link.radix().toStdString()) ; + + RsCollection(*ft).downloadFiles() ; + + delete ft; + } + break; + case TYPE_PERSON: { #ifdef DEBUG_RSLINK @@ -1347,34 +1412,6 @@ static void processList(const QStringList &list, const QString &textSingular, co PGPKeyDialog::showIt(detail.gpg_id,PGPKeyDialog::PageDetails) ; else personNotFound.append(PeerDefs::rsid(link.name().toUtf8().constData(), RsPgpId(link.hash().toStdString()))); - -// needNotifySuccess = true; - -// RsPeerDetails detail; -// if (rsPeers->getGPGDetails(RsPgpId(link.hash().toStdString()), detail)) -// { -// if (RsPgpId(detail.gpg_id) == rsPeers->getGPGOwnId()) { -// // it's me, do nothing -// break; -// } -// -// if (detail.accept_connection) { -// // peer connection is already accepted -// personExist.append(PeerDefs::rsid(detail)); -// break; -// } -// -// if (rsPeers->addFriend(RsPeerId(), RsPgpId(link.hash().toStdString()))) { -// ConfCertDialog::loadAll(); -// personAdded.append(PeerDefs::rsid(detail)); -// break; -// } -// -// personFailed.append(PeerDefs::rsid(link.name().toUtf8().constData(), RsPgpId(link.hash().toStdString()))); -// break; -// } -// -// personNotFound.append(PeerDefs::rsid(link.name().toUtf8().constData(), RsPgpId(link.hash().toStdString()))); } break; @@ -1679,17 +1716,17 @@ void RSLinkClipboard::copyLinks(const QList& links) QApplication::clipboard()->setText(res) ; } -void RSLinkClipboard::pasteLinks(QList &links) +void RSLinkClipboard::pasteLinks(QList &links,RetroShareLink::enumType type) { - return parseClipboard(links); + return parseClipboard(links,type); } -void RSLinkClipboard::parseClipboard(QList &links) +void RSLinkClipboard::parseClipboard(QList &links,RetroShareLink::enumType type) { // parse clipboard for links. // QString text = QApplication::clipboard()->text() ; - parseText(text, links); + parseText(text, links,type); } QString RSLinkClipboard::toString() @@ -1734,26 +1771,18 @@ bool RSLinkClipboard::empty(RetroShareLink::enumType type /* = RetroShareLink::T return true; } -/*static*/ int RSLinkClipboard::process(RetroShareLink::enumType type /* = RetroShareLink::TYPE_UNKNOWN*/, uint flag /* = RSLINK_PROCESS_NOTIFY_ALL*/) +int RSLinkClipboard::process(RetroShareLink::enumType type /* = RetroShareLink::TYPE_UNKNOWN*/, uint flag /* = RSLINK_PROCESS_NOTIFY_ALL*/) { QList links; - pasteLinks(links); + pasteLinks(links,type); - QList linksToProcess; - for (int i = 0; i < links.size(); ++i) { - if (links[i].valid() && (type == RetroShareLink::TYPE_UNKNOWN || links[i].type() == type)) { - linksToProcess.append(links[i]); - } - } - - if (linksToProcess.isEmpty()) { + if (links.isEmpty()) return 0; - } - return RetroShareLink::process(linksToProcess, flag); + return RetroShareLink::process(links, flag); } -void RSLinkClipboard::parseText(QString text, QList &links) +void RSLinkClipboard::parseText(QString text, QList &links,RetroShareLink::enumType type ) { links.clear(); @@ -1768,7 +1797,7 @@ void RSLinkClipboard::parseText(QString text, QList &links) QString url(text.mid(pos, rx.matchedLength())); RetroShareLink link(url); - if(link.valid()) + if(link.valid() && (type == RetroShareLink::TYPE_UNKNOWN || type == link.type())) { // check that the link is not already in the list: bool already = false ; diff --git a/retroshare-gui/src/gui/RetroShareLink.h b/retroshare-gui/src/gui/RetroShareLink.h index 131970887..43ca67ab0 100644 --- a/retroshare-gui/src/gui/RetroShareLink.h +++ b/retroshare-gui/src/gui/RetroShareLink.h @@ -69,7 +69,8 @@ class RetroShareLink TYPE_PRIVATE_CHAT = 0x09, TYPE_PUBLIC_MSG = 0x0a, TYPE_POSTED = 0x0b, - TYPE_IDENTITY = 0x0c + TYPE_IDENTITY = 0x0c, + TYPE_FILE_TREE = 0x0d }; public: @@ -80,6 +81,7 @@ class RetroShareLink static RetroShareLink createIdentity(const RsGxsId& gxs_id,const QString& name,const QString& radix_data) ; static RetroShareLink createExtraFile(const QString& name, uint64_t size, const QString& hash, const QString& ssl_id); static RetroShareLink createFile(const QString& name, uint64_t size, const QString& hash); + static RetroShareLink createCollection(const QString& name, uint64_t size,uint32_t count,const QString& radix_data); static RetroShareLink createPublicMsgInvite(time_t time_stamp,const QString& pgp_id,const QString& hash) ; static RetroShareLink createPerson(const RsPgpId &id); static RetroShareLink createCertificate(const RsPeerId &ssl_id) ; @@ -167,6 +169,7 @@ class RetroShareLink QString _encrypted_chat_info ; // encrypted data string for the recipient of a chat invite time_t _time_stamp ; // time stamp at which the link will expire. QString _radix_group_data; + uint32_t _count ; unsigned int _subType; // for general use as sub type for _type (RSLINK_SUBTYPE_...) }; @@ -189,7 +192,7 @@ class RSLinkClipboard // Get the liste of pasted links, either from the internal RS links, or by default // from the clipboard. // - static void pasteLinks(QList &links) ; + static void pasteLinks(QList &links,RetroShareLink::enumType type = RetroShareLink::TYPE_UNKNOWN) ; // Produces a list of links with no html structure. static QString toString() ; @@ -212,10 +215,10 @@ class RSLinkClipboard // static int process(RetroShareLink::enumType type = RetroShareLink::TYPE_UNKNOWN, uint flag = RSLINK_PROCESS_NOTIFY_ALL); - static void parseText(QString text, QList &links) ; + static void parseText(QString text, QList &links, RetroShareLink::enumType type = RetroShareLink::TYPE_UNKNOWN) ; private: - static void parseClipboard(QList &links) ; + static void parseClipboard(QList &links, RetroShareLink::enumType type = RetroShareLink::TYPE_UNKNOWN) ; }; #endif diff --git a/retroshare-gui/src/gui/chat/ChatWidget.cpp b/retroshare-gui/src/gui/chat/ChatWidget.cpp index bc7e21de6..ba5c94af7 100644 --- a/retroshare-gui/src/gui/chat/ChatWidget.cpp +++ b/retroshare-gui/src/gui/chat/ChatWidget.cpp @@ -111,7 +111,7 @@ ChatWidget::ChatWidget(QWidget *parent) : ui->searchButton->setFixedSize(buttonSize); ui->searchButton->setIconSize(iconSize); ui->sendButton->setFixedHeight(iconHeight); - ui->sendButton->setIconSize(iconSize); + ui->sendButton->setIconSize(iconSize); //Initialize search iCharToStartSearch=Settings->getChatSearchCharToStartSearch(); @@ -295,6 +295,7 @@ void ChatWidget::init(const ChatId &chat_id, const QString &title) this->title = title; ui->titleLabel->setText(RsHtml::plainText(title)); + ui->chatTextEdit->setMaxBytes(this->maxMessageSize() - 200); RsPeerId ownId = rsPeers->getOwnId(); setName(QString::fromUtf8(rsPeers->getPeerName(ownId).c_str())); @@ -452,6 +453,25 @@ void ChatWidget::processSettings(bool load) Settings->endGroup(); } +uint32_t ChatWidget::maxMessageSize() +{ + uint32_t maxMessageSize = 0; + switch (chatType()) { + case CHATTYPE_UNKNOWN: + break; + case CHATTYPE_PRIVATE: + maxMessageSize = rsMsgs->getMaxMessageSecuritySize(RS_CHAT_TYPE_PRIVATE); + break; + case CHATTYPE_LOBBY: + maxMessageSize = rsMsgs->getMaxMessageSecuritySize(RS_CHAT_TYPE_LOBBY); + break; + case CHATTYPE_DISTANT: + maxMessageSize = rsMsgs->getMaxMessageSecuritySize(RS_CHAT_TYPE_DISTANT); + break; + } + return maxMessageSize; +} + bool ChatWidget::eventFilter(QObject *obj, QEvent *event) { if (obj == ui->textBrowser || obj == ui->textBrowser->viewport() @@ -1169,20 +1189,7 @@ void ChatWidget::updateLenOfChatTextEdit() RsHtml::optimizeHtml(chatWidget, text); std::wstring msg = text.toStdWString(); - uint32_t maxMessageSize = 0; - switch (chatType()) { - case CHATTYPE_UNKNOWN: - break; - case CHATTYPE_PRIVATE: - maxMessageSize = rsMsgs->getMaxMessageSecuritySize(RS_CHAT_TYPE_PRIVATE); - break; - case CHATTYPE_LOBBY: - maxMessageSize = rsMsgs->getMaxMessageSecuritySize(RS_CHAT_TYPE_LOBBY); - break; - case CHATTYPE_DISTANT: - maxMessageSize = rsMsgs->getMaxMessageSecuritySize(RS_CHAT_TYPE_DISTANT); - break; - } + uint32_t maxMessageSize = this->maxMessageSize(); int charRemains = 0; if (maxMessageSize > 0) { @@ -1561,7 +1568,8 @@ void ChatWidget::addExtraPicture() QString file; if (misc::getOpenFileName(window(), RshareSettings::LASTDIR_IMAGES, tr("Load Picture File"), "Pictures (*.png *.xpm *.jpg *.jpeg)", file)) { QString encodedImage; - if (RsHtml::makeEmbeddedImage(file, encodedImage, 640*480)) { + uint32_t maxMessageSize = this->maxMessageSize(); + if (RsHtml::makeEmbeddedImage(file, encodedImage, 640*480, maxMessageSize - 200)) { //-200 for the html stuff QTextDocumentFragment fragment = QTextDocumentFragment::fromHtml(encodedImage); ui->chatTextEdit->textCursor().insertFragment(fragment); } diff --git a/retroshare-gui/src/gui/chat/ChatWidget.h b/retroshare-gui/src/gui/chat/ChatWidget.h index 174b4d777..30f2e8427 100644 --- a/retroshare-gui/src/gui/chat/ChatWidget.h +++ b/retroshare-gui/src/gui/chat/ChatWidget.h @@ -198,6 +198,8 @@ private: void setColorAndFont(bool both); void processSettings(bool load); + uint32_t maxMessageSize(); + void completeNickname(bool reverse); QAbstractItemModel *modelFromPeers(); diff --git a/retroshare-gui/src/gui/chat/ChatWidget.ui b/retroshare-gui/src/gui/chat/ChatWidget.ui index fed47f6ce..e9e1e0bfe 100644 --- a/retroshare-gui/src/gui/chat/ChatWidget.ui +++ b/retroshare-gui/src/gui/chat/ChatWidget.ui @@ -11,21 +11,12 @@ - - 0 - - - 0 - - - 0 - - - 0 - 2 + + 0 + @@ -46,16 +37,7 @@ QFrame::Raised - - 0 - - - 0 - - - 0 - - + 0 @@ -131,16 +113,7 @@ QFrame::Box - - 6 - - - 6 - - - 6 - - + 6 @@ -308,7 +281,7 @@ border-image: url(:/images/closepressed.png) 30 - + Type a message here @@ -326,16 +299,7 @@ border-image: url(:/images/closepressed.png) QFrame::Sunken - - 2 - - - 2 - - - 2 - - + 2 @@ -436,6 +400,41 @@ border-image: url(:/images/closepressed.png) + + + + + 0 + 0 + + + + Qt::NoFocus + + + Qt::CustomContextMenu + + + + :/icons/png/search.png:/icons/png/search.png + + + + 28 + 28 + + + + true + + + true + + + + + + @@ -465,9 +464,6 @@ border-image: url(:/images/closepressed.png) - - - @@ -520,38 +516,6 @@ border-image: url(:/images/closepressed.png) - - - - - 0 - 0 - - - - Qt::NoFocus - - - Qt::CustomContextMenu - - - - :/icons/png/search.png:/icons/png/search.png - - - - 28 - 28 - - - - true - - - true - - - @@ -581,16 +545,7 @@ border-image: url(:/images/closepressed.png) QFrame::Plain - - 2 - - - 2 - - - 2 - - + 2 @@ -688,16 +643,7 @@ border-image: url(:/images/closepressed.png) QFrame::Sunken - - 2 - - - 2 - - - 2 - - + 2 @@ -778,16 +724,7 @@ border-image: url(:/images/closepressed.png) QFrame::Plain - - 2 - - - 2 - - - 2 - - + 2 diff --git a/retroshare-gui/src/gui/common/FilesDefs.cpp b/retroshare-gui/src/gui/common/FilesDefs.cpp index 7c4fc9140..b20660b20 100644 --- a/retroshare-gui/src/gui/common/FilesDefs.cpp +++ b/retroshare-gui/src/gui/common/FilesDefs.cpp @@ -23,7 +23,7 @@ #include #include "FilesDefs.h" -#include "RsCollectionFile.h" +#include "RsCollection.h" static QString getInfoFromFilename(const QString& filename, bool anyForUnknown, bool image) { @@ -54,7 +54,7 @@ static QString getInfoFromFilename(const QString& filename, bool anyForUnknown, return image ? ":/images/FileTypeDocument.png" : QApplication::translate("FilesDefs", "Document"); } else if (ext == "pdf") { return image ? ":/images/mimetypes/pdf.png" : QApplication::translate("FilesDefs", "Document"); - } else if (ext == RsCollectionFile::ExtensionString) { + } else if (ext == RsCollection::ExtensionString) { return image ? ":/images/mimetypes/rscollection-16.png" : QApplication::translate("FilesDefs", "RetroShare collection file"); } else if (ext == "sub" || ext == "srt") { return image ? ":/images/FileTypeAny.png" : QApplication::translate("FilesDefs", "Subtitles"); diff --git a/retroshare-gui/src/gui/common/MimeTextEdit.cpp b/retroshare-gui/src/gui/common/MimeTextEdit.cpp index e3b6d1437..3ec714f0c 100644 --- a/retroshare-gui/src/gui/common/MimeTextEdit.cpp +++ b/retroshare-gui/src/gui/common/MimeTextEdit.cpp @@ -69,7 +69,7 @@ void MimeTextEdit::insertFromMimeData(const QMimeData* source) QImage image = qvariant_cast(source->imageData()); if (image.isNull() == false) { QString encodedImage; - if (RsHtml::makeEmbeddedImage(image, encodedImage, 640*480)) { + if (RsHtml::makeEmbeddedImage(image, encodedImage, 640*480, mMaxBytes)) { QTextDocumentFragment fragment = QTextDocumentFragment::fromHtml(encodedImage); textCursor().insertFragment(fragment); return; diff --git a/retroshare-gui/src/gui/common/MimeTextEdit.h b/retroshare-gui/src/gui/common/MimeTextEdit.h index 2a6529ca1..463452bbf 100644 --- a/retroshare-gui/src/gui/common/MimeTextEdit.h +++ b/retroshare-gui/src/gui/common/MimeTextEdit.h @@ -50,6 +50,8 @@ public: QColor textColorQuote() const { return highliter->textColorQuote();} bool onlyPlainText() const {return mOnlyPlainText;} + void setMaxBytes(int limit) {mMaxBytes = limit;} + public slots: void setTextColorQuote(QColor textColorQuote) { highliter->setTextColorQuote(textColorQuote);} void setOnlyPlainText(bool bOnlyPlainText) {mOnlyPlainText = bOnlyPlainText;} @@ -83,6 +85,7 @@ private: QList mContextMenuActions; RsSyntaxHighlighter *highliter; bool mOnlyPlainText; + int mMaxBytes = -1; //limit content size, for pasting images }; #endif // MIMETEXTEDIT_H diff --git a/retroshare-gui/src/gui/common/RsCollectionFile.cpp b/retroshare-gui/src/gui/common/RsCollection.cpp similarity index 77% rename from retroshare-gui/src/gui/common/RsCollectionFile.cpp rename to retroshare-gui/src/gui/common/RsCollection.cpp index 3e4792063..f92598137 100644 --- a/retroshare-gui/src/gui/common/RsCollectionFile.cpp +++ b/retroshare-gui/src/gui/common/RsCollection.cpp @@ -24,7 +24,7 @@ #include #include -#include "RsCollectionFile.h" +#include "RsCollection.h" #include "RsCollectionDialog.h" #include "util/misc.h" @@ -36,28 +36,41 @@ #include #include -const QString RsCollectionFile::ExtensionString = QString("rscollection") ; +// #define COLLECTION_DEBUG 1 -RsCollectionFile::RsCollectionFile(QObject *parent) +const QString RsCollection::ExtensionString = QString("rscollection") ; + +RsCollection::RsCollection(QObject *parent) : QObject(parent), _xml_doc("RsCollection") { + _root = _xml_doc.createElement("RsCollection"); + _xml_doc.appendChild(_root); } -RsCollectionFile::RsCollectionFile(const std::vector& file_infos, QObject *parent) +RsCollection::RsCollection(const FileTree& fr) + : _xml_doc("RsCollection") +{ + _root = _xml_doc.createElement("RsCollection"); + _xml_doc.appendChild(_root); + + recursAddElements(_xml_doc,fr,0,_root) ; +} + +RsCollection::RsCollection(const std::vector& file_infos, QObject *parent) : QObject(parent), _xml_doc("RsCollection") { - QDomElement root = _xml_doc.createElement("RsCollection"); - _xml_doc.appendChild(root); + _root = _xml_doc.createElement("RsCollection"); + _xml_doc.appendChild(_root); for(uint32_t i = 0;i& colFileInfos,const QString& current_path, bool bad_chars_in_parent) const +void RsCollection::merge_in(const QString& fname,uint64_t size,const RsFileHash& hash) +{ + ColFileInfo info ; + info.type = DIR_TYPE_FILE ; + info.name = fname ; + info.size = size ; + info.hash = QString::fromStdString(hash.toStdString()) ; + + recursAddElements(_xml_doc,info,_root) ; +} +void RsCollection::merge_in(const FileTree& tree) +{ + recursAddElements(_xml_doc,tree,0,_root) ; +} + +void RsCollection::recursCollectColFileInfos(const QDomElement& e,std::vector& colFileInfos,const QString& current_path, bool bad_chars_in_parent) const { QDomNode n = e.firstChild() ; - +#ifdef COLLECTION_DEBUG std::cerr << "Parsing element " << e.tagName().toStdString() << std::endl; +#endif while(!n.isNull()) { QDomElement ee = n.toElement(); // try to convert the node to an element. +#ifdef COLLECTION_DEBUG std::cerr << " Seeing child " << ee.tagName().toStdString() << std::endl; +#endif if(ee.tagName() == QString("File")) { @@ -139,7 +170,7 @@ void RsCollectionFile::recursCollectColFileInfos(const QDomElement& e,std::vecto } -void RsCollectionFile::recursAddElements(QDomDocument& doc,const DirDetails& details,QDomElement& e) const +void RsCollection::recursAddElements(QDomDocument& doc,const DirDetails& details,QDomElement& e) const { if (details.type == DIR_TYPE_FILE) { @@ -175,7 +206,7 @@ void RsCollectionFile::recursAddElements(QDomDocument& doc,const DirDetails& det } } -void RsCollectionFile::recursAddElements(QDomDocument& doc,const ColFileInfo& colFileInfo,QDomElement& e) const +void RsCollection::recursAddElements(QDomDocument& doc,const ColFileInfo& colFileInfo,QDomElement& e) const { if (colFileInfo.type == DIR_TYPE_FILE) { @@ -186,7 +217,7 @@ void RsCollectionFile::recursAddElements(QDomDocument& doc,const ColFileInfo& co f.setAttribute(QString("size"),QString::number(colFileInfo.size)) ; e.appendChild(f) ; -} + } else if (colFileInfo.type == DIR_TYPE_DIR) { QDomElement d = doc.createElement("Directory") ; @@ -194,21 +225,47 @@ void RsCollectionFile::recursAddElements(QDomDocument& doc,const ColFileInfo& co d.setAttribute(QString("name"),colFileInfo.name) ; for (std::vector::const_iterator it = colFileInfo.children.begin(); it != colFileInfo.children.end(); ++it) -{ recursAddElements(doc,(*it),d) ; - } e.appendChild(d) ; } } +void RsCollection::recursAddElements(QDomDocument& doc,const FileTree& ft,uint32_t index,QDomElement& e) const +{ + std::vector subdirs ; + std::vector subfiles ; + std::string name; + + if(!ft.getDirectoryContent(index,name,subdirs,subfiles)) + return ; + + QDomElement d = doc.createElement("Directory") ; + d.setAttribute(QString("name"),QString::fromUtf8(name.c_str())) ; + e.appendChild(d) ; + + for (uint32_t i=0;i colFileInfos ; @@ -433,11 +489,11 @@ bool RsCollectionFile::openColl(const QString& fileName, bool readOnly /* = fals delete rcd; return _saved; - }//if (load(fileName, showError)) + } return false; } -qulonglong RsCollectionFile::size() +qulonglong RsCollection::size() { QDomElement docElem = _xml_doc.documentElement(); @@ -453,14 +509,14 @@ qulonglong RsCollectionFile::size() return size; } -bool RsCollectionFile::isCollectionFile(const QString &fileName) +bool RsCollection::isCollectionFile(const QString &fileName) { QString ext = QFileInfo(fileName).suffix().toLower(); - return (ext == RsCollectionFile::ExtensionString); + return (ext == RsCollection::ExtensionString); } -void RsCollectionFile::saveColl(std::vector colFileInfos, const QString &fileName) +void RsCollection::saveColl(std::vector colFileInfos, const QString &fileName) { QDomElement root = _xml_doc.elementsByTagName("RsCollection").at(0).toElement(); diff --git a/retroshare-gui/src/gui/common/RsCollectionFile.h b/retroshare-gui/src/gui/common/RsCollection.h similarity index 76% rename from retroshare-gui/src/gui/common/RsCollectionFile.h rename to retroshare-gui/src/gui/common/RsCollection.h index d2a86eba9..0a6e1e65f 100644 --- a/retroshare-gui/src/gui/common/RsCollectionFile.h +++ b/retroshare-gui/src/gui/common/RsCollection.h @@ -56,27 +56,29 @@ public: }; Q_DECLARE_METATYPE(ColFileInfo) -class RsCollectionFile : public QObject +class RsCollection : public QObject { Q_OBJECT public: - RsCollectionFile(QObject *parent = 0) ; - // create from list of files and directories - RsCollectionFile(const std::vector& file_entries, QObject *parent = 0) ; - virtual ~RsCollectionFile() ; + RsCollection(QObject *parent = 0) ; + // create from list of files and directories + RsCollection(const std::vector& file_entries, QObject *parent = 0) ; + RsCollection(const FileTree& fr); + virtual ~RsCollection() ; + + void merge_in(const QString& fname,uint64_t size,const RsFileHash& hash) ; + void merge_in(const FileTree& tree) ; static const QString ExtensionString ; - - - // Loads file from disk. - bool load(QWidget *parent); + // Loads file from disk. + bool load(QWidget *parent); bool load(const QString& fileName, bool showError = true); - // Save to disk - bool save(QWidget *parent) const ; + // Save to disk + bool save(QWidget *parent) const ; bool save(const QString& fileName) const ; // Open new collection @@ -84,28 +86,31 @@ public: // Open existing collection bool openColl(const QString& fileName, bool readOnly = false, bool showError = true); - // Download the content. - void downloadFiles() const ; + // Download the content. + void downloadFiles() const ; - qulonglong size(); + qulonglong size(); static bool isCollectionFile(const QString& fileName); private slots: void saveColl(std::vector colFileInfos, const QString& fileName); - private: +private: - void recursAddElements(QDomDocument&,const DirDetails&,QDomElement&) const ; + void recursAddElements(QDomDocument&,const DirDetails&,QDomElement&) const ; void recursAddElements(QDomDocument&,const ColFileInfo&,QDomElement&) const; + void recursAddElements(QDomDocument& doc,const FileTree& ft,uint32_t index,QDomElement& e) const; + void recursCollectColFileInfos(const QDomElement&,std::vector& colFileInfos,const QString& current_dir,bool bad_chars_in_parent) const ; // check that the file is a valid rscollection file, and not a lol bomb or some shit like this static bool checkFile(const QString &fileName, bool showError); - QDomDocument _xml_doc ; + QDomDocument _xml_doc ; QString _fileName ; bool _saved; + QDomElement _root ; - friend class RsCollectionDialog ; + friend class RsCollectionDialog ; }; diff --git a/retroshare-gui/src/gui/common/RsCollectionDialog.cpp b/retroshare-gui/src/gui/common/RsCollectionDialog.cpp index eb920f219..e78fbdf08 100644 --- a/retroshare-gui/src/gui/common/RsCollectionDialog.cpp +++ b/retroshare-gui/src/gui/common/RsCollectionDialog.cpp @@ -23,13 +23,14 @@ #include #include +#include #include #include #include #include #include #include "RsCollectionDialog.h" -#include "RsCollectionFile.h" +#include "RsCollection.h" #include "util/misc.h" #define COLUMN_FILE 0 #define COLUMN_FILEPATH 1 @@ -50,6 +51,7 @@ #define MAX_FILE_ADDED_BEFORE_ASK 500 //Number of file added in Recursive mode before asking to continue +#define IMAGE_SEARCH ":/icons/svg/magnifying-glass.svg" /** * @brief The FSMSortFilterProxyModel class sort directory before file. @@ -138,7 +140,23 @@ RsCollectionDialog::RsCollectionDialog(const QString& collectionFileName ui.headerFrame->setHeaderImage(QPixmap(":/images/library64.png")); - ui.headerFrame->setHeaderText(tr("Collection Editor")); + + if(creation) + { + ui.headerFrame->setHeaderText(tr("Collection Editor")); + ui.downloadFolder_LE->hide(); + ui.downloadFolder_LB->hide(); + } + else + { + ui.headerFrame->setHeaderText(tr("Download files")); + ui.downloadFolder_LE->show(); + ui.downloadFolder_LB->show(); + + ui.downloadFolder_LE->setText(QString::fromUtf8(rsFiles->getDownloadDirectory().c_str())) ; + + QObject::connect(ui.downloadFolder_LE,SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(openDestinationDirectoryMenu(QPoint))); + } // 1 - add all elements to the list. @@ -209,6 +227,46 @@ RsCollectionDialog::RsCollectionDialog(const QString& collectionFileName QMessageBox::warning(NULL,tr("Bad filenames have been cleaned"),tr("Some filenames or directory names contained forbidden characters.\nCharacters \",|,/,\\,<,>,*,? will be replaced by '_'.\n Concerned files are listed in red.")) ; } +void RsCollectionDialog::openDestinationDirectoryMenu(QPoint) +{ + QMenu contextMnu( this ); + + // pop a menu with existing entries and also a custom entry + // Now get the list of existing directories. + + std::list< SharedDirInfo> dirs ; + rsFiles->getSharedDirectories( dirs) ; + + for (std::list::const_iterator it(dirs.begin());it!=dirs.end();++it){ + // Check for existence of directory name + QFile directory( QString::fromUtf8((*it).filename.c_str())) ; + + if (!directory.exists()) continue ; + if (!(directory.permissions() & QFile::WriteOwner)) continue ; + + contextMnu.addAction(QString::fromUtf8((*it).filename.c_str()), this, SLOT(setDestinationDirectory()))->setData(QString::fromUtf8( (*it).filename.c_str() ) ) ; + } + + contextMnu.addAction( QIcon(IMAGE_SEARCH),tr("Specify..."),this,SLOT(chooseDestinationDirectory())); + + contextMnu.exec(QCursor::pos()) ; +} + +void RsCollectionDialog::setDestinationDirectory() +{ + QString dest_dir(qobject_cast(sender())->data().toString()) ; + ui.downloadFolder_LE->setText(dest_dir) ; +} + +void RsCollectionDialog::chooseDestinationDirectory() +{ + QString dest_dir = QFileDialog::getExistingDirectory(this,tr("Choose directory")) ; + + if(dest_dir.isNull()) + return ; + + ui.downloadFolder_LE->setText(dest_dir) ; +} /** * @brief RsCollectionDialog::~RsCollectionDialog */ @@ -243,26 +301,26 @@ bool RsCollectionDialog::eventFilter(QObject *obj, QEvent *event) for (it = selectedItems.begin(); it != selectedItems.end(); ++it) { if ((*it)->checkState(COLUMN_FILE) != checkState) (*it)->setCheckState(COLUMN_FILE, checkState); - }//for (it - }//if (item) + } + } return true; // eat event - }//if (keyEvent && keyEvent->key() == Qt::Key_Space) + } if (keyEvent && (keyEvent->key() == Qt::Key_Delete)) { // Delete pressed remove(); return true; // eat event - }//if (keyEvent && keyEvent->key() == Qt::Key_Delete) + } if (keyEvent && (keyEvent->key() == Qt::Key_Plus)) { // Plus pressed makeDir(); return true; // eat event - }//if (keyEvent && keyEvent->key() == Qt::Key_Plus) + } - }//if (event->type() == QEvent::KeyPress) - }//if (obj == ui._fileEntriesTW) + } + } if (obj == ui._systemFileTW) { if (event->type() == QEvent::KeyPress) { @@ -277,9 +335,9 @@ bool RsCollectionDialog::eventFilter(QObject *obj, QEvent *event) } return true; // eat event - }//if (keyEvent && keyEvent->key() == Qt::Key_Enter... - }//if (event->type() == QEvent::KeyPress) - }//if (obj == ui._systemFileTW) + } + } + } // pass the event on to the parent class return QDialog::eventFilter(obj, event); @@ -452,7 +510,7 @@ bool RsCollectionDialog::addChild(QTreeWidgetItem* parent, const std::vectorsetToolTip(COLUMN_FILEC, tr("Real File Count=%1").arg(1)); item->setData(COLUMN_FILEC, ROLE_FILEC, 1); item->setData(COLUMN_FILEC, ROLE_SELFILEC, 1); - }//if (colFileInfo.type==DIR_TYPE_DIR + } item->setFont(COLUMN_FILE, font); if (colFileInfo.filename_has_wrong_characters) @@ -487,12 +545,12 @@ bool RsCollectionDialog::addChild(QTreeWidgetItem* parent, const std::vectorsetText(COLUMN_FILEC, QString("%1").arg(parentSelFileCount)); itemParent = itemParent->parent(); - }//while (itemParent) - }//if (itemParent) - }//if (colFileInfo.type==DIR_TYPE_FILE) + } + } + } founds.push_back(item); - }//(founds.empty()) + } if (!founds.empty()) { @@ -591,12 +649,12 @@ void RsCollectionDialog::changeFileName() QString fileName; if(!misc::getSaveFileName(this, RshareSettings::LASTDIR_EXTRAFILE , QApplication::translate("RsCollectionFile", "Create collection file") - , QApplication::translate("RsCollectionFile", "Collection files") + " (*." + RsCollectionFile::ExtensionString + ")" + , QApplication::translate("RsCollectionFile", "Collection files") + " (*." + RsCollection::ExtensionString + ")" , fileName,0, QFileDialog::DontConfirmOverwrite)) return; - if (!fileName.endsWith("." + RsCollectionFile::ExtensionString)) - fileName += "." + RsCollectionFile::ExtensionString ; + if (!fileName.endsWith("." + RsCollection::ExtensionString)) + fileName += "." + RsCollection::ExtensionString ; std::cerr << "Got file name: " << fileName.toStdString() << std::endl; @@ -604,7 +662,7 @@ void RsCollectionDialog::changeFileName() if(file.exists()) { - RsCollectionFile collFile; + RsCollection collFile; if (!collFile.checkFile(fileName,true)) return; QMessageBox mb; @@ -624,7 +682,7 @@ void RsCollectionDialog::changeFileName() if (qddOldFile.setContent(&file)) { QDomElement docOldElem = qddOldFile.elementsByTagName("RsCollection").at(0).toElement(); collFile.recursCollectColFileInfos(docOldElem,_newColFileInfos,QString(),false); - }//(qddOldFile.setContent(&file)) + } } else if (mb.clickedButton()==btnCancel) { return; @@ -636,7 +694,7 @@ void RsCollectionDialog::changeFileName() //create a new empty file to check if name if good. if (!file.open(QFile::WriteOnly)) return; file.remove(); - }//if(file.exists()) + } _fileName = fileName; @@ -686,7 +744,7 @@ void RsCollectionDialog::addRecursive(bool recursive) } else { continue; } - }//if (fileInfo.isDir()) + } if (fileInfo.isFile()){ fileToHash.append(fileInfo.absoluteFilePath()); ++count; @@ -694,9 +752,9 @@ void RsCollectionDialog::addRecursive(bool recursive) _listOfFilesAddedInDir.insert(fileInfo.absoluteFilePath(),fileInfo.absolutePath()); else _listOfFilesAddedInDir.insert(fileInfo.absoluteFilePath(),""); - }//if (fileInfo.isFile()) - }//if (index.column()==0) - }//foreach (QModelIndex index, milSelectionList) + } + } + } // Process Dirs QTreeWidgetItem *item = NULL; @@ -705,8 +763,8 @@ void RsCollectionDialog::addRecursive(bool recursive) if (item) { while (item->data(COLUMN_HASH, ROLE_TYPE).toUInt() != DIR_TYPE_DIR) { item = item->parent();//Only Dir as Parent - }//while - }//if (item) + } + } int index = 0; while (index < dirToAdd.count()) @@ -722,7 +780,7 @@ void RsCollectionDialog::addRecursive(bool recursive) //QMap is ordered, so we get parent before child //Iterator is moved inside this function processItem(dirToAdd, index, root); - }//while (index < dirToAdd.count()) + } //Update liste before attach files to be sure when file is hashed, parent directory exists. updateList(); @@ -736,9 +794,9 @@ void RsCollectionDialog::addRecursive(bool recursive) } else if(item) { if (item->data(COLUMN_HASH, ROLE_NAME) != "") { it.value() = item->text(COLUMN_FILEPATH); - }//if (item->data(COLUMN_HASH, ROLE_NAME) != "") - }//if (dirToAdd.contains(path)) - }//for (QHash::Iterator it + } + } + } // Process Files once all done ui._hashBox->addAttachments(fileToHash,RS_FILE_REQ_ANONYMOUS_ROUTING /*, 0*/); @@ -781,13 +839,13 @@ bool RsCollectionDialog::addAllChild(QFileInfo &fileInfoParent default: // should never be reached break; - }//switch (ret) + } } if (fileInfo.isDir()) { dirToAdd.insert(fileInfo.absoluteFilePath(),fileInfo.absolutePath()); ++count; if (!addAllChild(fileInfo, dirToAdd, fileToHash, count)) return false; - }//if (fileInfo.isDir()) + } if (fileInfo.isFile()){ fileToHash.append(fileInfo.absoluteFilePath()); ++count; @@ -795,8 +853,8 @@ bool RsCollectionDialog::addAllChild(QFileInfo &fileInfoParent _listOfFilesAddedInDir.insert(fileInfo.absoluteFilePath(),fileInfo.absolutePath()); else _listOfFilesAddedInDir.insert(fileInfo.absoluteFilePath(),""); - }//if (fileInfo.isFile()) - }//foreach (QFileInfo fileInfo, dirParent.entryInfoList()) + } + } return true; } @@ -821,10 +879,10 @@ void RsCollectionDialog::remove() item->setSelected(false); } else { listDir += item->data(COLUMN_HASH, ROLE_NAME).toString() +"
"; - }//if (listDir.contains(item->data(COLUMN_HASH, ROLE_PATH).toString())) - }//if (item->data(COLUMN_HASH, ROLE_TYPE).toUInt() == DIR_TYPE_DIR) - }//if (item != getRootItem()) - }//for (int curs = 0; curs < count; ++curs) + } + } + } + } //If directories, ask to remove them or not if (!listDir.isEmpty()){ @@ -870,14 +928,14 @@ void RsCollectionDialog::remove() case QMessageBox::Cancel: { delete msgBox; return; - }//case QMessageBox::Cancel: + } break; default: // should never be reached break; - }//switch (ret) + } delete msgBox; - }//if (!listDir.isEmpty()) + } //Remove wanted items int leftItem = 0; @@ -966,7 +1024,7 @@ void RsCollectionDialog::processItem(QMap &dirToAdd } else { _newColFileInfos.push_back(newChild); } - }//(index < count) + } } /** @@ -1006,13 +1064,13 @@ void RsCollectionDialog::makeDir() default: // should never be reached break; - }//switch (ret) - }//if (badChar) + } + } } else {//if (ok && !childName.isEmpty()) return; - }//if (ok && !childName.isEmpty()) + } - }//while (!nameOK) + } // Process all selected items int count = ui._fileEntriesTW->selectedItems().count(); @@ -1040,8 +1098,8 @@ void RsCollectionDialog::makeDir() if (item == getRootItem()) newChild.path = ""; _newColFileInfos.push_back(newChild); - }//if (item) - }//for (; curs < count; ++curs) + } + } updateList(); @@ -1074,7 +1132,7 @@ void RsCollectionDialog::fileHashingFinished(QList hashedFiles) //File Added in directory, find its parent colFileInfo.path = _listOfFilesAddedInDir.value(hashedFile.filepath,""); _listOfFilesAddedInDir.remove(hashedFile.filepath); - }//if (_listOfFilesAddedInDir.value(hashedFile.filepath,"")!="") + } _newColFileInfos.push_back(colFileInfo); @@ -1112,7 +1170,7 @@ void RsCollectionDialog::itemChanged(QTreeWidgetItem *item, int col) itemParent->setText(COLUMN_FILEC, QString("%1").arg(parentFileCount)); itemParent = itemParent->parent(); - }//while (itemParent) + } updateSizes() ; @@ -1222,7 +1280,7 @@ void RsCollectionDialog::download() { std::cerr << "Downloading!" << std::endl; - QString dldir = QString::fromUtf8(rsFiles->getDownloadDirectory().c_str()) ; + QString dldir = ui.downloadFolder_LE->text(); std::cerr << "downloading all these files:" << std::endl; @@ -1258,8 +1316,8 @@ void RsCollectionDialog::download() std::list()); } else {//if (item->checkState(COLUMN_FILE) == Qt::Checked) std::cerr<<"Skipping file : " << item->data(COLUMN_HASH,ROLE_NAME).toString().toStdString() << std::endl; - }//if (item->checkState(COLUMN_FILE) == Qt::Checked) - }//while ((item = *itemIterator) != NULL) + } + } close(); } diff --git a/retroshare-gui/src/gui/common/RsCollectionDialog.h b/retroshare-gui/src/gui/common/RsCollectionDialog.h index 2c5721d8b..7ecf9b6da 100644 --- a/retroshare-gui/src/gui/common/RsCollectionDialog.h +++ b/retroshare-gui/src/gui/common/RsCollectionDialog.h @@ -22,7 +22,7 @@ ****************************************************************/ #include "ui_RsCollectionDialog.h" -#include "RsCollectionFile.h" +#include "RsCollection.h" #include #include @@ -49,6 +49,9 @@ private slots: void add() ; void addRecursive() ; void remove() ; + void chooseDestinationDirectory(); + void setDestinationDirectory(); + void openDestinationDirectoryMenu(QPoint pt); void processItem(QMap &dirToAdd , int &index , ColFileInfo &parent diff --git a/retroshare-gui/src/gui/common/RsCollectionDialog.ui b/retroshare-gui/src/gui/common/RsCollectionDialog.ui index 78e736c24..1e867eb2a 100644 --- a/retroshare-gui/src/gui/common/RsCollectionDialog.ui +++ b/retroshare-gui/src/gui/common/RsCollectionDialog.ui @@ -6,10 +6,13 @@ 0 0 - 693 - 525 + 969 + 778 + + Qt::CustomContextMenu + Collection @@ -67,19 +70,7 @@ QFrame::Plain - - - 0 - - - 0 - - - 0 - - - 0 - + @@ -403,7 +394,7 @@ - + @@ -424,6 +415,26 @@ + + + + Destination: + + + + + + + Qt::CustomContextMenu + + + Right click to change download directory + + + true + + + diff --git a/retroshare-gui/src/gui/common/RsUrlHandler.cpp b/retroshare-gui/src/gui/common/RsUrlHandler.cpp index e2c3cd092..d307d7db1 100644 --- a/retroshare-gui/src/gui/common/RsUrlHandler.cpp +++ b/retroshare-gui/src/gui/common/RsUrlHandler.cpp @@ -22,14 +22,14 @@ #include #include #include -#include "RsCollectionFile.h" +#include "RsCollection.h" #include "RsUrlHandler.h" bool RsUrlHandler::openUrl(const QUrl& url) { - if(url.scheme() == QString("file") && url.toLocalFile().endsWith("."+RsCollectionFile::ExtensionString)) + if(url.scheme() == QString("file") && url.toLocalFile().endsWith("."+RsCollection::ExtensionString)) { - RsCollectionFile collection ; + RsCollection collection ; if(collection.load(url.toLocalFile())) { collection.downloadFiles() ; diff --git a/retroshare-gui/src/gui/connect/ConfCertDialog.cpp b/retroshare-gui/src/gui/connect/ConfCertDialog.cpp index e33ad13ec..dd18b4d16 100644 --- a/retroshare-gui/src/gui/connect/ConfCertDialog.cpp +++ b/retroshare-gui/src/gui/connect/ConfCertDialog.cpp @@ -83,7 +83,7 @@ ConfCertDialog::ConfCertDialog(const RsPeerId& id, const RsPgpId &pgp_id, QWidge { /* Invoke Qt Designer generated QObject setup routine */ ui.setupUi(this); - + Settings->loadWidgetInformation(this); ui.headerFrame->setHeaderImage(QPixmap(":/images/user/identityinfo64.png")); //ui.headerFrame->setHeaderText(tr("Friend node details")); @@ -105,6 +105,7 @@ ConfCertDialog::ConfCertDialog(const RsPeerId& id, const RsPgpId &pgp_id, QWidge ConfCertDialog::~ConfCertDialog() { + Settings->saveWidgetInformation(this); QMap::iterator it = instances_ssl.find(peerId); if (it != instances_ssl.end()) instances_ssl.erase(it); diff --git a/retroshare-gui/src/gui/connect/ConnectFriendWizard.cpp b/retroshare-gui/src/gui/connect/ConnectFriendWizard.cpp index ad6c93a1e..0eb83b5fc 100755 --- a/retroshare-gui/src/gui/connect/ConnectFriendWizard.cpp +++ b/retroshare-gui/src/gui/connect/ConnectFriendWizard.cpp @@ -475,33 +475,17 @@ void ConnectFriendWizard::initializePage(int id) else ui->addKeyToKeyring_CB->setToolTip(tr("Check this to add the key to your keyring\nThis might be useful for sending\ndistant messages to this peer\neven if you don't make friends.")) ; - //set the radio button to sign the GPG key - if (peerDetails.accept_connection && !peerDetails.ownsign) { - //gpg key connection is already accepted, don't propose to accept it again - ui->signGPGCheckBox->setChecked(false); - ui->acceptNoSignGPGCheckBox->hide(); - ui->acceptNoSignGPGCheckBox->setChecked(false); - } - if (!peerDetails.accept_connection && peerDetails.ownsign) { - //gpg key is already signed, don't propose to sign it again + if(tmp_det.accept_connection) { ui->acceptNoSignGPGCheckBox->setChecked(true); - ui->signGPGCheckBox->hide(); - ui->signGPGCheckBox->setChecked(false); + ui->acceptNoSignGPGCheckBox->setEnabled(false); + ui->acceptNoSignGPGCheckBox->setToolTip(tr("This key is already on your trusted list")); } - if (!peerDetails.accept_connection && !peerDetails.ownsign) { - ui->acceptNoSignGPGCheckBox->setChecked(true); - ui->signGPGCheckBox->show(); - ui->signGPGCheckBox->setChecked(false); - ui->acceptNoSignGPGCheckBox->show(); - } - if (peerDetails.accept_connection && peerDetails.ownsign) { - ui->acceptNoSignGPGCheckBox->setChecked(false); - ui->acceptNoSignGPGCheckBox->hide(); - ui->signGPGCheckBox->setChecked(false); - ui->signGPGCheckBox->hide(); - ui->alreadyRegisteredLabel->show(); - } else { + else ui->alreadyRegisteredLabel->hide(); + if(tmp_det.ownsign) { + ui->signGPGCheckBox->setChecked(true); + ui->signGPGCheckBox->setEnabled(false); + ui->signGPGCheckBox->setToolTip(tr("You have already signed this key")); } QString trustString; diff --git a/retroshare-gui/src/gui/connect/PGPKeyDialog.cpp b/retroshare-gui/src/gui/connect/PGPKeyDialog.cpp index e878a22ec..a9e67585b 100644 --- a/retroshare-gui/src/gui/connect/PGPKeyDialog.cpp +++ b/retroshare-gui/src/gui/connect/PGPKeyDialog.cpp @@ -65,7 +65,7 @@ PGPKeyDialog::PGPKeyDialog(const RsPeerId& id, const RsPgpId &pgp_id, QWidget *p { /* Invoke Qt Designer generated QObject setup routine */ ui.setupUi(this); - + Settings->loadWidgetInformation(this); // if(id.isNull()) // ui._useOldFormat_CB->setChecked(true) ; // else @@ -100,6 +100,7 @@ PGPKeyDialog::PGPKeyDialog(const RsPeerId& id, const RsPgpId &pgp_id, QWidget *p PGPKeyDialog::~PGPKeyDialog() { + Settings->saveWidgetInformation(this); QMap::iterator it = instances_pgp.find(pgpId); if (it != instances_pgp.end()) instances_pgp.erase(it); diff --git a/retroshare-gui/src/gui/feeds/SecurityIpItem.cpp b/retroshare-gui/src/gui/feeds/SecurityIpItem.cpp index c2cca5d57..be2d2cc19 100644 --- a/retroshare-gui/src/gui/feeds/SecurityIpItem.cpp +++ b/retroshare-gui/src/gui/feeds/SecurityIpItem.cpp @@ -79,9 +79,9 @@ void SecurityIpItem::setup() updateItem(); } -bool SecurityIpItem::isSame(const std::string& ipAddr, const std::string& ipAddrReported, uint32_t type) +bool SecurityIpItem::isSame(const RsPeerId &sslId, const std::string& ipAddr, const std::string& ipAddrReported, uint32_t type) { - if (mType == type && mIpAddr == ipAddr && mIpAddrReported == ipAddrReported) { + if (mType == type && mSslId==sslId && mIpAddr == ipAddr && mIpAddrReported == ipAddrReported) { return true; } diff --git a/retroshare-gui/src/gui/feeds/SecurityIpItem.h b/retroshare-gui/src/gui/feeds/SecurityIpItem.h index 949140c83..93f6a4c26 100644 --- a/retroshare-gui/src/gui/feeds/SecurityIpItem.h +++ b/retroshare-gui/src/gui/feeds/SecurityIpItem.h @@ -44,7 +44,7 @@ public: void updateItemStatic(); - bool isSame(const std::string& ipAddr, const std::string& ipAddrReported, uint32_t type); + bool isSame(const RsPeerId &sslId, const std::string& ipAddr, const std::string& ipAddrReported, uint32_t type); protected: /* FeedItem */ diff --git a/retroshare-gui/src/gui/gxs/GxsGroupDialog.cpp b/retroshare-gui/src/gui/gxs/GxsGroupDialog.cpp index 7757a7a15..378151d78 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupDialog.cpp +++ b/retroshare-gui/src/gui/gxs/GxsGroupDialog.cpp @@ -34,6 +34,8 @@ #include #include +#include + #include // Control of Publish Signatures. @@ -66,7 +68,7 @@ GxsGroupDialog::GxsGroupDialog(TokenQueue *tokenExternalQueue, uint32_t enableFl { /* Invoke the Qt Designer generated object setup routine */ ui.setupUi(this); - + mInternalTokenQueue = NULL; init(); @@ -87,6 +89,7 @@ GxsGroupDialog::GxsGroupDialog(TokenQueue *tokenExternalQueue, RsTokenService *t GxsGroupDialog::~GxsGroupDialog() { + Settings->saveWidgetInformation(this); if (mInternalTokenQueue) { delete(mInternalTokenQueue); } @@ -134,6 +137,7 @@ void GxsGroupDialog::init() ui.personal_required->setChecked(true) ; // this is always true initMode(); + Settings->loadWidgetInformation(this); } QIcon GxsGroupDialog::serviceWindowIcon() diff --git a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp index 998f01a6e..25f6bf2b6 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp +++ b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp @@ -121,6 +121,13 @@ GxsGroupFrameDialog::~GxsGroupFrameDialog() delete(ui); } +void GxsGroupFrameDialog::getGroupList(std::list& group_list) +{ + group_list = mCachedGroupMetas ; + + if(group_list.empty()) + requestGroupSummary(); +} void GxsGroupFrameDialog::initUi() { registerHelpButton(ui->helpButton, getHelpString(),pageName()) ; @@ -927,6 +934,8 @@ void GxsGroupFrameDialog::loadGroupSummary(const uint32_t &token) RsUserdata *userdata = NULL; loadGroupSummaryToken(token, groupInfo, userdata); + mCachedGroupMetas = groupInfo ; + insertGroupsData(groupInfo, userdata); mStateHelper->setLoading(TOKEN_TYPE_GROUP_SUMMARY, false); diff --git a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.h b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.h index 8138cc1e1..2c0dca594 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.h +++ b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.h @@ -81,6 +81,8 @@ public: virtual QString getHelpString() const =0; + virtual void getGroupList(std::list& groups) ; + protected: virtual void showEvent(QShowEvent *event); virtual void updateDisplay(bool complete); @@ -197,6 +199,8 @@ private: /** Qt Designer generated object */ Ui::GxsGroupFrameDialog *ui; + + std::list mCachedGroupMetas; }; #endif diff --git a/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.cpp b/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.cpp index f21fd8660..8c4f2c524 100644 --- a/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.cpp +++ b/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.cpp @@ -50,7 +50,7 @@ CreateGxsChannelMsg::CreateGxsChannelMsg(const RsGxsGroupId &cId, RsGxsMessageId { /* Invoke the Qt Designer generated object setup routine */ setupUi(this); - + Settings->loadWidgetInformation(this); mChannelQueue = new TokenQueue(rsGxsChannels->getTokenService(), this); headerFrame->setHeaderImage(QPixmap(":/images/channels.png")); @@ -95,6 +95,7 @@ CreateGxsChannelMsg::CreateGxsChannelMsg(const RsGxsGroupId &cId, RsGxsMessageId CreateGxsChannelMsg::~CreateGxsChannelMsg() { + Settings->saveWidgetInformation(this); #ifdef CHANNELS_FRAME_CATCHER delete fCatcher; #endif @@ -411,6 +412,11 @@ void CreateGxsChannelMsg::addExtraFile() } } +void CreateGxsChannelMsg::addHtmlText(const QString& text) +{ + msgEdit->setHtml(text) ; +} + void CreateGxsChannelMsg::addAttachment(const std::string &path) { /* add a SubFileItem to the attachment section */ diff --git a/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.h b/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.h index 4a99ad9aa..5168c6226 100644 --- a/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.h +++ b/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.h @@ -43,6 +43,7 @@ public: /** Default Destructor */ ~CreateGxsChannelMsg(); + void addHtmlText(const QString& text) ; void addAttachment(const std::string &path); void addAttachment(const RsFileHash &hash, const std::string &fname, uint64_t size, bool local, const RsPeerId &srcId,bool assume_file_ready = false); diff --git a/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.ui b/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.ui index 180a79672..3f4cd7d4b 100644 --- a/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.ui +++ b/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.ui @@ -60,7 +60,7 @@ false - 1 + 0 diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.cpp index 1bd22504e..c011621a8 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.cpp @@ -27,6 +27,7 @@ #include "GxsChannelDialog.h" #include "GxsChannelGroupDialog.h" #include "GxsChannelPostsWidget.h" +#include "CreateGxsChannelMsg.h" #include "GxsChannelUserNotify.h" #include "gui/gxs/GxsGroupShareKey.h" #include "gui/feeds/GxsChannelPostItem.h" @@ -76,6 +77,20 @@ UserNotify *GxsChannelDialog::getUserNotify(QObject *parent) return new GxsChannelUserNotify(rsGxsChannels, parent); } +void GxsChannelDialog::shareOnChannel(const RsGxsGroupId& channel_id,const QList& file_links) +{ + std::cerr << "Sharing file link on channel " << channel_id << ": Not yet implemented!" << std::endl; + + CreateGxsChannelMsg *msgDialog = new CreateGxsChannelMsg(channel_id) ; + + QString txt ; + for(QList::const_iterator it(file_links.begin());it!=file_links.end();++it) + txt += (*it).toHtml() + "\n" ; + + msgDialog->addHtmlText(txt); + msgDialog->show(); +} + QString GxsChannelDialog::text(TextType type) { switch (type) { diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.h b/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.h index 9915ec362..e1f3c75d0 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.h @@ -42,6 +42,8 @@ public: virtual UserNotify *getUserNotify(QObject *parent); + void shareOnChannel(const RsGxsGroupId& channel_id, const QList& file_link) ; + protected: /* GxsGroupFrameDialog */ virtual RetroShareLink::enumType getLinkType() { return RetroShareLink::TYPE_CHANNEL; } diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.cpp index 56cf042a5..b3f5662b4 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.cpp @@ -75,6 +75,8 @@ GxsChannelPostsWidget::GxsChannelPostsWidget(const RsGxsGroupId &channelId, QWid connect(ui->subscribeToolButton, SIGNAL(subscribe(bool)), this, SLOT(subscribeGroup(bool))); connect(NotifyQt::getInstance(), SIGNAL(settingsChanged()), this, SLOT(settingsChanged())); + ui->postButton->setText(tr("Add new post")); + /* add filter actions */ ui->filterLineEdit->addFilter(QIcon(), tr("Title"), FILTER_TITLE, tr("Search Title")); ui->filterLineEdit->addFilter(QIcon(), tr("Message"), FILTER_MSG, tr("Search Message")); diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.ui index 47f98bcdf..7e9b478ac 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.ui @@ -169,6 +169,9 @@ Post to Channel + + Add new post + :/images/mail_send.png:/images/mail_send.png @@ -179,6 +182,9 @@ 16 + + Qt::ToolButtonTextBesideIcon + true diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.cpp b/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.cpp index f08b1f61f..5209ca184 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.cpp +++ b/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.cpp @@ -210,6 +210,9 @@ GxsForumThreadWidget::GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget connect(ui->newmessageButton, SIGNAL(clicked()), this, SLOT(replytoforummessage())); connect(ui->newthreadButton, SIGNAL(clicked()), this, SLOT(createthread())); + ui->newmessageButton->setText(tr("Reply")); + ui->newthreadButton->setText(tr("New thread")); + connect(ui->threadTreeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(changedThread())); connect(ui->threadTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(clickedThread(QTreeWidgetItem*,int))); connect(ui->viewBox, SIGNAL(currentIndexChanged(int)), this, SLOT(changedViewBox())); @@ -228,6 +231,7 @@ GxsForumThreadWidget::GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget /* Set own item delegate */ RSElidedItemDelegate *itemDelegate = new RSElidedItemDelegate(this); itemDelegate->setSpacing(QSize(0, 2)); + itemDelegate->setOnlyPlainText(true); ui->threadTreeWidget->setItemDelegate(itemDelegate); /* Set header resize modes and initial section sizes */ diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.ui b/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.ui index 789616228..571e99c01 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.ui +++ b/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.ui @@ -84,6 +84,9 @@ Start new Thread for Selected Forum + + New Thread + :/images/mail_new.png:/images/mail_new.png @@ -94,6 +97,9 @@ 16 + + Qt::ToolButtonTextBesideIcon + true @@ -306,8 +312,8 @@ - 24 - 24 + 16777215 + 16777215 @@ -316,10 +322,16 @@ Reply Message + + Reply + :/images/mail_reply.png:/images/mail_reply.png + + Qt::ToolButtonTextBesideIcon + true @@ -358,7 +370,7 @@ - + Qt::Vertical @@ -471,7 +483,7 @@ - + diff --git a/retroshare-gui/src/gui/settings/AppearancePage.cpp b/retroshare-gui/src/gui/settings/AppearancePage.cpp index e5e742c72..5ffa7e10a 100755 --- a/retroshare-gui/src/gui/settings/AppearancePage.cpp +++ b/retroshare-gui/src/gui/settings/AppearancePage.cpp @@ -301,8 +301,15 @@ void AppearancePage::load() whileBlocking(ui.checkBoxDisableSysTrayToolTip)->setChecked(Settings->valueFromGroup("StatusBar", "DisableSysTrayToolTip", QVariant(false)).toBool()); whileBlocking(ui.checkBoxShowStatusStatus)-> setChecked(Settings->valueFromGroup("StatusBar", "ShowStatus", QVariant(true)).toBool()); whileBlocking(ui.checkBoxShowPeerStatus)-> setChecked(Settings->valueFromGroup("StatusBar", "ShowPeer", QVariant(true)).toBool()); - whileBlocking(ui.checkBoxShowNATStatus)-> setChecked(Settings->valueFromGroup("StatusBar", "ShowNAT", QVariant(true)).toBool()); - whileBlocking(ui.checkBoxShowDHTStatus)-> setChecked(Settings->valueFromGroup("StatusBar", "ShowDHT", QVariant(true)).toBool()); + if(MainWindow::hiddenmode) { + whileBlocking(ui.checkBoxShowNATStatus)-> setChecked(0); + whileBlocking(ui.checkBoxShowDHTStatus)-> setChecked(0); + ui.checkBoxShowNATStatus->setVisible(false); + ui.checkBoxShowDHTStatus->setVisible(false); + } else { + whileBlocking(ui.checkBoxShowNATStatus)-> setChecked(Settings->valueFromGroup("StatusBar", "ShowNAT", QVariant(true)).toBool()); + whileBlocking(ui.checkBoxShowDHTStatus)-> setChecked(Settings->valueFromGroup("StatusBar", "ShowDHT", QVariant(true)).toBool()); + } whileBlocking(ui.checkBoxShowHashingStatus)-> setChecked(Settings->valueFromGroup("StatusBar", "ShowHashing", QVariant(true)).toBool()); whileBlocking(ui.checkBoxShowDiscStatus)-> setChecked(Settings->valueFromGroup("StatusBar", "ShowDisc", QVariant(true)).toBool()); whileBlocking(ui.checkBoxShowRateStatus)-> setChecked(Settings->valueFromGroup("StatusBar", "ShowRate", QVariant(true)).toBool()); diff --git a/retroshare-gui/src/gui/settings/ServerPage.cpp b/retroshare-gui/src/gui/settings/ServerPage.cpp index ae7411c4f..e2179e8f1 100755 --- a/retroshare-gui/src/gui/settings/ServerPage.cpp +++ b/retroshare-gui/src/gui/settings/ServerPage.cpp @@ -152,7 +152,6 @@ ServerPage::ServerPage(QWidget * parent, Qt::WindowFlags flags) connect( ui.allowIpDeterminationCB, SIGNAL( toggled( bool ) ), this, SLOT( toggleIpDetermination(bool) ) ); connect( ui.cleanKnownIPs_PB, SIGNAL( clicked( ) ), this, SLOT( clearKnownAddressList() ) ); connect( ui.testIncoming_PB, SIGNAL( clicked( ) ), this, SLOT( saveAndTestInProxy() ) ); - connect( ui.showDiscStatusBar,SIGNAL(toggled(bool)),this,SLOT(updateShowDiscStatusBar())) ; #ifdef SERVER_DEBUG std::cerr << "ServerPage::ServerPage() called"; @@ -160,7 +159,6 @@ ServerPage::ServerPage(QWidget * parent, Qt::WindowFlags flags) #endif connect(ui.netModeComboBox,SIGNAL(currentIndexChanged(int)),this,SLOT(saveAddresses())); - connect(ui.discComboBox, SIGNAL(currentIndexChanged(int)),this,SLOT(saveAddresses())); connect(ui.localAddress, SIGNAL(textChanged(QString)),this,SLOT(saveAddresses())); connect(ui.extAddress, SIGNAL(textChanged(QString)),this,SLOT(saveAddresses())); connect(ui.dynDNS, SIGNAL(textChanged(QString)),this,SLOT(saveAddresses())); @@ -277,8 +275,6 @@ void ServerPage::toggleTunnelConnection(bool b) //rsPeers->allowTunnelConnection(b) ; } -void ServerPage::updateShowDiscStatusBar() { Settings->setStatusBarFlag(STATUSBAR_DISC, ui.showDiscStatusBar->isChecked()); } - /** Loads the settings for this page */ void ServerPage::load() { @@ -303,7 +299,8 @@ void ServerPage::load() if (mIsHiddenNode) { mHiddenType = detail.hiddenType; - ui.tabWidget->setTabEnabled(1,false) ; + ui.tabWidget->setTabEnabled(1,false) ; // ip filter + ui.tabWidget->setTabEnabled(3,false) ; // relay loadHiddenNode(); return; } @@ -380,7 +377,6 @@ void ServerPage::load() /* set DynDNS */ whileBlocking(ui.dynDNS) -> setText(QString::fromStdString(detail.dyndns)); - whileBlocking(ui.showDiscStatusBar)->setChecked(Settings->getStatusBarFlags() & STATUSBAR_DISC); whileBlocking(ui.ipAddressList)->clear(); for(std::list::const_iterator it(detail.ipAddressList.begin());it!=detail.ipAddressList.end();++it) @@ -1004,10 +1000,24 @@ void ServerPage::loadHiddenNode() ui.iconlabel_upnp->hide(); ui.label_nat->hide(); + ui.label_warningBandwidth->hide(); + ui.iconlabel_netLimited->hide(); + ui.textlabel_netLimited->hide(); + ui.iconlabel_ext->hide(); + ui.textlabel_ext->hide(); + ui.extPortLabel->hide(); + + ui.ipAddressLabel->hide(); + ui.cleanKnownIPs_PB->hide(); + + ui.ipAddressList->hide(); + ui.allowIpDeterminationCB->hide(); + ui.IPServersLV->hide(); + ui.textlabel_hiddenMode->show(); ui.iconlabel_hiddenMode->show() ; ui.iconlabel_hiddenMode->setPixmap(QPixmap(":/images/ledon1.png")); - + // CHANGE OPTIONS ON whileBlocking(ui.discComboBox)->removeItem(3); whileBlocking(ui.discComboBox)->removeItem(2); @@ -1048,9 +1058,6 @@ void ServerPage::loadHiddenNode() whileBlocking(ui.extAddress)->setText(tr("Hidden - See Config")); - whileBlocking(ui.showDiscStatusBar)->setChecked(Settings->getStatusBarFlags() & STATUSBAR_DISC); - ui.showDiscStatusBar->hide() ; // hidden because not functional at the moment. - //ui._turtle_enabled_CB->setChecked(rsTurtle->enabled()) ; // show what we have in ipAddresses. (should be nothing!) diff --git a/retroshare-gui/src/gui/settings/ServerPage.h b/retroshare-gui/src/gui/settings/ServerPage.h index 87f13400c..ce0e4baa4 100755 --- a/retroshare-gui/src/gui/settings/ServerPage.h +++ b/retroshare-gui/src/gui/settings/ServerPage.h @@ -87,7 +87,6 @@ private slots: // server void saveAddresses(); void saveRates(); - void updateShowDiscStatusBar() ; void toggleUPnP(); void toggleIpDetermination(bool) ; void toggleTunnelConnection(bool) ; diff --git a/retroshare-gui/src/gui/settings/ServerPage.ui b/retroshare-gui/src/gui/settings/ServerPage.ui index 0adb576c6..eac1323c8 100755 --- a/retroshare-gui/src/gui/settings/ServerPage.ui +++ b/retroshare-gui/src/gui/settings/ServerPage.ui @@ -482,13 +482,6 @@ These values don't take into account the Relays.
- - - Show Discovery information in statusbar - - - - If you uncheck this, RetroShare can only determine your IP @@ -504,7 +497,7 @@ behind a firewall or a VPN. - + @@ -517,7 +510,7 @@ behind a firewall or a VPN. - + Qt::Vertical @@ -531,7 +524,6 @@ behind a firewall or a VPN.
- showDiscStatusBar allowIpDeterminationCB IPServersLV ipAddressList @@ -831,7 +823,7 @@ behind a firewall or a VPN. - <html><head/><body><p>Outgoing Connctions</p><p>Configure your Tor and I2P SOCKS proxy here. <br/>If you prefer to use BOB to automatically manage I2P check the other tab.</p></body></html> + <html><head/><body><p>Configure your Tor and I2P SOCKS proxy here. <br/>If you prefer to use BOB to automatically manage I2P check the other tab.</p></body></html> @@ -1475,7 +1467,7 @@ You can connect to Hidden Nodes, even if you are running a standard Node, so why - <html><head/><body><p>Incoming Connection</p><p>Setup your hidden address (and port if needed)</p></body></html> + <html><head/><body><p>Setup your hidden address (and port if needed)</p></body></html> @@ -2097,7 +2089,6 @@ If you have issues connecting over Tor check the Tor logs too. extAddress extPort dynDNS - showDiscStatusBar diff --git a/retroshare-gui/src/gui/statistics/StatisticsWindow.cpp b/retroshare-gui/src/gui/statistics/StatisticsWindow.cpp index 597cf6320..d343f4d99 100644 --- a/retroshare-gui/src/gui/statistics/StatisticsWindow.cpp +++ b/retroshare-gui/src/gui/statistics/StatisticsWindow.cpp @@ -95,10 +95,11 @@ StatisticsWindow::StatisticsWindow(QWidget *parent) : { ui->setupUi(this); + Settings->loadWidgetInformation(this); + initStackedPage(); connect(ui->stackPages, SIGNAL(currentChanged(int)), this, SLOT(setNewPage(int))); ui->stackPages->setCurrentIndex(0); - int toolSize = Settings->getToolButtonSize(); ui->toolBar->setToolButtonStyle(Settings->getToolButtonStyle()); ui->toolBar->setIconSize(QSize(toolSize,toolSize)); @@ -110,6 +111,11 @@ StatisticsWindow::~StatisticsWindow() mInstance = NULL; } +void StatisticsWindow::closeEvent (QCloseEvent * /*event*/) +{ + Settings->saveWidgetInformation(this); +} + void StatisticsWindow::changeEvent(QEvent *e) { QMainWindow::changeEvent(e); @@ -147,9 +153,19 @@ void StatisticsWindow::initStackedPage() ui->stackPages->add(rttdlg = new RttStatistics(ui->stackPages), action = createPageAction(QIcon(IMAGE_RTT), tr("RTT Statistics"), grp)); - ui->stackPages->add(dhtw = new DhtWindow(ui->stackPages), + bool showdht = true; + RsPeerDetails detail; + if (rsPeers->getPeerDetails(rsPeers->getOwnId(), detail)) + { + if(detail.netMode == RS_NETMODE_HIDDEN) + showdht = false; + } + if(showdht) + { + ui->stackPages->add(dhtw = new DhtWindow(ui->stackPages), action = createPageAction(QIcon(IMAGE_DHT), tr("DHT"), grp)); - + } + /*std::cerr << "Looking for interfaces in existing plugins:" << std::endl; for(int i = 0;inbPlugins();++i) { diff --git a/retroshare-gui/src/gui/statistics/StatisticsWindow.h b/retroshare-gui/src/gui/statistics/StatisticsWindow.h index 3541a6cc6..a491792a8 100644 --- a/retroshare-gui/src/gui/statistics/StatisticsWindow.h +++ b/retroshare-gui/src/gui/statistics/StatisticsWindow.h @@ -65,7 +65,8 @@ public slots: protected: void changeEvent(QEvent *e); - + void closeEvent (QCloseEvent * event); + private: void initStackedPage(); diff --git a/retroshare-gui/src/retroshare-gui.pro b/retroshare-gui/src/retroshare-gui.pro index 6c4c821d1..cecc40dc7 100644 --- a/retroshare-gui/src/retroshare-gui.pro +++ b/retroshare-gui/src/retroshare-gui.pro @@ -476,7 +476,6 @@ HEADERS += rshare.h \ gui/common/ElidedLabel.h \ gui/common/vmessagebox.h \ gui/common/RsUrlHandler.h \ - gui/common/RsCollectionFile.h \ gui/common/RsCollectionDialog.h \ gui/common/rwindow.h \ gui/common/html.h \ @@ -568,7 +567,7 @@ HEADERS += rshare.h \ util/imageutil.h \ gui/NetworkDialog/pgpid_item_model.h \ gui/NetworkDialog/pgpid_item_proxy.h - + gui/common/RsCollection.h # gui/ForumsDialog.h \ # gui/forums/ForumDetails.h \ # gui/forums/EditForumDetails.h \ @@ -793,7 +792,6 @@ SOURCES += main.cpp \ gui/common/RSGraphWidget.cpp \ gui/common/ElidedLabel.cpp \ gui/common/vmessagebox.cpp \ - gui/common/RsCollectionFile.cpp \ gui/common/RsCollectionDialog.cpp \ gui/common/RsUrlHandler.cpp \ gui/common/rwindow.cpp \ @@ -930,7 +928,7 @@ SOURCES += main.cpp \ util/imageutil.cpp \ gui/NetworkDialog/pgpid_item_model.cpp \ gui/NetworkDialog/pgpid_item_proxy.cpp - + gui/common/RsCollection.cpp # gui/ForumsDialog.cpp \ # gui/forums/ForumDetails.cpp \ # gui/forums/EditForumDetails.cpp \ diff --git a/retroshare-gui/src/util/HandleRichText.cpp b/retroshare-gui/src/util/HandleRichText.cpp index 96fd36d73..ee087d66e 100644 --- a/retroshare-gui/src/util/HandleRichText.cpp +++ b/retroshare-gui/src/util/HandleRichText.cpp @@ -35,6 +35,8 @@ #include "HandleRichText.h" #include "gui/RetroShareLink.h" #include "util/ObjectPainter.h" +#include "util/imageutil.h" +#include "util/rsscopetimer.h" #include @@ -227,6 +229,7 @@ bool RsHtml::canReplaceAnchor(QDomDocument &/*doc*/, QDomElement &/*element*/, c switch (link.type()) { case RetroShareLink::TYPE_UNKNOWN: case RetroShareLink::TYPE_FILE: + case RetroShareLink::TYPE_FILE_TREE: case RetroShareLink::TYPE_PERSON: case RetroShareLink::TYPE_FORUM: case RetroShareLink::TYPE_CHANNEL: @@ -257,6 +260,7 @@ void RsHtml::anchorStylesheetForImg(QDomDocument &/*doc*/, QDomElement &/*elemen switch (link.type()) { case RetroShareLink::TYPE_UNKNOWN: case RetroShareLink::TYPE_FILE: + case RetroShareLink::TYPE_FILE_TREE: case RetroShareLink::TYPE_PERSON: case RetroShareLink::TYPE_FORUM: case RetroShareLink::TYPE_CHANNEL: @@ -1105,7 +1109,7 @@ QString RsHtml::toHtml(QString text, bool realHtml) } /** Loads image and converts image to embedded image HTML fragment **/ -bool RsHtml::makeEmbeddedImage(const QString &fileName, QString &embeddedImage, const int maxPixels) +bool RsHtml::makeEmbeddedImage(const QString &fileName, QString &embeddedImage, const int maxPixels, const int maxBytes) { QImage image; @@ -1113,54 +1117,15 @@ bool RsHtml::makeEmbeddedImage(const QString &fileName, QString &embeddedImage, fprintf (stderr, "RsHtml::makeEmbeddedImage() - image \"%s\" can't be load\n", fileName.toLatin1().constData()); return false; } - return RsHtml::makeEmbeddedImage(image, embeddedImage, maxPixels); + return RsHtml::makeEmbeddedImage(image, embeddedImage, maxPixels, maxBytes); } /** Converts image to embedded image HTML fragment **/ -bool RsHtml::makeEmbeddedImage(const QImage &originalImage, QString &embeddedImage, const int maxPixels) +bool RsHtml::makeEmbeddedImage(const QImage &originalImage, QString &embeddedImage, const int maxPixels, const int maxBytes) { - QByteArray bytearray; - QBuffer buffer(&bytearray); - QImage resizedImage; - const QImage *image = &originalImage; - - if (maxPixels > 0) { - QSize imgSize = originalImage.size(); - if ((imgSize.height() * imgSize.width()) > maxPixels) { - // image is too large - resize keeping aspect ratio - QSize newSize; - newSize.setWidth(int(qSqrt((maxPixels * imgSize.width()) / imgSize.height()))); - newSize.setHeight(int((imgSize.height() * newSize.width()) / imgSize.width())); - - // ask user - QMessageBox msgBox; - msgBox.setText(QString(QApplication::translate("RsHtml", "Image is oversized for transmission.\nReducing image to %1x%2 pixels?")).arg(newSize.width()).arg(newSize.height())); - msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); - msgBox.setDefaultButton(QMessageBox::Ok); - if (msgBox.exec() != QMessageBox::Ok) { - return false; - } - resizedImage = originalImage.scaled(newSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); - image = &resizedImage; - } - } - - if (buffer.open(QIODevice::WriteOnly)) { - if (image->save(&buffer, "PNG")) { - QByteArray encodedByteArray = bytearray.toBase64(); - - embeddedImage = ""); - } else { - //fprintf (stderr, "RsHtml::makeEmbeddedImage() - image can't be saved to buffer\n"); - return false; - } - } else { - fprintf (stderr, "RsHtml::makeEmbeddedImage() - buffer can't be opened\n"); - return false; - } - return true; + RsScopeTimer s("Embed image"); + QImage opt; + return ImageUtil::optimizeSize(embeddedImage, originalImage, opt, maxPixels, maxBytes); } QString RsHtml::plainText(const QString &text) diff --git a/retroshare-gui/src/util/HandleRichText.h b/retroshare-gui/src/util/HandleRichText.h index 54036b53c..a8f8d66ef 100644 --- a/retroshare-gui/src/util/HandleRichText.h +++ b/retroshare-gui/src/util/HandleRichText.h @@ -69,8 +69,8 @@ public: static void optimizeHtml(QString &text, unsigned int flag = 0, const QColor &backgroundColor = Qt::white, qreal desiredContrast = 1.0, int desiredMinimumFontSize = 10); static QString toHtml(QString text, bool realHtml = true); - static bool makeEmbeddedImage(const QString &fileName, QString &embeddedImage, const int maxPixels); - static bool makeEmbeddedImage(const QImage &originalImage, QString &embeddedImage, const int maxPixels); + static bool makeEmbeddedImage(const QString &fileName, QString &embeddedImage, const int maxPixels, const int maxBytes = -1); + static bool makeEmbeddedImage(const QImage &originalImage, QString &embeddedImage, const int maxPixels, const int maxBytes = -1); static QString plainText(const QString &text); static QString plainText(const std::string &text); diff --git a/retroshare-gui/src/util/imageutil.cpp b/retroshare-gui/src/util/imageutil.cpp index 1a08ffb07..c0cc2cde4 100644 --- a/retroshare-gui/src/util/imageutil.cpp +++ b/retroshare-gui/src/util/imageutil.cpp @@ -1,5 +1,6 @@ #include "imageutil.h" #include "util/misc.h" +#include "util/rsscopetimer.h" #include #include @@ -8,6 +9,11 @@ #include #include #include +#include +#include +#include +#include +#include ImageUtil::ImageUtil() {} @@ -42,3 +48,211 @@ void ImageUtil::extractImage(QWidget *window, QTextCursor cursor) } } +bool ImageUtil::optimizeSize(QString &html, const QImage& original, QImage &optimized, int maxPixels, int maxBytes) +{ + //nothing to do if it fits into the limits + optimized = original; + if ((maxPixels <= 0) || (optimized.width()*optimized.height() <= maxPixels)) { + if(checkSize(html, optimized, maxBytes) <= maxBytes) { + return true; + } + } + + QVector ct; + quantization(original, ct); + + //Downscale the image to fit into maxPixels + double whratio = (qreal)original.width() / (qreal)original.height(); + int maxwidth; + if(maxPixels > 0) + maxwidth = (int)sqrt((double)(maxPixels) * whratio); + else + maxwidth = original.width(); + + int minwidth = (int)sqrt(100.0 * whratio); + + //if maxBytes not defined, do not reduce color space, just downscale + if(maxBytes <= 0) { + checkSize(html, optimized = original.scaledToWidth(maxwidth, Qt::SmoothTransformation), maxBytes); + return true; + } + + //Use binary search to find a suitable image size + linear regression to guess the file size + double maxsize = (double)checkSize(html, optimized = original.scaledToWidth(maxwidth, Qt::SmoothTransformation).convertToFormat(QImage::Format_Indexed8, ct), maxBytes); + if(maxsize <= maxBytes) return true; //success + double minsize = (double)checkSize(html, optimized = original.scaledToWidth(minwidth, Qt::SmoothTransformation).convertToFormat(QImage::Format_Indexed8, ct), maxBytes); + if(minsize > maxBytes) return false; //impossible + +// std::cout << "maxS: " << maxsize << " minS: " << minsize << std::endl; +// std::cout << "maxW: " << maxwidth << " minW: " << minwidth << std::endl; + int region = 500; + bool success = false; + do { + double m = (maxsize - minsize) / ((double)maxwidth * (double)maxwidth / whratio - (double)minwidth * (double)minwidth / whratio); + double b = maxsize - m * ((double)maxwidth * (double)maxwidth / whratio); + double a = ((double)(maxBytes - region/2) - b) / m; //maxBytes - region/2 target the center of the accepted region + int nextwidth = (int)sqrt(a * whratio); + double nextsize = (double)checkSize(html, optimized = original.scaledToWidth(nextwidth, Qt::SmoothTransformation).convertToFormat(QImage::Format_Indexed8, ct), maxBytes); + if(nextsize <= maxBytes) { + minsize = nextsize; + minwidth = nextwidth; + if(nextsize >= (maxBytes - region)) //the file size is close anough to the limit + success = true; + } else { + maxsize = nextsize; + maxwidth = nextwidth; + } +// std::cout << "maxS: " << maxsize << " minS: " << minsize << std::endl; +// std::cout << "maxW: " << maxwidth << " minW: " << minwidth << std::endl; + } while(!success); + return true; + //html = html.arg(original.width()); + //std::cout << html.toStdString() << std::endl; +} + +int ImageUtil::checkSize(QString &embeddedImage, const QImage &img, int maxBytes) +{ + RsScopeTimer st("Check size"); + + QByteArray bytearray; + QBuffer buffer(&bytearray); + int size = 0; + + //std::cout << QString("Trying image: format PNG, size %1x%2, colors %3\n").arg(img.width()).arg(img.height()).arg(img.colorCount()).toStdString(); + if (buffer.open(QIODevice::WriteOnly)) { + if (img.save(&buffer, "PNG", 0)) { + size = bytearray.length() * 4/3; + if((maxBytes > 0) && (size > maxBytes)) // *4/3 for base64 + { + //std::cout << QString("\tToo large, size: %1, limit: %2 bytes\n").arg(bytearray.length() * 4/3).arg(maxBytes).toStdString(); + }else{ + //std::cout << QString("\tOK, size: %1, limit: %2 bytes\n").arg(bytearray.length() * 4/3).arg(maxBytes).toStdString(); + QByteArray encodedByteArray = bytearray.toBase64(); + //embeddedImage = ""); + } + } else { + std::cerr << "ImageUtil: image can't be saved to buffer" << std::endl; + } + buffer.close(); + bytearray.clear(); + } else { + std::cerr << "ImageUtil: buffer can't be opened" << std::endl; + } + return size; +} + +bool redLessThan(const QRgb &c1, const QRgb &c2) +{ + return qRed(c1) < qRed(c2); +} + +bool greenLessThan(const QRgb &c1, const QRgb &c2) +{ + return qGreen(c1) < qGreen(c2); +} + +bool blueLessThan(const QRgb &c1, const QRgb &c2) +{ + return qBlue(c1) < qBlue(c2); +} + +//median cut algoritmh +void ImageUtil::quantization(const QImage &img, QVector &palette) +{ + int bits = 4; // bits/pixel + int samplesize = 100000; //only take this many color samples + + RsScopeTimer st("Quantization"); + QSet colors; + + //collect color information + int imgsize = img.width()*img.height(); + int width = img.width(); + samplesize = qMin(samplesize, imgsize); + double sampledist = (double)imgsize / (double)samplesize; + for (double i = 0; i < imgsize; i += sampledist) { + QRgb pixel = img.pixel((int)i % width, (int)i / width); + colors.insert(pixel); + } + + QList colorlist = colors.toList(); + //don't do the algoritmh if we have less than 16 different colors + if(colorlist.size() <= (1 << bits)) { + for(int i = 0; i < colors.count(); ++i) + palette.append(colorlist[i]); + } else { + quantization(colorlist.begin(), colorlist.end(), bits, palette); + } +} + +void ImageUtil::quantization(QList::iterator begin, QList::iterator end, int depth, QVector &palette) +{ + //the buckets are ready + if(depth == 0) { + avgbucket(begin, end, palette); + return; + } + + //nothing to do + int count = end - begin; + if(count == 1) { + palette.append(*begin); + return; + } + + //widest color channel + int rl = 255; + int gl = 255; + int bl = 255; + int rh = 0; + int gh = 0; + int bh = 0; + for(QList::iterator it = begin; it < end; ++it) { + rl = qMin(rl, qRed(*it)); + gl = qMin(gl, qGreen(*it)); + bl = qMin(bl, qBlue(*it)); + rh = qMax(rh, qRed(*it)); + gh = qMax(gh, qGreen(*it)); + bh = qMax(bh, qBlue(*it)); + } + int red = rh - rl; + int green = gh - gl; + int blue = bh - bl; + + //order by the widest channel + if(red > green) + if(red > blue) + qSort(begin, end, redLessThan); + else + qSort(begin, end, blueLessThan); + else + if(green > blue) + qSort(begin, end, greenLessThan); + else + qSort(begin, end, blueLessThan); + + //split into two buckets + QList::iterator split = begin + count / 2; + quantization(begin, split, depth - 1, palette); + quantization(split, end, depth - 1, palette); +} + +void ImageUtil::avgbucket(QList::iterator begin, QList::iterator end, QVector &palette) +{ + int red = 0; + int green = 0; + int blue = 0; + int count = end - begin; + + for(QList::iterator it = begin; it < end; ++it) { + red += qRed(*it); + green += qGreen(*it); + blue += qBlue(*it); + } + + QRgb color = qRgb(red/count, green/count, blue/count); + palette.append(color); +} diff --git a/retroshare-gui/src/util/imageutil.h b/retroshare-gui/src/util/imageutil.h index 30e6ecb67..e4dc717fa 100644 --- a/retroshare-gui/src/util/imageutil.h +++ b/retroshare-gui/src/util/imageutil.h @@ -3,6 +3,7 @@ #include #include +#include class ImageUtil { @@ -10,6 +11,13 @@ public: ImageUtil(); static void extractImage(QWidget *window, QTextCursor cursor); + static bool optimizeSize(QString &html, const QImage& original, QImage &optimized, int maxPixels = -1, int maxBytes = -1); + + private: + static int checkSize(QString& embeddedImage, const QImage& img, int maxBytes = -1); + static void quantization(const QImage& img, QVector& palette); + static void quantization(QList::iterator begin, QList::iterator end, int depth, QVector& palette); + static void avgbucket(QList::iterator begin, QList::iterator end, QVector& palette); }; #endif // IMAGEUTIL_H diff --git a/retroshare-qml-app/src/ChatView.qml b/retroshare-qml-app/src/ChatView.qml index 8a814545b..07b81f9ad 100644 --- a/retroshare-qml-app/src/ChatView.qml +++ b/retroshare-qml-app/src/ChatView.qml @@ -28,7 +28,7 @@ Item { id: chatView property string chatId - property var gxsInfo: "" + property var gxsInfo: ({}) property int token: 0 property string objectName:"chatView" @@ -64,7 +64,7 @@ Item function changeState () { toolBar.state = "CHATVIEW" - gxsInfo= ChatCache.lastMessageCache.getGxsFromChatId(chatView.chatId) + gxsInfo = ChatCache.lastMessageCache.getGxsFromChatId(chatView.chatId) toolBar.gxsSource = gxsInfo.gxs toolBar.titleText = gxsInfo.name } @@ -100,7 +100,7 @@ Item anchors.fill: parent anchors.topMargin: parent.height / 2 - anchors.bottomMargin: categorySelectorHeight + anchors.bottomMargin: if(!androidMode) categorySelectorHeight property int categorySelectorHeight: 50 @@ -114,6 +114,7 @@ Item name: "EMOJI_HIDDEN" PropertyChanges { target: emojiPicker; anchors.topMargin: parent.height } PropertyChanges { target: emojiPicker; anchors.bottomMargin: -1 } + PropertyChanges { target: emojiPicker; height: 0 } }, State { name: "EMOJI_SHOWN" @@ -132,7 +133,7 @@ Item id: inferiorPanel height: ( msgComposer.height > styles.height)? msgComposer.height: styles.height width: parent.width - anchors.bottom: parent.bottom + anchors.bottom: emojiPicker.androidMode ? emojiPicker.top : parent.bottom Rectangle { diff --git a/retroshare-qml-app/src/GxsIdentityDelegate.qml b/retroshare-qml-app/src/GxsIdentityDelegate.qml index 650cf6551..6bdbf2524 100644 --- a/retroshare-qml-app/src/GxsIdentityDelegate.qml +++ b/retroshare-qml-app/src/GxsIdentityDelegate.qml @@ -50,7 +50,14 @@ Item console.log("GxsIntentityDelegate onclicked:", model.name, model.gxs_id) contactsView.searching = false - if(model.own) contactsView.own_gxs_id = model.gxs_id + if(model.own) + { + contactsView.own_gxs_id = model.gxs_id + stackView.push( + "qrc:/ContactDetails.qml", + {md: ChatCache.contactsCache.getContactFromGxsId(model.gxs_id)}) + + } else { startDistantChat() diff --git a/retroshare-qml-app/src/TokensManager.qml b/retroshare-qml-app/src/TokensManager.qml index fd2bfe73e..6a7bd498a 100644 --- a/retroshare-qml-app/src/TokensManager.qml +++ b/retroshare-qml-app/src/TokensManager.qml @@ -28,6 +28,13 @@ QtObject property var tokens: ({}) function registerToken(token, callback) { + if(!maybeToken(token)) + { + console.error("TokensManager attempt to register a non int token") + console.trace() + return + } + if (Array.isArray(tokens[token])) { if(QT_DEBUG) @@ -72,6 +79,7 @@ QtObject delete tokens[token] } function isTokenValid(token) { return Array.isArray(tokens[token]) } + function maybeToken(value) { return Number(value) === parseInt(value) } property alias refreshInterval: refreshTokensTimer.interval @@ -81,16 +89,22 @@ QtObject onResponseReceived: { + /* TODO: This is vital enough and if some fails appens can create + * difficult to debug unexpected behaviours in any place of the app. + * We should do some more checking on the data received here + */ var jsonData = JSON.parse(msg).data + // console.log("refreshTokensApi got expired tokens:", msg) var arrayLength = jsonData.length - for (var i = 0; i < arrayLength; i++) - { + for (var i = 0; i < arrayLength; ++i) tokensManager.tokenExpire(jsonData[i]) - } } Component.onCompleted: { + /* Disable debugging only for this instance of LibresapiLocalClient + * as it is particularly noisy and repetitive, and not useful in + * most of the cases */ if(QT_DEBUG) debug = false openConnection(apiSocketPath) @@ -99,8 +113,13 @@ QtObject function refreshTokens() { - request("/statetokenservice/*", - '['+Object.keys(tokensManager.tokens)+']') + var tokensArr = Object.keys(tokensManager.tokens) + + // Filter to avoid "undefined" being sent toghether with tokens + var tokensStr = '['+ tokensArr.filter(maybeToken) +']' + + // console.log("refreshTokensApi checking tokens:", tokensStr) + request("/statetokenservice/*", tokensStr) } } diff --git a/retroshare-qml-app/src/android/gradle.properties b/retroshare-qml-app/src/android/gradle.properties index 6915cd803..47ab4e463 100644 --- a/retroshare-qml-app/src/android/gradle.properties +++ b/retroshare-qml-app/src/android/gradle.properties @@ -6,4 +6,4 @@ androidBuildToolsVersion=24.0.1 androidCompileSdkVersion=23 buildDir=.build -qt5AndroidDir=/opt/Qt5.8.0/5.8/android_armv7/src/android/java +qt5AndroidDir=/opt/Qt/5.9.2/android_armv7/src/android/java diff --git a/retroshare-qml-app/src/androidimagepicker.h b/retroshare-qml-app/src/androidimagepicker.h index ad5f0e302..06ceade18 100644 --- a/retroshare-qml-app/src/androidimagepicker.h +++ b/retroshare-qml-app/src/androidimagepicker.h @@ -9,6 +9,8 @@ #include #include +#include "qpainter.h" + #ifdef __ANDROID__ # include @@ -41,14 +43,44 @@ public slots: QString localPath = url.toLocalFile(); qDebug() << "imageToBase64() local path:" << localPath ; - - // Read the image - QImageReader reader; - reader.setFileName(localPath); - QImage image = reader.read(); + QImage image= getImage (localPath); image = image.scaled(96,96,Qt::KeepAspectRatio,Qt::SmoothTransformation); + qDebug() << "imageToBase64() encoding" ; + + return imageToB64(image); + } + + static QString b64AvatarGen (QVariantList onloads, int size) + { + qDebug() << "b64AvatarGen(): Generating face Avatar from"; + + QImage result(size, size, QImage::Format_ARGB32_Premultiplied); + QPainter painter(&result); + + + int counter = 0; + for (QVariantList::iterator j = onloads.begin(); j != onloads.end(); j++) + { + QImage image = getImage (":/"+(*j).toString()); + painter.drawImage(0, 0, image); // xi, yi is the position for imagei + counter++; + } + painter.end(); + + return imageToB64(result); + } + + static QImage getImage (QString const& path) + { + QImageReader reader; + reader.setFileName(path); + return reader.read(); + } + + static QString imageToB64 (QImage image) + { // Transform image into PNG format QByteArray ba; QBuffer buffer( &ba ); @@ -56,11 +88,8 @@ public slots: image.save( &buffer, "png" ); // Get Based 64 image string - QString encoded = QString(ba.toBase64()); - - qDebug() << "imageToBase64() encoded" ; - - return encoded; - + return QString(ba.toBase64()); } + + }; diff --git a/retroshare-qml-app/src/components/Faces.qml b/retroshare-qml-app/src/components/Faces.qml index f814ebb6d..27ba5dab8 100644 --- a/retroshare-qml-app/src/components/Faces.qml +++ b/retroshare-qml-app/src/components/Faces.qml @@ -4,64 +4,22 @@ import "../" // Needed by ChatCache (where stores generated faces) Item { - id: faces property string hash property var facesCache: ChatCache.facesCache - Image { id: imageAvatar width: height height: iconSize - visible: true } - Canvas - { - id: canvasAvatar - width: height - height: canvasSizes - visible: false - - renderStrategy: Canvas.Threaded; - renderTarget: Canvas.Image; - - property var images - property var callback - - - onPaint: - { - var ctx = getContext("2d"); - - if (images) - { - for (y = 0 ; y< nPieces ; y++) - { - ctx.drawImage(images[y], 0, 0, iconSize, iconSize ) - } - } - - } - - onPainted: - { - if (callback) - { - var data = toDataURL('image/png') - callback(data) - } - } - } - - Component.onCompleted: - { - createFromHex(hash) - } + Component.onCompleted: createFromHex(hash) + /* TODO: Is there a reason why we are using var and not proper type for the + * following properties? */ property var facesPath: "/icons/faces/" @@ -134,11 +92,9 @@ Item { var url = src(gender, i, data[i+1]) onloads.push(url) - canvasAvatar.loadImage(url) } - canvasAvatar.images = onloads - canvasAvatar.callback = callback - canvasAvatar.requestPaint() + var base64Image = androidImagePicker.b64AvatarGen(onloads, canvasSizes) + callback("data:image/png;base64,"+base64Image) } // Create the identicon @@ -146,32 +102,11 @@ Item { var iconId = [dataHex, iconSize]; var update = function(data) - { - // This conditions are for solve a bug on an Lg S3. - // On this device the toDataURL() is incompleted. - // So for see the complete avatar at least at first execution we'll show the canvas, - // instead of the image component. - // See issue: https://gitlab.com/angesoc/RetroShare/issues/37 - if (facesCache.iconCache[iconId]) - { - imageAvatar.source = data - imageAvatar.visible = true - canvasAvatar.visible = false - - canvasAvatar.height = 0 - imageAvatar.height = iconSize - } - else - { - canvasAvatar.visible = true - imageAvatar.visible = false - - canvasAvatar.height = iconSize - imageAvatar.height = 0 - } - - facesCache.iconCache[iconId] = data; - } + { + imageAvatar.source = data + imageAvatar.height = iconSize + facesCache.iconCache[iconId] = data; + } if (facesCache.iconCache.hasOwnProperty(iconId)) { @@ -184,13 +119,13 @@ Item else { var onImageGenerated = function(data) - { + { - facesCache.callbackCache[iconId].forEach(function(callback) - { - callback(data); - }) - } + facesCache.callbackCache[iconId].forEach(function(callback) + { + callback(data); + }) + } facesCache.callbackCache[iconId] = [update]; if (dataHex) diff --git a/retroshare-qml-app/src/components/emoji/EmojiButton.qml b/retroshare-qml-app/src/components/emoji/EmojiButton.qml index 3720d9f5c..34408b2e2 100644 --- a/retroshare-qml-app/src/components/emoji/EmojiButton.qml +++ b/retroshare-qml-app/src/components/emoji/EmojiButton.qml @@ -1,12 +1,14 @@ import QtQuick 2.7 import QtQuick.Controls.Styles 1.2 -Rectangle { +Rectangle +{ id: emojiButton property var fontName - Text { + Text + { id: emojiText color: "gray" text: qsTr(eCatText) @@ -17,17 +19,22 @@ Rectangle { state: "RELEASED" - states: [ - State { + states: + [ + State + { name: "PRESSED" - PropertyChanges { + PropertyChanges + { target: emojiText font.pixelSize: emojiButton.width - 10 } }, - State { + State + { name: "RELEASED" - PropertyChanges { + PropertyChanges + { target: emojiText font.pixelSize: emojiButton.width - 8 } @@ -35,20 +42,25 @@ Rectangle { ] - MouseArea { + MouseArea + { anchors.fill: parent hoverEnabled: true - onEntered: { + onEntered: + { emojiText.color = "black" } - onExited: { + onExited: + { emojiText.color = "gray" } - onPressedChanged: { + onPressedChanged: + { emojiButton.state = emojiButton.state == "PRESSED" ? "RELEASED" : "PRESSED" } - onClicked: { + onClicked: + { Qt.emojiClickedHandler(emojiText.text) } } diff --git a/retroshare-qml-app/src/components/emoji/EmojiCategoryButton.qml b/retroshare-qml-app/src/components/emoji/EmojiCategoryButton.qml index 12983cf5b..251d8f70e 100644 --- a/retroshare-qml-app/src/components/emoji/EmojiCategoryButton.qml +++ b/retroshare-qml-app/src/components/emoji/EmojiCategoryButton.qml @@ -1,30 +1,37 @@ import QtQuick 2.7 import QtQuick.Controls.Styles 1.2 -Rectangle { +Rectangle +{ id: emojiCategoryButton property string categoryName property var fontName - function completedHandler() { + function completedHandler() + { categoryName = eCatName //initialize - if (parent.currSelEmojiButton === undefined) { + if (parent.currSelEmojiButton === undefined) + { clickedHandler() } } - function pressedHandler() { - if (state != "SELECTED") { + function pressedHandler() + { + if (state != "SELECTED") + { state = state == "PRESSED" ? "RELEASED" : "PRESSED" } } - function clickedHandler() { - if (parent.currSelEmojiButton !== undefined) { + function clickedHandler() + { + if (parent.currSelEmojiButton !== undefined) + { parent.currSelEmojiButton.state = "RELEASED" } @@ -34,7 +41,8 @@ Rectangle { } - Text { + Text + { id: emojiText color: "gray" text: qsTr(eCatText) @@ -45,31 +53,39 @@ Rectangle { state: "RELEASED" - states: [ - State { + states: + [ + State + { name: "PRESSED" - PropertyChanges { + PropertyChanges + { target: emojiText font.pixelSize: emojiCategoryButton.width - 10 } }, - State { + State + { name: "RELEASED" - PropertyChanges { + PropertyChanges + { target: emojiText font.pixelSize: emojiCategoryButton.width - 8 } }, - State { + State + { name: "SELECTED" - PropertyChanges { + PropertyChanges + { target: emojiCategoryButton color: "#ADD6FF" } } ] - MouseArea { + MouseArea + { anchors.fill: parent hoverEnabled: true onEntered: emojiText.color = "black" diff --git a/retroshare-qml-app/src/components/emoji/EmojiPicker.qml b/retroshare-qml-app/src/components/emoji/EmojiPicker.qml index f99d8f444..844ba60b1 100644 --- a/retroshare-qml-app/src/components/emoji/EmojiPicker.qml +++ b/retroshare-qml-app/src/components/emoji/EmojiPicker.qml @@ -2,33 +2,41 @@ import QtQuick 2.7 import QtQuick.Controls 2.0 import "emoji.js" as EmojiJSON -Rectangle { +Rectangle +{ id: emojiPicker property EmojiCategoryButton currSelEmojiButton property variant emojiParsedJson property int buttonWidth: 40 property TextArea textArea + property bool androidMode: Qt.platform.os === "android" // On Desktop appears on top of text field, instead appears in place of virtual keyboard (under text field) + + property var rootFontName: theme.emojiFontName //displays all Emoji of one categroy by modifying the ListModel of emojiGrid - function categoryChangedHandler (newCategoryName){ + function categoryChangedHandler (newCategoryName) + { emojiByCategory.clear() - for (var i = 0; i < emojiParsedJson.emoji_by_category[newCategoryName].length; i++) { + for (var i = 0; i < emojiParsedJson.emoji_by_category[newCategoryName].length; i++) + { var elem = emojiParsedJson.emoji_by_category[newCategoryName][i] emojiByCategory.append({eCatName: newCategoryName, eCatText: elem}) } } //adds the clicked Emoji (and one ' ' if the previous character isn't an Emoji) to textArea - function emojiClickedHandler(selectedEmoji) { + function emojiClickedHandler(selectedEmoji) + { var strAppnd = "" var plainText = textArea.getText(0, textArea.length) if (plainText.length > 0) { var lastChar = plainText[plainText.length-1] - if ((lastChar !== ' ') && (lastChar.charCodeAt(0) < 255)) { + if ((lastChar !== ' ') && (lastChar.charCodeAt(0) < 255)) + { strAppnd = " " } } @@ -38,10 +46,12 @@ Rectangle { } //parses JSON, publishes button handlers and inits textArea - function completedHandler() { + function completedHandler() + { // emojiParsedJson = JSON.parse(EmojiJSON.emoji_json) emojiParsedJson = EmojiJSON.emoji_json - for (var i = 0; i < emojiParsedJson.emoji_categories.length; i++) { + for (var i = 0; i < emojiParsedJson.emoji_categories.length; i++) + { var elem = emojiParsedJson.emoji_categories[i] emojiCategoryButtons.append({eCatName: elem.name, eCatText: elem.emoji_unified}) } @@ -56,29 +66,39 @@ Rectangle { //checks if the previous character is an Emoji and adds a ' ' if that's the case //this is necessary, because Emoji use a bigger font-size, and that font-size is kept using without a ' ' - function keyPressedHandler(event) { + function keyPressedHandler(event) + { var testStr = textArea.getText(textArea.length-2, textArea.length) var ptrn = new RegExp("[\uD800-\uDBFF][\uDC00-\uDFFF]") - if ((event.key !== Qt.Key_Backspace) && (ptrn.test(testStr))) { + if ((event.key !== Qt.Key_Backspace) && (ptrn.test(testStr))) + { textArea.text += " " textArea.cursorPosition = textArea.length } } //all emoji of one category - ListModel { + ListModel + { id: emojiByCategory } - GridView { + GridView + { id: emojiGrid width: parent.width - anchors.fill: parent - anchors.bottomMargin: buttonWidth - cellWidth: buttonWidth; cellHeight: buttonWidth + anchors.fill: parent + anchors + { + bottomMargin: if (!androidMode) buttonWidth + topMargin: if (androidMode) buttonWidth + } + + cellWidth: buttonWidth; cellHeight: buttonWidth model: emojiByCategory - delegate: EmojiButton { + delegate: EmojiButton + { width: buttonWidth height: buttonWidth color: emojiPicker.color @@ -88,37 +108,52 @@ Rectangle { //seperator - Rectangle { - color: emojiPicker.color - anchors.bottom: parent.bottom + Rectangle + { + color: "gray" + anchors + { + bottom: if (!androidMode) parent.bottom + top: if (androidMode) parent.top + bottomMargin: if (!androidMode) buttonWidth + topMargin: if (androidMode) buttonWidth + } + + width: parent.width + height: 1 + } + Rectangle + { + color: emojiPicker.color width: parent.width height: buttonWidth - } - Rectangle { - color: "black" - anchors.bottom: parent.bottom - anchors.bottomMargin: buttonWidth - width: parent.width - height: 1 + + anchors + { + bottom: if (!androidMode) parent.bottom + top: if (androidMode) parent.top + } + + //emoji category selector + ListView + { + width: parent.width + orientation: ListView.Horizontal + anchors.fill: parent + + model: emojiCategoryButtons + delegate: EmojiCategoryButton + { + width: buttonWidth + height: buttonWidth + color: emojiPicker.color + fontName: rootFontName + } + } } - //emoji category selector - ListView { - width: parent.width - anchors.bottom: parent.bottom - anchors.bottomMargin: buttonWidth - orientation: ListView.Horizontal - - model: emojiCategoryButtons - delegate: EmojiCategoryButton { - width: buttonWidth - height: buttonWidth - color: emojiPicker.color - fontName: rootFontName - } - } - - ListModel { + ListModel + { id: emojiCategoryButtons } diff --git a/retroshare.pri b/retroshare.pri index 7f827542f..0b61671f6 100644 --- a/retroshare.pri +++ b/retroshare.pri @@ -133,7 +133,7 @@ android-g++ { # message(ANDROID_EXTRA_LIBS: $$ANDROID_EXTRA_LIBS) # message(ANDROID_PLATFORM: $$ANDROID_PLATFORM) # message(ANDROID_PLATFORM_ROOT_PATH: $$ANDROID_PLATFORM_ROOT_PATH) -# message(NDK_TOOLCHAIN_PATH: $$NDK_TOOLCHAIN_PATH) +# message(NATIVE_LIBS_TOOLCHAIN_PATH: $$NATIVE_LIBS_TOOLCHAIN_PATH) } win32 {