diff --git a/libretroshare/src/gxs/rsgenexchange.cc b/libretroshare/src/gxs/rsgenexchange.cc index bfc1bbd23..e3ef41eb9 100644 --- a/libretroshare/src/gxs/rsgenexchange.cc +++ b/libretroshare/src/gxs/rsgenexchange.cc @@ -1675,13 +1675,7 @@ void RsGenExchange::receiveNewMessages(std::vector& messages) void RsGenExchange::receiveDistantSearchResults(TurtleRequestId id,const RsGxsGroupId &grpId) { - std::cerr << __PRETTY_FUNCTION__ << " received result for request " - << std::hex << id << std::dec << std::endl; - - RS_STACK_MUTEX(mGenMtx); - - RsGxsDistantSearchResultChange* gc = new RsGxsDistantSearchResultChange(id,grpId); - mNotifications.push_back(gc); + std::cerr << __PRETTY_FUNCTION__ << " received result for request " << std::hex << id << std::dec << ": this method should be overloaded in the client service, but it is not. This is a bug!" << std::endl; } void RsGenExchange::notifyReceivePublishKey(const RsGxsGroupId &grpId) diff --git a/libretroshare/src/gxs/rsgxsnetservice.cc b/libretroshare/src/gxs/rsgxsnetservice.cc index 48b12caa5..79be6fb95 100644 --- a/libretroshare/src/gxs/rsgxsnetservice.cc +++ b/libretroshare/src/gxs/rsgxsnetservice.cc @@ -272,6 +272,7 @@ NXS_NET_DEBUG_6 group sync statistics (e.g. number of posts at nighbour nodes, etc) NXS_NET_DEBUG_7 encryption/decryption of transactions NXS_NET_DEBUG_8 gxs distant sync + NXS_NET_DEBUG_9 gxs distant search ***/ #define NXS_NET_DEBUG_0 1 @@ -283,6 +284,7 @@ //#define NXS_NET_DEBUG_6 1 //#define NXS_NET_DEBUG_7 1 //#define NXS_NET_DEBUG_8 1 +//#define NXS_NET_DEBUG_9 1 //#define NXS_FRAG @@ -319,7 +321,7 @@ static const uint32_t RS_NXS_ITEM_ENCRYPTION_STATUS_GXS_KEY_MISSING = 0x05 ; #if defined(NXS_NET_DEBUG_0) || defined(NXS_NET_DEBUG_1) || defined(NXS_NET_DEBUG_2) || defined(NXS_NET_DEBUG_3) \ || defined(NXS_NET_DEBUG_4) || defined(NXS_NET_DEBUG_5) || defined(NXS_NET_DEBUG_6) || defined(NXS_NET_DEBUG_7) \ - || defined(NXS_NET_DEBUG_8) + || defined(NXS_NET_DEBUG_8) || defined(NXS_NET_DEBUG_9) static const RsPeerId peer_to_print = RsPeerId();//std::string("a97fef0e2dc82ddb19200fb30f9ac575")) ; static const RsGxsGroupId group_id_to_print = RsGxsGroupId(std::string("66052380f5d1d0c5992e2b55dc402ce6")) ; // use this to allow to this group id only, or "" for all IDs @@ -5187,7 +5189,7 @@ static bool termSearch(const std::string& src, const std::string& substring) } #endif // ndef RS_DEEP_CHANNEL_INDEX -bool RsGxsNetService::retrieveDistantSearchResults(TurtleRequestId req,std::map& group_infos) +bool RsGxsNetService::retrieveDistantSearchResults(TurtleRequestId req,std::map& group_infos) { RS_STACK_MUTEX(mNxsMutex) ; @@ -5199,7 +5201,7 @@ bool RsGxsNetService::retrieveDistantSearchResults(TurtleRequestId req,std::map< group_infos = it->second; return true ; } -bool RsGxsNetService::retrieveDistantGroupSummary(const RsGxsGroupId& group_id,RsGxsGroupSummary& gs) +bool RsGxsNetService::retrieveDistantGroupSummary(const RsGxsGroupId& group_id,RsGxsGroupSearchResults& gs) { RS_STACK_MUTEX(mNxsMutex) ; for(auto it(mDistantSearchResults.begin());it!=mDistantSearchResults.end();++it) @@ -5221,8 +5223,7 @@ bool RsGxsNetService::clearDistantSearchResults(const TurtleRequestId& id) return true ; } -void RsGxsNetService::receiveTurtleSearchResults( - TurtleRequestId req, const std::list& group_infos ) +void RsGxsNetService::receiveTurtleSearchResults( TurtleRequestId req, const std::list& group_infos ) { std::set groupsToNotifyResults; @@ -5230,20 +5231,43 @@ void RsGxsNetService::receiveTurtleSearchResults( RS_STACK_MUTEX(mNxsMutex); RsGxsGrpMetaTemporaryMap grpMeta; - std::map& - search_results_map(mDistantSearchResults[req]); + std::map& search_results_map(mDistantSearchResults[req]); + +#ifdef NXS_NET_DEBUG_9 + std::cerr << "Received group summary through turtle search for the following groups:" << std::endl; +#endif for(const RsGxsGroupSummary& gps : group_infos) - if(search_results_map.find(gps.mGroupId) == search_results_map.end()) - grpMeta[gps.mGroupId] = nullptr; + { + std::cerr <<" " << gps.mGroupId << " \"" << gps.mGroupName << "\"" << std::endl; + grpMeta[gps.mGroupId] = nullptr; + } + mDataStore->retrieveGxsGrpMetaData(grpMeta); +#ifdef NXS_NET_DEBUG_9 + std::cerr << "Retrieved data store group data for the following groups:" <mGroupName << std::endl; +#endif + for (const RsGxsGroupSummary& gps : group_infos) { #ifndef RS_DEEP_CHANNEL_INDEX /* Only keep groups that are not locally known, and groups that are - * not already in the mDistantSearchResults structure. */ - if(grpMeta[gps.mGroupId]) continue; + * not already in the mDistantSearchResults structure. + * mDataStore may in some situations allocate an empty group meta data, so it's important + * to test that the group meta is both non null and actually corresponds to the group id we seek. */ + + auto& meta(grpMeta[gps.mGroupId]); + + if(meta != nullptr && meta->mGroupId == gps.mGroupId) + continue; + +#ifdef NXS_NET_DEBUG_9 + std::cerr << " group " << gps.mGroupId << " is not known. Adding it to search results..." << std::endl; +#endif + #else // ndef RS_DEEP_CHANNEL_INDEX /* When deep search is enabled search results may bring more info * then we already have also about post that are indexed by xapian, @@ -5252,22 +5276,32 @@ void RsGxsNetService::receiveTurtleSearchResults( const RsGxsGroupId& grpId(gps.mGroupId); groupsToNotifyResults.insert(grpId); - auto it2 = search_results_map.find(grpId); - if(it2 != search_results_map.end()) - { - // update existing data - RsGxsGroupSummary& eGpS(it2->second); - eGpS.mPopularity++; - eGpS.mNumberOfMessages = std::max( - eGpS.mNumberOfMessages, - gps.mNumberOfMessages ); - } - else - { - search_results_map[grpId] = gps; - // number of results so far - search_results_map[grpId].mPopularity = 1; - } + + // Find search results place for this particular group + +#ifdef NXS_NET_DEBUG_9 + std::cerr << " Adding gps=" << gps.mGroupId << " name=\"" << gps.mGroupName << "\" gps.mSearchContext=\"" << gps.mSearchContext << "\"" << std::endl; +#endif + RsGxsGroupSearchResults& eGpS(search_results_map[grpId]); + + if(eGpS.mGroupId != grpId) // not initialized yet. So we do it now. + { + eGpS.mGroupId = gps.mGroupId; + eGpS.mGroupName = gps.mGroupName; + eGpS.mAuthorId = gps.mAuthorId; + eGpS.mPublishTs = gps.mPublishTs; + eGpS.mSignFlags = gps.mSignFlags; + } + // We should check that the above values are always the same for all info that is received. In the end, we'll + // request the group meta and check the signature, but it may be misleading to receive a forged information + // that is not the real one. + + ++eGpS.mPopularity; // increase popularity. This is not a real counting, but therefore some heuristic estimate. + eGpS.mNumberOfMessages = std::max( eGpS.mNumberOfMessages, gps.mNumberOfMessages ); + eGpS.mLastMessageTs = std::max( eGpS.mLastMessageTs, gps.mLastMessageTs ); + + if(gps.mSearchContext != gps.mGroupName) // this is a bit of a hack. We should have flags to tell where the search hit happens + eGpS.mSearchContexts.insert(gps.mSearchContext); } } // end RS_STACK_MUTEX(mNxsMutex); diff --git a/libretroshare/src/gxs/rsgxsnetservice.h b/libretroshare/src/gxs/rsgxsnetservice.h index f7abdc176..6b3e39d30 100644 --- a/libretroshare/src/gxs/rsgxsnetservice.h +++ b/libretroshare/src/gxs/rsgxsnetservice.h @@ -140,9 +140,9 @@ public: virtual void receiveTurtleSearchResults(TurtleRequestId req,const std::list& group_infos); virtual void receiveTurtleSearchResults(TurtleRequestId req,const unsigned char *encrypted_group_data,uint32_t encrypted_group_data_len); - virtual bool retrieveDistantSearchResults(TurtleRequestId req, std::map &group_infos); + virtual bool retrieveDistantSearchResults(TurtleRequestId req, std::map &group_infos); virtual bool clearDistantSearchResults(const TurtleRequestId& id); - virtual bool retrieveDistantGroupSummary(const RsGxsGroupId&,RsGxsGroupSummary&); + virtual bool retrieveDistantGroupSummary(const RsGxsGroupId&, RsGxsGroupSearchResults &); /*! * pauses synchronisation of subscribed groups and request for group id @@ -610,7 +610,7 @@ private: std::set mNewPublishKeysToNotify ; // Distant search result map - std::map > mDistantSearchResults ; + std::map > mDistantSearchResults ; void debugDump(); diff --git a/libretroshare/src/gxs/rsgxsnettunnel.cc b/libretroshare/src/gxs/rsgxsnettunnel.cc index a58d20f5f..eabf21e7e 100644 --- a/libretroshare/src/gxs/rsgxsnettunnel.cc +++ b/libretroshare/src/gxs/rsgxsnettunnel.cc @@ -1090,8 +1090,10 @@ void RsGxsNetTunnelService::receiveSearchResult(TurtleSearchRequestId request_id { GXS_NET_TUNNEL_DEBUG() << " : result is of type group summary result for service " << result_gs->service << std::dec << ": " << std::endl; +#ifdef DEBUG_RSGXSNETTUNNEL for(auto it(result_gs->group_infos.begin());it!=result_gs->group_infos.end();++it) std::cerr << " group " << (*it).mGroupId << ": " << (*it).mGroupName << ", " << (*it).mNumberOfMessages << " messages, last is " << time(NULL)-(*it).mLastMessageTs << " secs ago." << std::endl; +#endif auto it = mSearchableServices.find(result_gs->service) ; diff --git a/libretroshare/src/gxs/rsgxsnotify.h b/libretroshare/src/gxs/rsgxsnotify.h index 4640f133a..a6fd4c6fb 100644 --- a/libretroshare/src/gxs/rsgxsnotify.h +++ b/libretroshare/src/gxs/rsgxsnotify.h @@ -76,16 +76,6 @@ protected: bool mMetaChange; }; -class RsGxsDistantSearchResultChange: public RsGxsNotify -{ -public: - RsGxsDistantSearchResultChange(TurtleRequestId id,const RsGxsGroupId& gid) : RsGxsNotify(gid), mRequestId(id){} - - NotifyType getType() { return TYPE_RECEIVED_DISTANT_SEARCH_RESULTS ; } - - TurtleRequestId mRequestId ; -}; - /*! * Relevant to message changes */ diff --git a/libretroshare/src/gxs/rsnxs.h b/libretroshare/src/gxs/rsnxs.h index 2752fa6bf..7276f3421 100644 --- a/libretroshare/src/gxs/rsnxs.h +++ b/libretroshare/src/gxs/rsnxs.h @@ -128,7 +128,7 @@ public: * \return * false when the request is unknown. */ - virtual bool retrieveDistantSearchResults(TurtleRequestId req, std::map &group_infos)=0; + virtual bool retrieveDistantSearchResults(TurtleRequestId req, std::map &group_infos)=0; /*! * \brief getDistantSearchResults * \param id @@ -136,7 +136,7 @@ public: * \return */ virtual bool clearDistantSearchResults(const TurtleRequestId& id)=0; - virtual bool retrieveDistantGroupSummary(const RsGxsGroupId&,RsGxsGroupSummary&)=0; + virtual bool retrieveDistantGroupSummary(const RsGxsGroupId&,RsGxsGroupSearchResults&)=0; virtual bool search(const std::string& substring,std::list& group_infos) =0; virtual bool search(const Sha1CheckSum& hashed_group_id,unsigned char *& encrypted_group_data,uint32_t& encrypted_group_data_len)=0; diff --git a/libretroshare/src/retroshare/rsgxschannels.h b/libretroshare/src/retroshare/rsgxschannels.h index 4b188ad01..106147dc6 100644 --- a/libretroshare/src/retroshare/rsgxschannels.h +++ b/libretroshare/src/retroshare/rsgxschannels.h @@ -117,14 +117,11 @@ enum class RsChannelEventCode: uint8_t struct RsGxsChannelEvent: RsEvent { - RsGxsChannelEvent(): - RsEvent(RsEventType::GXS_CHANNELS), - mChannelEventCode(RsChannelEventCode::UNKNOWN) {} + RsGxsChannelEvent(): RsEvent(RsEventType::GXS_CHANNELS), mChannelEventCode(RsChannelEventCode::UNKNOWN) {} RsChannelEventCode mChannelEventCode; RsGxsGroupId mChannelGroupId; RsGxsMessageId mChannelMsgId; - TurtleRequestId mDistantSearchRequestId; ///* @see RsEvent @see RsSerializable void serial_process( RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) override @@ -134,8 +131,28 @@ struct RsGxsChannelEvent: RsEvent RS_SERIAL_PROCESS(mChannelEventCode); RS_SERIAL_PROCESS(mChannelGroupId); RS_SERIAL_PROCESS(mChannelMsgId); - RS_SERIAL_PROCESS(mDistantSearchRequestId); - } + } +}; + +// This event is used to factor multiple search results notifications in a single event. + +struct RsGxsChannelSearchResultEvent: RsEvent +{ + RsGxsChannelSearchResultEvent(): + RsEvent(RsEventType::GXS_CHANNELS), + mChannelEventCode(RsChannelEventCode::RECEIVED_DISTANT_SEARCH_RESULT) {} + + RsChannelEventCode mChannelEventCode; + std::map > mSearchResultsMap; + + ///* @see RsEvent @see RsSerializable + void serial_process( RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) override + { + RsEvent::serial_process(j, ctx); + + RS_SERIAL_PROCESS(mChannelEventCode); + RS_SERIAL_PROCESS(mSearchResultsMap); + } }; class RsGxsChannels: public RsGxsIfaceHelper, public RsGxsCommentService @@ -407,48 +424,6 @@ public: */ virtual bool getChannelStatistics(const RsGxsGroupId& channelId,GxsGroupStatistic& stat) =0; - - /** - * @brief Request remote channels search - * @jsonapi{development} - * @param[in] matchString string to look for in the search - * @param multiCallback function that will be called each time a search - * result is received - * @param[in] maxWait maximum wait time in seconds for search results - * @return false on error, true otherwise - */ - virtual bool turtleSearchRequest( - const std::string& matchString, - const std::function& multiCallback, - rstime_t maxWait = 300 ) = 0; - - /** - * @brief Request remote channel - * @jsonapi{development} - * @param[in] channelId id of the channel to request to distants peers - * @param multiCallback function that will be called each time a result is - * received - * @param[in] maxWait maximum wait time in seconds for search results - * @return false on error, true otherwise - */ - virtual bool turtleChannelRequest( - const RsGxsGroupId& channelId, - const std::function& multiCallback, - rstime_t maxWait = 300 ) = 0; - - /** - * @brief Search local channels - * @jsonapi{development} - * @param[in] matchString string to look for in the search - * @param multiCallback function that will be called for each result - * @param[in] maxWait maximum wait time in seconds for search results - * @return false on error, true otherwise - */ - virtual bool localSearchRequest( - const std::string& matchString, - const std::function& multiCallback, - rstime_t maxWait = 30 ) = 0; - /// default base URL used for channels links @see exportChannelLink static const std::string DEFAULT_CHANNEL_BASE_URL; @@ -493,6 +468,7 @@ public: /** * @brief Import channel from full link + * @jsonapi{development} * @param[in] link channel link either in radix or link format * @param[out] chanId optional storage for parsed channel id * @param[out] errMsg optional storage for error message, meaningful only in @@ -504,7 +480,58 @@ public: RsGxsGroupId& chanId = RS_DEFAULT_STORAGE_PARAM(RsGxsGroupId), std::string& errMsg = RS_DEFAULT_STORAGE_PARAM(std::string) ) = 0; + /** + * @brief Search the turtle reachable network for matching channels + * @jsonapi{development} + * An @see RsGxsChannelSearchResultEvent is emitted when matching channels + * arrives from the network + * @param[in] matchString string to search into the channels + * @return search id + */ + virtual TurtleRequestId turtleSearchRequest(const std::string& matchString)=0; + /** + * @brief Retrieve available search results + * @jsonapi{development} + * @param[in] searchId search id + * @param[out] results storage for search results + * @return false on error, true otherwise + */ + virtual bool retrieveDistantSearchResults( + TurtleRequestId searchId, + std::map& results ) = 0; + + /** + * @brief Request distant channel details + * @jsonapi{development} + * An @see RsGxsChannelSearchResultEvent is emitted once details are + * retrieved from the network + * @param[in] groupId if of the group to request to the network + * @return search id + */ + virtual TurtleRequestId turtleGroupRequest(const RsGxsGroupId& groupId) = 0; + + /** + * @brief Retrieve previously requested distant group + * @jsonapi{development} + * @param[in] groupId if of teh group + * @param[out] distantGroup storage for group data + * @return false on error, true otherwise + */ + virtual bool getDistantSearchResultGroupData( + const RsGxsGroupId& groupId, RsGxsChannelGroup& distantGroup ) = 0; + + /** + * @brief Clear accumulated search results + * @jsonapi{development} + * @param[in] reqId search id + * @return false on error, true otherwise + */ + virtual bool clearDistantSearchResults(TurtleRequestId reqId) = 0; + + ~RsGxsChannels() override; + + //////////////////////////////////////////////////////////////////////////// /* Following functions are deprecated and should not be considered a safe to * use API */ @@ -690,22 +717,4 @@ public: */ RS_DEPRECATED_FOR(editChannel) virtual bool updateGroup(uint32_t& token, RsGxsChannelGroup& group) = 0; - - ////////////////////////////////////////////////////////////////////////////// - /// Distant synchronisation methods /// - ////////////////////////////////////////////////////////////////////////////// - /// - RS_DEPRECATED_FOR(turtleChannelRequest) - virtual TurtleRequestId turtleGroupRequest(const RsGxsGroupId& group_id)=0; - RS_DEPRECATED - virtual TurtleRequestId turtleSearchRequest(const std::string& match_string)=0; - RS_DEPRECATED_FOR(turtleSearchRequest) - virtual bool retrieveDistantSearchResults(TurtleRequestId req, std::map &results) =0; - RS_DEPRECATED - virtual bool clearDistantSearchResults(TurtleRequestId req)=0; - RS_DEPRECATED_FOR(turtleChannelRequest) - virtual bool retrieveDistantGroup(const RsGxsGroupId& group_id,RsGxsChannelGroup& distant_group)=0; - ////////////////////////////////////////////////////////////////////////////// - - ~RsGxsChannels() override; }; diff --git a/libretroshare/src/retroshare/rsgxsiface.h b/libretroshare/src/retroshare/rsgxsiface.h index 36bee517e..74e67008f 100644 --- a/libretroshare/src/retroshare/rsgxsiface.h +++ b/libretroshare/src/retroshare/rsgxsiface.h @@ -72,6 +72,46 @@ struct RsGxsGroupSummary : RsSerializable ~RsGxsGroupSummary(); }; +/*! + * This structure is used to locally store group search results for a given service. + * It contains the group information as well as a context + * strings to tell where the information was found. It is more compact than a + * GroupMeta object, so as to make search responses as light as possible. + */ +struct RsGxsGroupSearchResults : RsSerializable +{ + RsGxsGroupSearchResults() + : mPublishTs(0), mNumberOfMessages(0),mLastMessageTs(0), mSignFlags(0),mPopularity(0) + {} + + RsGxsGroupId mGroupId; + std::string mGroupName; + RsGxsId mAuthorId; + rstime_t mPublishTs; + uint32_t mNumberOfMessages; + rstime_t mLastMessageTs; + uint32_t mSignFlags; + uint32_t mPopularity; + + std::set mSearchContexts; + + /// @see RsSerializable::serial_process + void serial_process( RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx ) + { + RS_SERIAL_PROCESS(mGroupId); + RS_SERIAL_PROCESS(mGroupName); + RS_SERIAL_PROCESS(mAuthorId); + RS_SERIAL_PROCESS(mPublishTs); + RS_SERIAL_PROCESS(mNumberOfMessages); + RS_SERIAL_PROCESS(mLastMessageTs); + RS_SERIAL_PROCESS(mSignFlags); + RS_SERIAL_PROCESS(mPopularity); + RS_SERIAL_PROCESS(mSearchContexts); + } + + virtual ~RsGxsGroupSearchResults() = default; +}; /*! * Stores ids of changed gxs groups and messages. diff --git a/libretroshare/src/services/p3gxschannels.cc b/libretroshare/src/services/p3gxschannels.cc index 2a699e662..bdb1f9981 100644 --- a/libretroshare/src/services/p3gxschannels.cc +++ b/libretroshare/src/services/p3gxschannels.cc @@ -80,12 +80,15 @@ p3GxsChannels::p3GxsChannels( RS_SERVICE_GXS_TYPE_CHANNELS, gixs, channelsAuthenPolicy() ), RsGxsChannels(static_cast(*this)), GxsTokenQueue(this), mSubscribedGroupsMutex("GXS channels subscribed groups cache"), - mKnownChannelsMutex("GXS channels known channels timestamp cache"), + mKnownChannelsMutex("GXS channels known channels timestamp cache") +#ifdef TO_REMOVE mSearchCallbacksMapMutex("GXS channels search callbacks map"), mDistantChannelsCallbacksMapMutex("GXS channels distant channels callbacks map") +#endif { // For Dummy Msgs. mGenActive = false; + mLastDistantSearchNotificationTS = 0; mCommentService = new p3GxsCommentService(this, RS_SERVICE_GXS_TYPE_CHANNELS); RsTickEvent::schedule_in(CHANNEL_PROCESS, 0); @@ -352,18 +355,6 @@ void p3GxsChannels::notifyChanges(std::vector &changes) } - RsGxsDistantSearchResultChange *dsrChange = dynamic_cast(*it); - - if(dsrChange && rsEvents) - { - auto ev = std::make_shared(); - ev->mChannelGroupId = dsrChange->mGroupId; - ev->mChannelEventCode = RsChannelEventCode::RECEIVED_DISTANT_SEARCH_RESULT; - ev->mDistantSearchRequestId = dsrChange->mRequestId; - - rsEvents->postEvent(ev); - } - /* shouldn't need to worry about groups - as they need to be subscribed to */ delete *it; } @@ -383,17 +374,32 @@ void p3GxsChannels::notifyChanges(std::vector &changes) void p3GxsChannels::service_tick() { static rstime_t last_dummy_tick = 0; + rstime_t now = time(NULL); if (time(NULL) > last_dummy_tick + 5) { dummy_tick(); - last_dummy_tick = time(NULL); + last_dummy_tick = now; } RsTickEvent::tick_events(); GxsTokenQueue::checkRequests(); mCommentService->comment_tick(); + + // Notify distant search results, not more than once per sec. Normally we should + // rather send one item for all, but that needs another class type + + if(now > mLastDistantSearchNotificationTS+2 && !mSearchResultsToNotify.empty()) + { + auto ev = std::make_shared(); + ev->mSearchResultsMap = mSearchResultsToNotify; + + mLastDistantSearchNotificationTS = now; + mSearchResultsToNotify.clear(); + + rsEvents->postEvent(ev); + } } bool p3GxsChannels::getGroupData(const uint32_t &token, std::vector &groups) @@ -2210,7 +2216,9 @@ void p3GxsChannels::dummy_tick() } +#ifdef TO_REMOVE cleanTimedOutCallbacks(); +#endif } @@ -2389,18 +2397,18 @@ bool p3GxsChannels::clearDistantSearchResults(TurtleRequestId req) { return netService()->clearDistantSearchResults(req); } -bool p3GxsChannels::retrieveDistantSearchResults(TurtleRequestId req,std::map& results) +bool p3GxsChannels::retrieveDistantSearchResults(TurtleRequestId req,std::map& results) { return netService()->retrieveDistantSearchResults(req,results); } -bool p3GxsChannels::retrieveDistantGroup(const RsGxsGroupId& group_id,RsGxsChannelGroup& distant_group) +bool p3GxsChannels::getDistantSearchResultGroupData(const RsGxsGroupId& group_id,RsGxsChannelGroup& distant_group) { - RsGxsGroupSummary gs; + RsGxsGroupSearchResults gs; if(netService()->retrieveDistantGroupSummary(group_id,gs)) { - // This is a placeholder information by the time we receive the full group meta data. + // This is a placeholder information by the time we receive the full group meta data and check the signature. distant_group.mMeta.mGroupId = gs.mGroupId ; distant_group.mMeta.mGroupName = gs.mGroupName; distant_group.mMeta.mGroupFlags = GXS_SERV::FLAG_PRIVACY_PUBLIC ; @@ -2426,6 +2434,7 @@ bool p3GxsChannels::retrieveDistantGroup(const RsGxsGroupId& group_id,RsGxsChann return false ; } +#ifdef TO_REMOVE bool p3GxsChannels::turtleSearchRequest( const std::string& matchString, const std::function& multiCallback, @@ -2505,17 +2514,24 @@ bool p3GxsChannels::localSearchRequest( return true; } +#endif -void p3GxsChannels::receiveDistantSearchResults( - TurtleRequestId id, const RsGxsGroupId& grpId ) +void p3GxsChannels::receiveDistantSearchResults( TurtleRequestId id, const RsGxsGroupId& grpId ) { - std::cerr << __PRETTY_FUNCTION__ << "(" << id << ", " << grpId << ")" - << std::endl; + if(!rsEvents) + return; + + // We temporise here, in order to avoid notifying clients with many events + // So we put some data in there and will send an event with all of them at once every 1 sec at most. + + mSearchResultsToNotify[id].insert(grpId); + +#ifdef TO_REMOVE + std::cerr << __PRETTY_FUNCTION__ << "(" << id << ", " << grpId << ")" << std::endl; { RsGenExchange::receiveDistantSearchResults(id, grpId); - RsGxsGroupSummary gs; - gs.mGroupId = grpId; + RsGxsGroupSearchResults gs; netService()->retrieveDistantGroupSummary(grpId, gs); { @@ -2556,8 +2572,10 @@ void p3GxsChannels::receiveDistantSearchResults( return; } } // RS_STACK_MUTEX(mDistantChannelsCallbacksMapMutex); +#endif } +#ifdef TO_REMOVE void p3GxsChannels::cleanTimedOutCallbacks() { auto now = std::chrono::system_clock::now(); @@ -2586,6 +2604,7 @@ void p3GxsChannels::cleanTimedOutCallbacks() else ++cbpt; } // RS_STACK_MUTEX(mDistantChannelsCallbacksMapMutex) } +#endif bool p3GxsChannels::exportChannelLink( std::string& link, const RsGxsGroupId& chanId, bool includeGxsData, diff --git a/libretroshare/src/services/p3gxschannels.h b/libretroshare/src/services/p3gxschannels.h index e6917e252..da274690b 100644 --- a/libretroshare/src/services/p3gxschannels.h +++ b/libretroshare/src/services/p3gxschannels.h @@ -68,9 +68,9 @@ protected: virtual TurtleRequestId turtleGroupRequest(const RsGxsGroupId& group_id); virtual TurtleRequestId turtleSearchRequest(const std::string& match_string); - virtual bool retrieveDistantSearchResults(TurtleRequestId req, std::map &results) ; + virtual bool retrieveDistantSearchResults(TurtleRequestId req, std::map &results) ; virtual bool clearDistantSearchResults(TurtleRequestId req); - virtual bool retrieveDistantGroup(const RsGxsGroupId& group_id,RsGxsChannelGroup& distant_group); + virtual bool getDistantSearchResultGroupData(const RsGxsGroupId& group_id,RsGxsChannelGroup& distant_group); // Overloaded to cache new groups. virtual RsGenExchange::ServiceCreate_Return service_CreateGroup(RsGxsGrpItem* grpItem, RsTlvSecurityKeySet& keySet); @@ -109,6 +109,7 @@ virtual bool getChannelAutoDownload(const RsGxsGroupId &groupid, bool& enabled); virtual bool setChannelDownloadDirectory(const RsGxsGroupId &groupId, const std::string& directory); virtual bool getChannelDownloadDirectory(const RsGxsGroupId &groupId, std::string& directory); +#ifdef TO_REMOVE /// @see RsGxsChannels::turtleSearchRequest virtual bool turtleSearchRequest(const std::string& matchString, const std::function& multiCallback, @@ -124,6 +125,7 @@ virtual bool getChannelDownloadDirectory(const RsGxsGroupId &groupId, std::strin virtual bool localSearchRequest(const std::string& matchString, const std::function& multiCallback, rstime_t maxWait = 30 ) override; +#endif /** * Receive results from turtle search @see RsGenExchange @see RsNxsObserver @@ -374,6 +376,9 @@ bool generateGroup(uint32_t &token, std::string groupName); std::map mKnownChannels; RsMutex mKnownChannelsMutex; + rstime_t mLastDistantSearchNotificationTS; + std::map > mSearchResultsToNotify; +#ifdef TO_REMOVE /** Store search callbacks with timeout*/ std::map< TurtleRequestId, @@ -394,4 +399,5 @@ bool generateGroup(uint32_t &token, std::string groupName); /// Cleanup mSearchCallbacksMap and mDistantChannelsCallbacksMap void cleanTimedOutCallbacks(); +#endif }; diff --git a/retroshare-gui/src/gui/common/GroupTreeWidget.cpp b/retroshare-gui/src/gui/common/GroupTreeWidget.cpp index fa12ebf54..2fa8de0c0 100644 --- a/retroshare-gui/src/gui/common/GroupTreeWidget.cpp +++ b/retroshare-gui/src/gui/common/GroupTreeWidget.cpp @@ -463,6 +463,17 @@ void GroupTreeWidget::fillGroupItems(QTreeWidgetItem *categoryItem, const QList< item->setData(COLUMN_DATA, ROLE_NAME, itemInfo.name); item->setData(COLUMN_DATA, ROLE_DESCRIPTION, itemInfo.description); + // Add children for context strings. This happens in the search. + while(nullptr != item->takeChild(0)); + + for(auto str:itemInfo.context_strings) + if(!str.empty()) + { + QTreeWidgetItem *it = new QTreeWidgetItem(QStringList(QString::fromUtf8(str.c_str()))); + it->setData(COLUMN_DATA,ROLE_ID,itemInfo.id); + item->addChild(it); + } + /* Set last post */ qlonglong lastPost = itemInfo.lastpost.toTime_t(); item->setData(COLUMN_DATA, ROLE_LASTPOST, -lastPost); // negative for correct sorting diff --git a/retroshare-gui/src/gui/common/GroupTreeWidget.h b/retroshare-gui/src/gui/common/GroupTreeWidget.h index 23c90a292..9f8d0ccd4 100644 --- a/retroshare-gui/src/gui/common/GroupTreeWidget.h +++ b/retroshare-gui/src/gui/common/GroupTreeWidget.h @@ -21,6 +21,8 @@ #ifndef GROUPTREEWIDGET_H #define GROUPTREEWIDGET_H +#include + #include #include @@ -47,16 +49,17 @@ public: {} public: - QString id; - QString name; - QString description; - int popularity; - QDateTime lastpost; - QIcon icon; - bool publishKey; - bool adminKey; - quint32 subscribeFlags; - quint32 max_visible_posts ; + QString id; + QString name; + QString description; + int popularity; + QDateTime lastpost; + QIcon icon; + bool publishKey; + bool adminKey; + quint32 subscribeFlags; + quint32 max_visible_posts ; + std::set context_strings; }; //cppcheck-suppress noConstructor diff --git a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp index de528d23e..49b91866d 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp +++ b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp @@ -290,55 +290,50 @@ void GxsGroupFrameDialog::updateDisplay(bool complete) if(complete) // || !getGrpIds().empty() || !getGrpIdsMeta().empty()) { updateGroupSummary(); /* Update group list */ - updateSearchResults() ; +// updateSearchResults() ; } void GxsGroupFrameDialog::updateSearchResults() { - const std::set& reqs = getSearchRequests(); + for(auto& it:mSearchGroupsItems) + updateSearchResults(it.first); +} - for(auto it(reqs.begin());it!=reqs.end();++it) - { - std::cerr << "updating search ID " << std::hex << *it << std::dec << std::endl; +void GxsGroupFrameDialog::updateSearchResults(const TurtleRequestId& sid) +{ + std::cerr << "updating search ID " << std::hex << sid << std::dec << std::endl; - std::map group_infos; + std::map group_infos; - getDistantSearchResults(*it,group_infos) ; + getDistantSearchResults(sid,group_infos) ; - std::cerr << "retrieved " << std::endl; + std::cerr << "retrieved " << std::endl; - auto it2 = mSearchGroupsItems.find(*it); + auto it2 = mSearchGroupsItems.find(sid); - if(mSearchGroupsItems.end() == it2) - { - std::cerr << "GxsGroupFrameDialog::updateSearchResults(): received result notification for req " << std::hex << *it << std::dec << " but no item present!" << std::endl; - continue ; // we could create the item just as well but since this situation is not supposed to happen, I prefer to make this a failure case. - } + QList group_items ; - QList group_items ; + for(auto it3(group_infos.begin());it3!=group_infos.end();++it3) + { + std::cerr << " adding group " << it3->first << " " << it3->second.mGroupId << " \"" << it3->second.mGroupName << "\"" << std::endl; + for(auto s:it3->second.mSearchContexts) + std::cerr << " Context string \"" << s << "\"" << std::endl; - for(auto it3(group_infos.begin());it3!=group_infos.end();++it3) - if(mCachedGroupMetas.find(it3->first) == mCachedGroupMetas.end()) - { - std::cerr << " adding new group " << it3->first << " " - << it3->second.mGroupId << " \"" - << it3->second.mGroupName << "\"" << std::endl; + GroupItemInfo i; + i.id = QString(it3->second.mGroupId.toStdString().c_str()); + i.name = QString::fromUtf8(it3->second.mGroupName.c_str()); + i.popularity = 0; // could be set to the number of hits + i.lastpost = QDateTime::fromTime_t(it3->second.mLastMessageTs); + i.subscribeFlags = 0; // irrelevant here + i.publishKey = false ; // IS_GROUP_PUBLISHER(groupInfo.mSubscribeFlags); + i.adminKey = false ; // IS_GROUP_ADMIN(groupInfo.mSubscribeFlags); + i.max_visible_posts = it3->second.mNumberOfMessages; + i.context_strings = it3->second.mSearchContexts; - GroupItemInfo i; - i.id = QString(it3->second.mGroupId.toStdString().c_str()); - i.name = QString::fromUtf8(it3->second.mGroupName.c_str()); - i.popularity = 0; // could be set to the number of hits - i.lastpost = QDateTime::fromTime_t(it3->second.mLastMessageTs); - i.subscribeFlags = 0; // irrelevant here - i.publishKey = false ; // IS_GROUP_PUBLISHER(groupInfo.mSubscribeFlags); - i.adminKey = false ; // IS_GROUP_ADMIN(groupInfo.mSubscribeFlags); - i.max_visible_posts = it3->second.mNumberOfMessages; + group_items.push_back(i); + } - group_items.push_back(i); - } - - ui->groupTreeWidget->fillGroupItems(it2->second, group_items); - } + ui->groupTreeWidget->fillGroupItems(it2->second, group_items); } void GxsGroupFrameDialog::todo() @@ -1074,15 +1069,20 @@ void GxsGroupFrameDialog::updateGroupSummary() { RsThread::async([this]() { - std::list groupInfo; + auto groupInfo = new std::list() ; - if(!getGroupData(groupInfo)) + if(!getGroupData(*groupInfo)) { - std::cerr << __PRETTY_FUNCTION__ << " failed to collect group info " << std::endl; + std::cerr << __PRETTY_FUNCTION__ << " failed to collect group info." << std::endl; + delete groupInfo; return; } - if(groupInfo.empty()) + if(groupInfo->empty()) + { + std::cerr << __PRETTY_FUNCTION__ << " no group info collected." << std::endl; + delete groupInfo; return; + } RsQThreadUtils::postToObject( [this,groupInfo]() { @@ -1092,7 +1092,7 @@ void GxsGroupFrameDialog::updateGroupSummary() * Qt::QueuedConnection is important! */ - insertGroupsData(groupInfo); + insertGroupsData(*groupInfo); updateSearchResults(); mStateHelper->setLoading(TOKEN_TYPE_GROUP_SUMMARY, false); @@ -1111,12 +1111,14 @@ void GxsGroupFrameDialog::updateGroupSummary() // now delete the data that is not used anymore - for(auto& g:groupInfo) + for(auto& g:*groupInfo) { mCachedGroupMetas[g->mMeta.mGroupId] = g->mMeta; delete g; } + delete groupInfo; + }, this ); }); } diff --git a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.h b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.h index 73938584e..05b311b04 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.h +++ b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.h @@ -161,7 +161,8 @@ private: virtual void groupTreeCustomActions(RsGxsGroupId /*grpId*/, int /*subscribeFlags*/, QList &/*actions*/) {} virtual RsGxsCommentService *getCommentService() { return NULL; } virtual QWidget *createCommentHeaderWidget(const RsGxsGroupId &/*grpId*/, const RsGxsMessageId &/*msgId*/) { return NULL; } - virtual bool getDistantSearchResults(TurtleRequestId /* id */, std::map& /* group_infos */){ return false ;} + virtual bool getDistantSearchResults(TurtleRequestId /* id */, std::map& /* group_infos */){ return false ;} + virtual RsGxsGenericGroupData *getDistantSearchResultGroupData(const RsGxsGroupId& group_id){ return nullptr ;} void initUi(); @@ -187,7 +188,8 @@ private: GxsCommentDialog *commentWidget(const RsGxsMessageId &msgId); protected: - void updateSearchResults(); + void updateSearchResults(const TurtleRequestId &sid); + void updateSearchResults(); // update all searches bool mCountChildMsgs; // Count unread child messages? diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.cpp index 23e0854a6..8cbeccfbc 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.cpp @@ -61,9 +61,7 @@ void GxsChannelDialog::handleEvent_main_thread(std::shared_ptr ev { const RsGxsChannelEvent *e = dynamic_cast(event.get()); - if(!e) - return; - + if(e) switch(e->mChannelEventCode) { case RsChannelEventCode::NEW_MESSAGE: // [[fallthrough]]; @@ -72,11 +70,6 @@ void GxsChannelDialog::handleEvent_main_thread(std::shared_ptr ev updateGroupStatisticsReal(e->mChannelGroupId); // update the list immediately break; - case RsChannelEventCode::RECEIVED_DISTANT_SEARCH_RESULT: - mSearchResults.insert(e->mDistantSearchRequestId); - updateSearchResults(); - break; - case RsChannelEventCode::NEW_CHANNEL: // [[fallthrough]]; case RsChannelEventCode::SUBSCRIBE_STATUS_CHANGED: updateDisplay(true); @@ -89,6 +82,13 @@ void GxsChannelDialog::handleEvent_main_thread(std::shared_ptr ev default: break; } + + + const RsGxsChannelSearchResultEvent*f = dynamic_cast(event.get()); + + if(nullptr != f) + for(auto it:f->mSearchResultsMap) + updateSearchResults(it.first); } GxsChannelDialog::~GxsChannelDialog() @@ -401,16 +401,26 @@ TurtleRequestId GxsChannelDialog::distantSearch(const QString& search_string) return rsGxsChannels->turtleSearchRequest(search_string.toStdString()) ; } -bool GxsChannelDialog::getDistantSearchResults(TurtleRequestId id, std::map& group_infos) +bool GxsChannelDialog::getDistantSearchResults(TurtleRequestId id, std::map& group_infos) { return rsGxsChannels->retrieveDistantSearchResults(id,group_infos); } +RsGxsGenericGroupData *GxsChannelDialog::getDistantSearchResultGroupData(const RsGxsGroupId& group_id) +{ + RsGxsChannelGroup channel_group; + + if(rsGxsChannels->getDistantSearchResultGroupData(group_id,channel_group)) + return new RsGxsGenericGroupData(channel_group); + else + return nullptr; +} + void GxsChannelDialog::checkRequestGroup(const RsGxsGroupId& grpId) { RsGxsChannelGroup distant_group; - if( rsGxsChannels->retrieveDistantGroup(grpId,distant_group)) // normally we should also check that the group meta is not already here. + if( rsGxsChannels->getDistantSearchResultGroupData(grpId,distant_group)) // normally we should also check that the group meta is not already here. { std::cerr << "GxsChannelDialog::checkRequestGroup() sending turtle request for group data for group " << grpId << std::endl; rsGxsChannels->turtleGroupRequest(grpId); diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.h b/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.h index b0b46205d..7902b7361 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.h @@ -43,10 +43,11 @@ public: protected: /* GxsGroupFrameDialog */ - virtual bool getDistantSearchResults(TurtleRequestId id, std::map& group_infos); + virtual bool getDistantSearchResults(TurtleRequestId id, std::map &group_infos) override; + virtual RsGxsGenericGroupData *getDistantSearchResultGroupData(const RsGxsGroupId& group_id) override; - virtual TurtleRequestId distantSearch(const QString& search_string) ; - virtual void checkRequestGroup(const RsGxsGroupId& grpId) ; + virtual TurtleRequestId distantSearch(const QString& search_string) override; + virtual void checkRequestGroup(const RsGxsGroupId& grpId) override ; // Implementation of some abstract methods in GxsGroupFrameDialog diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.cpp index 5d6974442..bbbb26a42 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.cpp @@ -858,11 +858,12 @@ bool GxsChannelPostsWidget::getGroupData(RsGxsGenericGroupData *& data) { RsGxsChannelGroup distant_group; - if(rsGxsChannels->retrieveDistantGroup(groupId(),distant_group)) + if(rsGxsChannels->getDistantSearchResultGroupData(groupId(),distant_group)) { insertChannelDetails(distant_group); - data = new RsGxsChannelGroup(distant_group); + data = new RsGxsChannelGroup(distant_group); mGroup = distant_group; // make a local copy to pass on to items + return true ; } } diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 73508c1e8..cc60e003b 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -59,6 +59,7 @@ static const int mTokenTypeGroupData = 1; static const int CHANNEL_TABS_DETAILS= 0; static const int CHANNEL_TABS_POSTS = 1; +static const int CHANNEL_TABS_FILES = 2; /* View mode */ #define VIEW_MODE_FEEDS 1 @@ -538,23 +539,20 @@ void GxsChannelPostsWidgetWithModel::updateGroupData() RsThread::async([this]() { + RsGxsChannelGroup group; std::vector groups; - if(!rsGxsChannels->getChannelsInfo(std::list{ groupId() }, groups)) + if(rsGxsChannels->getChannelsInfo(std::list{ groupId() }, groups) && groups.size()==1) + group = groups[0]; + else if(!rsGxsChannels->getDistantSearchResultGroupData(groupId(),group)) { - std::cerr << __PRETTY_FUNCTION__ << " failed to get autodownload value for channel: " << groupId() << std::endl; + std::cerr << __PRETTY_FUNCTION__ << " failed to get group data for channel: " << groupId() << std::endl; return; } - if(groups.size() != 1) - { - RsErr() << __PRETTY_FUNCTION__ << " cannot retrieve channel data for group ID " << groupId() << ": ERROR." << std::endl; - return; - } - - RsQThreadUtils::postToObject( [this,groups]() + RsQThreadUtils::postToObject( [this,group]() { - mGroup = groups[0]; + mGroup = group; mChannelPostsModel->updateChannel(groupId()); insertChannelDetails(mGroup); @@ -779,12 +777,14 @@ void GxsChannelPostsWidgetWithModel::insertChannelDetails(const RsGxsChannelGrou //ui->feedToolButton->setEnabled(true); //ui->fileToolButton->setEnabled(true); ui->channel_TW->setTabEnabled(CHANNEL_TABS_POSTS,true); + ui->channel_TW->setTabEnabled(CHANNEL_TABS_FILES,true); ui->details_TW->setEnabled(true); } else { ui->details_TW->setEnabled(false); ui->channel_TW->setTabEnabled(CHANNEL_TABS_POSTS,false); + ui->channel_TW->setTabEnabled(CHANNEL_TABS_FILES,false); } diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui index 1f51702a2..2886e8dbe 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -161,7 +161,7 @@ - 1 + 0 diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumModel.cpp b/retroshare-gui/src/gui/gxsforums/GxsForumModel.cpp index f72649dfc..e4d6f13e9 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumModel.cpp +++ b/retroshare-gui/src/gui/gxsforums/GxsForumModel.cpp @@ -669,8 +669,11 @@ QVariant RsGxsForumModel::displayRole(const ForumModelPostEntry& fmpe,int col) c return QVariant(DateTime::formatDateTime(qtime)); } - case COLUMN_THREAD_DISTRIBUTION: - case COLUMN_THREAD_AUTHOR:{ + case COLUMN_THREAD_DISTRIBUTION: // passthrough // handled by delegate. + case COLUMN_THREAD_MSGID: + return QVariant(); + case COLUMN_THREAD_AUTHOR: + { QString name; RsGxsId id = RsGxsId(fmpe.mAuthorId.toStdString()); @@ -680,7 +683,6 @@ QVariant RsGxsForumModel::displayRole(const ForumModelPostEntry& fmpe,int col) c return name; return QVariant(tr("[Unknown]")); } - case COLUMN_THREAD_MSGID: return QVariant(); #ifdef TODO if (filterColumn == COLUMN_THREAD_CONTENT) { // need content for filter diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.cpp b/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.cpp index 29cfa5b44..fd9c591ca 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.cpp +++ b/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.cpp @@ -134,6 +134,13 @@ public: const QPoint p = QPoint((r.width() - pix.width())/2, (r.height() - pix.height())/2); painter->drawPixmap(r.topLeft() + p, pix); } + + virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override + { + static auto img(FilesDefs::getPixmapFromQtResourcePath(IMAGE_WARNING_YELLOW)); + + return QSize(img.width()*1.2,option.rect.height()); + } }; class ReadStatusItemDelegate: public QStyledItemDelegate @@ -183,6 +190,13 @@ public: const QPoint p = QPoint((r.width() - pix.width())/2, (r.height() - pix.height())/2); painter->drawPixmap(r.topLeft() + p, pix); } + + virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override + { + static auto img(FilesDefs::getPixmapFromQtResourcePath(":/images/message-state-unread.png")); + + return QSize(img.width()*1.2,option.rect.height()); + } }; class ForumPostSortFilterProxyModel: public QSortFilterProxyModel @@ -299,9 +313,10 @@ GxsForumThreadWidget::GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget QHeaderView * ttheader = ui->threadTreeWidget->header () ; ttheader->resizeSection (RsGxsForumModel::COLUMN_THREAD_DATE, 140*f); ttheader->resizeSection (RsGxsForumModel::COLUMN_THREAD_TITLE, 440*f); - ttheader->resizeSection (RsGxsForumModel::COLUMN_THREAD_DISTRIBUTION, 24*f); ttheader->resizeSection (RsGxsForumModel::COLUMN_THREAD_AUTHOR, 150*f); - ttheader->resizeSection (RsGxsForumModel::COLUMN_THREAD_READ, 24*f); + + ui->threadTreeWidget->resizeColumnToContents(RsGxsForumModel::COLUMN_THREAD_DISTRIBUTION); + ui->threadTreeWidget->resizeColumnToContents(RsGxsForumModel::COLUMN_THREAD_READ); QHeaderView_setSectionResizeModeColumn(ttheader, RsGxsForumModel::COLUMN_THREAD_TITLE, QHeaderView::Interactive); QHeaderView_setSectionResizeModeColumn(ttheader, RsGxsForumModel::COLUMN_THREAD_DATE, QHeaderView::Interactive);