mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-05-22 07:41:20 -04:00
attempt at not reloading full channel data at every change
This commit is contained in:
parent
9442d87644
commit
c06da99757
5 changed files with 520 additions and 479 deletions
|
@ -119,82 +119,18 @@ void RsGxsChannelPostsModel::handleEvent_main_thread(std::shared_ptr<const RsEve
|
||||||
{
|
{
|
||||||
case RsChannelEventCode::UPDATED_MESSAGE:
|
case RsChannelEventCode::UPDATED_MESSAGE:
|
||||||
case RsChannelEventCode::READ_STATUS_CHANGED:
|
case RsChannelEventCode::READ_STATUS_CHANGED:
|
||||||
|
case RsChannelEventCode::NEW_COMMENT:
|
||||||
{
|
{
|
||||||
// Normally we should just emit dataChanged() on the index of the data that has changed:
|
// Normally we should just emit dataChanged() on the index of the data that has changed:
|
||||||
// We need to update the data!
|
// We need to update the data!
|
||||||
|
|
||||||
// make a copy of e, so as to avoid destruction of the shared pointer during async thread execution, since [e] doesn't actually tell
|
// In particular, mChannelMsgId may refer to a comment, but this will be handled correctly
|
||||||
// the original shared_ptr that it is copied! So no counter is updated in event, which will be destroyed (as e will be) during or even before
|
// by update_single_post which will automatically retrive the correct parent msg.
|
||||||
// the execution of the lambda.
|
|
||||||
|
|
||||||
RsGxsChannelEvent E(*e);
|
if(e->mChannelGroupId == mChannelGroup.mMeta.mGroupId)
|
||||||
|
update_single_post(e->mChannelGroupId,e->mChannelMsgId,e->mChannelThreadId);
|
||||||
if(E.mChannelGroupId == mChannelGroup.mMeta.mGroupId)
|
};
|
||||||
RsThread::async([this, E]()
|
break;
|
||||||
{
|
|
||||||
// 1 - get message data from p3GxsChannels. No need for pointers here, because we send only a single post to postToObject()
|
|
||||||
// At this point we dont know what kind of msg id we have. It can be a vote, a comment or an actual message.
|
|
||||||
|
|
||||||
std::vector<RsGxsChannelPost> posts;
|
|
||||||
std::vector<RsGxsComment> comments;
|
|
||||||
std::vector<RsGxsVote> votes;
|
|
||||||
std::set<RsGxsMessageId> msg_ids{ E.mChannelMsgId };
|
|
||||||
|
|
||||||
if(!rsGxsChannels->getChannelContent(E.mChannelGroupId,msg_ids, posts,comments,votes))
|
|
||||||
{
|
|
||||||
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve channel message data for channel/msg " << E.mChannelGroupId << "/" << E.mChannelMsgId << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if what we have actually is a comment or a vote. If so we need to update the actual message they refer to
|
|
||||||
|
|
||||||
if(posts.empty()) // means we have a comment or a vote
|
|
||||||
{
|
|
||||||
msg_ids.clear();
|
|
||||||
|
|
||||||
for(auto c:comments) msg_ids.insert(c.mMeta.mThreadId);
|
|
||||||
for(auto v:votes ) msg_ids.insert(v.mMeta.mThreadId);
|
|
||||||
|
|
||||||
comments.clear();
|
|
||||||
votes.clear();
|
|
||||||
|
|
||||||
if(!rsGxsChannels->getChannelContent(E.mChannelGroupId,msg_ids,posts,comments,votes))
|
|
||||||
{
|
|
||||||
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve channel message data for channel/msg " << E.mChannelGroupId << "/" << E.mChannelMsgId << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Need to call this in order to get the actuall comment count. The previous call only retrieves the message, since we supplied the message ID.
|
|
||||||
// another way to go would be to save the comment ids of the existing message and re-insert them before calling getChannelContent.
|
|
||||||
|
|
||||||
if(!rsGxsChannels->getChannelComments(E.mChannelGroupId,msg_ids,comments))
|
|
||||||
{
|
|
||||||
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve message comment data for channel/msg " << E.mChannelGroupId << "/" << E.mChannelMsgId << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateCommentCounts(posts,comments);
|
|
||||||
|
|
||||||
// 2 - update the model in the UI thread.
|
|
||||||
|
|
||||||
RsQThreadUtils::postToObject( [posts,this]()
|
|
||||||
{
|
|
||||||
for(uint32_t i=0;i<posts.size();++i)
|
|
||||||
{
|
|
||||||
// linear search. Not good at all, but normally this is for a single post.
|
|
||||||
|
|
||||||
for(uint32_t j=0;j<mPosts.size();++j)
|
|
||||||
if(mPosts[j].mMeta.mMsgId == posts[i].mMeta.mMsgId)
|
|
||||||
{
|
|
||||||
mPosts[j] = posts[i];
|
|
||||||
|
|
||||||
triggerViewUpdate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},this);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -564,6 +500,47 @@ bool operator<(const RsGxsChannelPost& p1,const RsGxsChannelPost& p2)
|
||||||
return p1.mMeta.mPublishTs > p2.mMeta.mPublishTs;
|
return p1.mMeta.mPublishTs > p2.mMeta.mPublishTs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RsGxsChannelPostsModel::setSinglePost(const RsGxsChannelGroup& group, const RsGxsChannelPost& post)
|
||||||
|
{
|
||||||
|
if(mChannelGroup.mMeta.mGroupId != group.mMeta.mGroupId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
preMods();
|
||||||
|
|
||||||
|
// This is potentially quadratic, so it should not be called many times!
|
||||||
|
|
||||||
|
bool found=false;
|
||||||
|
|
||||||
|
for(auto& p:mPosts)
|
||||||
|
if(post.mMeta.mMsgId == p.mMeta.mMsgId || post.mMeta.mOrigMsgId == p.mMeta.mMsgId)
|
||||||
|
{
|
||||||
|
p = post;
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
if(!found)
|
||||||
|
mPosts.push_back(post);
|
||||||
|
|
||||||
|
std::sort(mPosts.begin(),mPosts.end());
|
||||||
|
|
||||||
|
mFilteredPosts.clear();
|
||||||
|
|
||||||
|
for(uint32_t i=0;i<mPosts.size();++i)
|
||||||
|
mFilteredPosts.push_back(i);
|
||||||
|
|
||||||
|
#ifdef DEBUG_CHANNEL_MODEL
|
||||||
|
// debug_dump();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (rowCount()>0)
|
||||||
|
{
|
||||||
|
beginInsertRows(QModelIndex(),0,rowCount()-1);
|
||||||
|
endInsertRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
postMods();
|
||||||
|
|
||||||
|
emit channelPostsLoaded();
|
||||||
|
}
|
||||||
void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vector<RsGxsChannelPost>& posts)
|
void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vector<RsGxsChannelPost>& posts)
|
||||||
{
|
{
|
||||||
preMods();
|
preMods();
|
||||||
|
@ -593,6 +570,57 @@ void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vecto
|
||||||
emit channelPostsLoaded();
|
emit channelPostsLoaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RsGxsChannelPostsModel::update_single_post(const RsGxsGroupId& group_id,const RsGxsMessageId& msg_id,const RsGxsMessageId& thread_id)
|
||||||
|
{
|
||||||
|
RsThread::async([this, group_id, msg_id,thread_id]()
|
||||||
|
{
|
||||||
|
// 1 - get message data from p3GxsChannels. No need for pointers here, because we send only a single post to postToObject()
|
||||||
|
// At this point we dont know what kind of msg id we have. It can be a vote, a comment or an actual message.
|
||||||
|
|
||||||
|
std::vector<RsGxsChannelPost> posts;
|
||||||
|
std::vector<RsGxsComment> comments;
|
||||||
|
std::vector<RsGxsVote> votes;
|
||||||
|
std::set<RsGxsMessageId> msg_ids({ (thread_id.isNull())?msg_id:thread_id } ); // we have a post message
|
||||||
|
|
||||||
|
if(!rsGxsChannels->getChannelContent(group_id,msg_ids, posts,comments,votes))
|
||||||
|
{
|
||||||
|
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve channel message data for channel/msg " << group_id << "/" << msg_id << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to call this in order to get the actual comment count. The previous call only retrieves the message, since we supplied the message ID.
|
||||||
|
// another way to go would be to save the comment ids of the existing message and re-insert them before calling getChannelContent.
|
||||||
|
|
||||||
|
if(!rsGxsChannels->getChannelComments(group_id,msg_ids,comments))
|
||||||
|
{
|
||||||
|
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve message comment data for channel/msg " << group_id << "/" << msg_id << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normally, there's a single post in the "post" array. The function below takes a full array of posts however.
|
||||||
|
|
||||||
|
updateCommentCounts(posts,comments);
|
||||||
|
|
||||||
|
// 2 - update the model in the UI thread.
|
||||||
|
|
||||||
|
RsQThreadUtils::postToObject( [posts,this]()
|
||||||
|
{
|
||||||
|
for(uint32_t i=0;i<posts.size();++i)
|
||||||
|
{
|
||||||
|
// linear search. Not good at all, but normally this is for a single post.
|
||||||
|
|
||||||
|
for(uint32_t j=0;j<mPosts.size();++j)
|
||||||
|
if(mPosts[j].mMeta.mMsgId == posts[i].mMeta.mMsgId)
|
||||||
|
{
|
||||||
|
mPosts[j] = posts[i];
|
||||||
|
|
||||||
|
triggerViewUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void RsGxsChannelPostsModel::update_posts(const RsGxsGroupId& group_id)
|
void RsGxsChannelPostsModel::update_posts(const RsGxsGroupId& group_id)
|
||||||
{
|
{
|
||||||
if(group_id.isNull())
|
if(group_id.isNull())
|
||||||
|
|
|
@ -214,6 +214,7 @@ private:
|
||||||
static void computeReputationLevel(uint32_t forum_sign_flags, RsGxsChannelPost& entry);
|
static void computeReputationLevel(uint32_t forum_sign_flags, RsGxsChannelPost& entry);
|
||||||
|
|
||||||
void update_posts(const RsGxsGroupId& group_id);
|
void update_posts(const RsGxsGroupId& group_id);
|
||||||
|
void update_single_post(const RsGxsGroupId& group_id, const RsGxsMessageId& msgId, const RsGxsMessageId &thread_id);
|
||||||
|
|
||||||
#ifdef TODO
|
#ifdef TODO
|
||||||
void setForumMessageSummary(const std::vector<RsGxsForumMsg>& messages);
|
void setForumMessageSummary(const std::vector<RsGxsForumMsg>& messages);
|
||||||
|
@ -231,6 +232,7 @@ private:
|
||||||
//void computeMessagesHierarchy(const RsGxsChannelGroup& forum_group, const std::vector<RsMsgMetaData> &msgs_array, std::vector<ChannelPostsModelPostEntry> &posts, std::map<RsGxsMessageId, std::vector<std::pair<time_t, RsGxsMessageId> > > &mPostVersions);
|
//void computeMessagesHierarchy(const RsGxsChannelGroup& forum_group, const std::vector<RsMsgMetaData> &msgs_array, std::vector<ChannelPostsModelPostEntry> &posts, std::map<RsGxsMessageId, std::vector<std::pair<time_t, RsGxsMessageId> > > &mPostVersions);
|
||||||
void createPostsArray(std::vector<RsGxsChannelPost> &posts);
|
void createPostsArray(std::vector<RsGxsChannelPost> &posts);
|
||||||
void setPosts(const RsGxsChannelGroup& group, std::vector<RsGxsChannelPost> &posts);
|
void setPosts(const RsGxsChannelGroup& group, std::vector<RsGxsChannelPost> &posts);
|
||||||
|
void setSinglePost(const RsGxsChannelGroup& group, const RsGxsChannelPost& post);
|
||||||
void initEmptyHierarchy();
|
void initEmptyHierarchy();
|
||||||
void handleEvent_main_thread(std::shared_ptr<const RsEvent> event);
|
void handleEvent_main_thread(std::shared_ptr<const RsEvent> event);
|
||||||
|
|
||||||
|
|
|
@ -751,20 +751,34 @@ void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptr<con
|
||||||
{
|
{
|
||||||
case RsChannelEventCode::NEW_CHANNEL: // [[fallthrough]];
|
case RsChannelEventCode::NEW_CHANNEL: // [[fallthrough]];
|
||||||
case RsChannelEventCode::DELETED_CHANNEL: // [[fallthrough]];
|
case RsChannelEventCode::DELETED_CHANNEL: // [[fallthrough]];
|
||||||
case RsChannelEventCode::NEW_COMMENT: // [[fallthrough]];
|
|
||||||
case RsChannelEventCode::NEW_VOTE: // [[fallthrough]];
|
|
||||||
case RsChannelEventCode::UPDATED_CHANNEL: // [[fallthrough]];
|
case RsChannelEventCode::UPDATED_CHANNEL: // [[fallthrough]];
|
||||||
case RsChannelEventCode::NEW_MESSAGE: // [[fallthrough]];
|
|
||||||
case RsChannelEventCode::UPDATED_MESSAGE:
|
|
||||||
case RsChannelEventCode::RECEIVED_PUBLISH_KEY:
|
case RsChannelEventCode::RECEIVED_PUBLISH_KEY:
|
||||||
case RsChannelEventCode::SYNC_PARAMETERS_UPDATED:
|
case RsChannelEventCode::SYNC_PARAMETERS_UPDATED:
|
||||||
{
|
{
|
||||||
if(e->mChannelGroupId == groupId())
|
if(e->mChannelGroupId == groupId())
|
||||||
updateDisplay(true);
|
updateDisplay(true,false);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RsChannelEventCode::NEW_COMMENT: // [[fallthrough]];
|
||||||
|
case RsChannelEventCode::NEW_MESSAGE: // [[fallthrough]];
|
||||||
|
{
|
||||||
|
if(e->mChannelGroupId == groupId())
|
||||||
|
updateDisplay(true,true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RsChannelEventCode::NEW_VOTE: // [[fallthrough]];
|
||||||
|
|
||||||
|
if(e->mChannelGroupId == groupId() && e->mChannelThreadId == ui->commentsDialog->messageId())
|
||||||
|
ui->commentsDialog->refresh();
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// case RsChannelEventCode::UPDATED_MESSAGE: // handled in GxsChannelPostsModel
|
||||||
|
// case RsChannelEventCode::READ_STATUS_CHANGED: // handled in GxsChannelPostsModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -863,7 +877,7 @@ void GxsChannelPostsWidgetWithModel::updateCommentsCount(int n)
|
||||||
else
|
else
|
||||||
ui->details_TW->setTabText(2,tr("Comments"));
|
ui->details_TW->setTabText(2,tr("Comments"));
|
||||||
}
|
}
|
||||||
void GxsChannelPostsWidgetWithModel::updateGroupData()
|
void GxsChannelPostsWidgetWithModel::updateData(bool update_group_data, bool update_posts)
|
||||||
{
|
{
|
||||||
if(groupId().isNull())
|
if(groupId().isNull())
|
||||||
{
|
{
|
||||||
|
@ -873,10 +887,10 @@ void GxsChannelPostsWidgetWithModel::updateGroupData()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RsThread::async([this]()
|
RsThread::async([this,update_group_data,update_posts]()
|
||||||
{
|
{
|
||||||
RsGxsChannelGroup group;
|
|
||||||
std::vector<RsGxsChannelGroup> groups;
|
std::vector<RsGxsChannelGroup> groups;
|
||||||
|
RsGxsChannelGroup group;
|
||||||
|
|
||||||
if(rsGxsChannels->getChannelsInfo(std::list<RsGxsGroupId>{ groupId() }, groups) && groups.size()==1)
|
if(rsGxsChannels->getChannelsInfo(std::list<RsGxsGroupId>{ groupId() }, groups) && groups.size()==1)
|
||||||
group = groups[0];
|
group = groups[0];
|
||||||
|
@ -886,7 +900,7 @@ void GxsChannelPostsWidgetWithModel::updateGroupData()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RsQThreadUtils::postToObject( [this,group]()
|
RsQThreadUtils::postToObject( [this,update_group_data,update_posts,group]()
|
||||||
{
|
{
|
||||||
if(mGroup.mMeta.mGroupId != group.mMeta.mGroupId) // this prevents any attempt to display the wrong index. Navigate() if needed will use mNavigatePendingMsgId
|
if(mGroup.mMeta.mGroupId != group.mMeta.mGroupId) // this prevents any attempt to display the wrong index. Navigate() if needed will use mNavigatePendingMsgId
|
||||||
{
|
{
|
||||||
|
@ -898,15 +912,21 @@ void GxsChannelPostsWidgetWithModel::updateGroupData()
|
||||||
updateCommentsCount(0);
|
updateCommentsCount(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(update_group_data)
|
||||||
|
{
|
||||||
mGroup = group;
|
mGroup = group;
|
||||||
|
insertChannelDetails(mGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(update_posts)
|
||||||
|
{
|
||||||
ui->postsTree->setPlaceholderText(tr("Loading..."));
|
ui->postsTree->setPlaceholderText(tr("Loading..."));
|
||||||
|
|
||||||
mChannelPostsModel->updateChannel(groupId());
|
mChannelPostsModel->updateChannel(groupId());
|
||||||
|
|
||||||
whileBlocking(ui->filterLineEdit)->clear();
|
whileBlocking(ui->filterLineEdit)->clear();
|
||||||
whileBlocking(ui->showUnread_TB)->setChecked(false);
|
whileBlocking(ui->showUnread_TB)->setChecked(false);
|
||||||
|
}
|
||||||
insertChannelDetails(mGroup);
|
|
||||||
|
|
||||||
emit groupDataLoaded();
|
emit groupDataLoaded();
|
||||||
emit groupChanged(this); // signals the parent widget to e.g. update the group tab name
|
emit groupChanged(this); // signals the parent widget to e.g. update the group tab name
|
||||||
|
@ -971,7 +991,7 @@ void GxsChannelPostsWidgetWithModel::postChannelPostLoad()
|
||||||
handlePostsTreeSizeChange(ui->postsTree->size(),true); // force the update
|
handlePostsTreeSizeChange(ui->postsTree->size(),true); // force the update
|
||||||
}
|
}
|
||||||
|
|
||||||
void GxsChannelPostsWidgetWithModel::updateDisplay(bool complete)
|
void GxsChannelPostsWidgetWithModel::updateDisplay(bool update_group_data,bool update_posts)
|
||||||
{
|
{
|
||||||
// First, clear all widget
|
// First, clear all widget
|
||||||
blank();
|
blank();
|
||||||
|
@ -986,25 +1006,16 @@ void GxsChannelPostsWidgetWithModel::updateDisplay(bool complete)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mGroup.mMeta.mGroupId.isNull() && !groupId().isNull())
|
if(mGroup.mMeta.mGroupId != groupId())
|
||||||
{
|
{
|
||||||
#ifdef DEBUG_FORUMS
|
#ifdef DEBUG_FORUMS
|
||||||
std::cerr << " inconsistent group data. Reloading!"<< std::endl;
|
std::cerr << " inconsistent group data. Reloading!"<< std::endl;
|
||||||
#endif
|
#endif
|
||||||
complete = true;
|
update_group_data = true;
|
||||||
|
update_posts = true;
|
||||||
}
|
}
|
||||||
if(complete) // need to update the group data, reload the messages etc.
|
|
||||||
{
|
|
||||||
#warning csoler 2020-06-02 : todo
|
|
||||||
//saveExpandedItems(mSavedExpandedMessages);
|
|
||||||
|
|
||||||
//if(mGroupId != mChannelPostsModel->currentGroupId())
|
updateData(update_group_data,update_posts);
|
||||||
// mThreadId.clear();
|
|
||||||
|
|
||||||
updateGroupData();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
GxsChannelPostsWidgetWithModel::~GxsChannelPostsWidgetWithModel()
|
GxsChannelPostsWidgetWithModel::~GxsChannelPostsWidgetWithModel()
|
||||||
{
|
{
|
||||||
|
|
|
@ -104,11 +104,11 @@ public:
|
||||||
|
|
||||||
/* GxsMessageFrameWidget */
|
/* GxsMessageFrameWidget */
|
||||||
virtual QIcon groupIcon() override;
|
virtual QIcon groupIcon() override;
|
||||||
virtual void groupIdChanged() override { updateDisplay(true); }
|
virtual void groupIdChanged() override { updateDisplay(true,true); }
|
||||||
virtual QString groupName(bool) override;
|
virtual QString groupName(bool) override;
|
||||||
virtual bool navigate(const RsGxsMessageId&) override;
|
virtual bool navigate(const RsGxsMessageId&) override;
|
||||||
|
|
||||||
void updateDisplay(bool complete);
|
void updateDisplay(bool update_group_data, bool update_posts);
|
||||||
|
|
||||||
#ifdef TODO
|
#ifdef TODO
|
||||||
/* FeedHolder */
|
/* FeedHolder */
|
||||||
|
@ -141,7 +141,7 @@ protected:
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void showPostDetails();
|
void showPostDetails();
|
||||||
void updateGroupData();
|
void updateData(bool update_group_data,bool update_posts);
|
||||||
void download();
|
void download();
|
||||||
void updateDAll_PB();
|
void updateDAll_PB();
|
||||||
void createMsg();
|
void createMsg();
|
||||||
|
|
|
@ -194,7 +194,7 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTabWidget" name="channel_TW">
|
<widget class="QTabWidget" name="channel_TW">
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>1</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="tab_3">
|
<widget class="QWidget" name="tab_3">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
|
@ -402,7 +402,7 @@
|
||||||
<string notr="true"><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
<string notr="true"><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||||
p, li { white-space: pre-wrap; }
|
p, li { white-space: pre-wrap; }
|
||||||
</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
|
</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;">
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">Description</span></p></body></html></string>
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">Description</span></p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="textInteractionFlags">
|
<property name="textInteractionFlags">
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue